about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
authorStarfall <root@starfall.blue>2020-02-04 17:44:29 -0600
committerStarfall <root@starfall.blue>2020-02-04 17:44:29 -0600
commit6d24d3bcb84abd04f31da95f97f6d60ef0afdc00 (patch)
treee7c38251a9e92bdf3a464b4aa7f1880aa5139bf0 /app
parentc0c9529df269816f52915a9802e5e30fbce9576b (diff)
parent885e9227c6e8e1ce5e4a5625d5126ba76dce2c00 (diff)
Merge branch 'glitch'
Diffstat (limited to 'app')
-rw-r--r--app/controllers/admin/accounts_controller.rb16
-rw-r--r--app/controllers/admin/announcements_controller.rb88
-rw-r--r--app/controllers/admin/custom_emojis_controller.rb6
-rw-r--r--app/controllers/admin/followers_controller.rb18
-rw-r--r--app/controllers/admin/instances_controller.rb2
-rw-r--r--app/controllers/admin/invites_controller.rb2
-rw-r--r--app/controllers/admin/relationships_controller.rb25
-rw-r--r--app/controllers/admin/reports_controller.rb7
-rw-r--r--app/controllers/admin/tags_controller.rb2
-rw-r--r--app/controllers/api/base_controller.rb2
-rw-r--r--app/controllers/api/oembed_controller.rb14
-rw-r--r--app/controllers/api/v1/accounts/follower_accounts_controller.rb6
-rw-r--r--app/controllers/api/v1/accounts/following_accounts_controller.rb6
-rw-r--r--app/controllers/api/v1/announcements/reactions_controller.rb29
-rw-r--r--app/controllers/api/v1/announcements_controller.rb29
-rw-r--r--app/controllers/api/v1/media_controller.rb3
-rw-r--r--app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb4
-rw-r--r--app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb4
-rw-r--r--app/controllers/application_controller.rb10
-rw-r--r--app/controllers/auth/passwords_controller.rb6
-rw-r--r--app/controllers/auth/registrations_controller.rb12
-rw-r--r--app/controllers/concerns/obfuscate_filename.rb16
-rw-r--r--app/controllers/filters_controller.rb3
-rw-r--r--app/controllers/follower_accounts_controller.rb7
-rw-r--r--app/controllers/following_accounts_controller.rb7
-rw-r--r--app/controllers/oauth/authorizations_controller.rb5
-rw-r--r--app/controllers/relationships_controller.rb48
-rw-r--r--app/controllers/settings/base_controller.rb5
-rw-r--r--app/controllers/settings/profiles_controller.rb5
-rw-r--r--app/controllers/statuses_controller.rb4
-rw-r--r--app/controllers/well_known/host_meta_controller.rb6
-rw-r--r--app/helpers/accounts_helper.rb4
-rw-r--r--app/helpers/admin/action_logs_helper.rb8
-rw-r--r--app/helpers/admin/announcements_helper.rb11
-rw-r--r--app/helpers/admin/filter_helper.rb19
-rw-r--r--app/helpers/routing_helper.rb4
-rw-r--r--app/helpers/settings_helper.rb2
-rw-r--r--app/javascript/flavours/glitch/actions/announcements.js180
-rw-r--r--app/javascript/flavours/glitch/actions/compose.js2
-rw-r--r--app/javascript/flavours/glitch/actions/importer/normalizer.js10
-rw-r--r--app/javascript/flavours/glitch/actions/markers.js46
-rw-r--r--app/javascript/flavours/glitch/actions/notifications.js3
-rw-r--r--app/javascript/flavours/glitch/actions/streaming.js19
-rw-r--r--app/javascript/flavours/glitch/actions/timelines.js2
-rw-r--r--app/javascript/flavours/glitch/components/animated_number.js65
-rw-r--r--app/javascript/flavours/glitch/components/column_header.js60
-rw-r--r--app/javascript/flavours/glitch/components/dropdown_menu.js8
-rw-r--r--app/javascript/flavours/glitch/components/media_gallery.js16
-rw-r--r--app/javascript/flavours/glitch/components/relative_timestamp.js16
-rw-r--r--app/javascript/flavours/glitch/components/status_content.js20
-rw-r--r--app/javascript/flavours/glitch/features/account_timeline/index.js1
-rw-r--r--app/javascript/flavours/glitch/features/directory/components/account_card.js1
-rw-r--r--app/javascript/flavours/glitch/features/emoji_picker/index.js7
-rw-r--r--app/javascript/flavours/glitch/features/getting_started/components/announcements.js436
-rw-r--r--app/javascript/flavours/glitch/features/getting_started/containers/announcements_container.js20
-rw-r--r--app/javascript/flavours/glitch/features/getting_started/containers/trends_container.js2
-rw-r--r--app/javascript/flavours/glitch/features/home_timeline/index.js38
-rw-r--r--app/javascript/flavours/glitch/features/local_settings/page/index.js8
-rw-r--r--app/javascript/flavours/glitch/features/notifications/index.js55
-rw-r--r--app/javascript/flavours/glitch/features/status/components/action_bar.js3
-rw-r--r--app/javascript/flavours/glitch/features/status/components/detailed_status.js11
-rw-r--r--app/javascript/flavours/glitch/features/status/index.js2
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/media_modal.js1
-rw-r--r--app/javascript/flavours/glitch/features/ui/index.js3
-rw-r--r--app/javascript/flavours/glitch/locales/ast.js7
-rw-r--r--app/javascript/flavours/glitch/locales/bn.js7
-rw-r--r--app/javascript/flavours/glitch/locales/br.js7
-rw-r--r--app/javascript/flavours/glitch/locales/co.js7
-rw-r--r--app/javascript/flavours/glitch/locales/cs.js7
-rw-r--r--app/javascript/flavours/glitch/locales/cy.js7
-rw-r--r--app/javascript/flavours/glitch/locales/da.js7
-rw-r--r--app/javascript/flavours/glitch/locales/el.js7
-rw-r--r--app/javascript/flavours/glitch/locales/es-AR.js7
-rw-r--r--app/javascript/flavours/glitch/locales/et.js7
-rw-r--r--app/javascript/flavours/glitch/locales/eu.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ga.js7
-rw-r--r--app/javascript/flavours/glitch/locales/gl.js7
-rw-r--r--app/javascript/flavours/glitch/locales/hi.js7
-rw-r--r--app/javascript/flavours/glitch/locales/hy.js7
-rw-r--r--app/javascript/flavours/glitch/locales/is.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ka.js7
-rw-r--r--app/javascript/flavours/glitch/locales/kab.js7
-rw-r--r--app/javascript/flavours/glitch/locales/kk.js7
-rw-r--r--app/javascript/flavours/glitch/locales/kn.js7
-rw-r--r--app/javascript/flavours/glitch/locales/lt.js7
-rw-r--r--app/javascript/flavours/glitch/locales/lv.js7
-rw-r--r--app/javascript/flavours/glitch/locales/mk.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ml.js7
-rw-r--r--app/javascript/flavours/glitch/locales/mr.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ms.js7
-rw-r--r--app/javascript/flavours/glitch/locales/nn.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ro.js7
-rw-r--r--app/javascript/flavours/glitch/locales/sk.js7
-rw-r--r--app/javascript/flavours/glitch/locales/sl.js7
-rw-r--r--app/javascript/flavours/glitch/locales/sq.js7
-rw-r--r--app/javascript/flavours/glitch/locales/sr-Latn.js7
-rw-r--r--app/javascript/flavours/glitch/locales/sr.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ta.js7
-rw-r--r--app/javascript/flavours/glitch/locales/te.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ur.js7
-rw-r--r--app/javascript/flavours/glitch/locales/vi.js7
-rw-r--r--app/javascript/flavours/glitch/packs/public.js9
-rw-r--r--app/javascript/flavours/glitch/reducers/announcements.js102
-rw-r--r--app/javascript/flavours/glitch/reducers/compose.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/index.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/local_settings.js1
-rw-r--r--app/javascript/flavours/glitch/reducers/notifications.js19
-rw-r--r--app/javascript/flavours/glitch/reducers/statuses.js3
-rw-r--r--app/javascript/flavours/glitch/selectors/index.js1
-rw-r--r--app/javascript/flavours/glitch/styles/admin.scss58
-rw-r--r--app/javascript/flavours/glitch/styles/components/accounts.scss4
-rw-r--r--app/javascript/flavours/glitch/styles/components/announcements.scss225
-rw-r--r--app/javascript/flavours/glitch/styles/components/columns.scss14
-rw-r--r--app/javascript/flavours/glitch/styles/components/composer.scss22
-rw-r--r--app/javascript/flavours/glitch/styles/components/drawer.scss2
-rw-r--r--app/javascript/flavours/glitch/styles/components/index.scss9
-rw-r--r--app/javascript/flavours/glitch/styles/components/status.scss1
-rw-r--r--app/javascript/flavours/glitch/styles/forms.scss6
-rw-r--r--app/javascript/flavours/glitch/styles/polls.scss1
-rw-r--r--app/javascript/flavours/glitch/styles/statuses.scss17
-rw-r--r--app/javascript/flavours/glitch/util/numbers.js8
-rw-r--r--app/javascript/flavours/glitch/util/stream.js55
-rw-r--r--app/javascript/images/elephant_ui_plane.svg2
-rw-r--r--app/javascript/mastodon/actions/announcements.js180
-rw-r--r--app/javascript/mastodon/actions/importer/normalizer.js10
-rw-r--r--app/javascript/mastodon/actions/notifications.js3
-rw-r--r--app/javascript/mastodon/actions/statuses.js13
-rw-r--r--app/javascript/mastodon/actions/streaming.js19
-rw-r--r--app/javascript/mastodon/actions/timelines.js2
-rw-r--r--app/javascript/mastodon/components/animated_number.js65
-rw-r--r--app/javascript/mastodon/components/column_header.js5
-rw-r--r--app/javascript/mastodon/components/error_boundary.js2
-rw-r--r--app/javascript/mastodon/components/media_gallery.js16
-rw-r--r--app/javascript/mastodon/components/relative_timestamp.js16
-rw-r--r--app/javascript/mastodon/components/scrollable_list.js9
-rw-r--r--app/javascript/mastodon/components/status.js48
-rw-r--r--app/javascript/mastodon/components/status_content.js25
-rw-r--r--app/javascript/mastodon/containers/status_container.js5
-rw-r--r--app/javascript/mastodon/features/account_timeline/index.js1
-rw-r--r--app/javascript/mastodon/features/audio/index.js35
-rw-r--r--app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js7
-rw-r--r--app/javascript/mastodon/features/directory/components/account_card.js1
-rw-r--r--app/javascript/mastodon/features/getting_started/components/announcements.js436
-rw-r--r--app/javascript/mastodon/features/getting_started/containers/announcements_container.js20
-rw-r--r--app/javascript/mastodon/features/getting_started/containers/trends_container.js2
-rw-r--r--app/javascript/mastodon/features/home_timeline/index.js38
-rw-r--r--app/javascript/mastodon/features/status/components/action_bar.js3
-rw-r--r--app/javascript/mastodon/features/status/components/detailed_status.js11
-rw-r--r--app/javascript/mastodon/features/status/index.js2
-rw-r--r--app/javascript/mastodon/features/ui/components/media_modal.js1
-rw-r--r--app/javascript/mastodon/features/video/index.js50
-rw-r--r--app/javascript/mastodon/locales/ar.json28
-rw-r--r--app/javascript/mastodon/locales/ast.json170
-rw-r--r--app/javascript/mastodon/locales/bg.json16
-rw-r--r--app/javascript/mastodon/locales/bn.json70
-rw-r--r--app/javascript/mastodon/locales/br.json98
-rw-r--r--app/javascript/mastodon/locales/ca.json120
-rw-r--r--app/javascript/mastodon/locales/co.json16
-rw-r--r--app/javascript/mastodon/locales/cs.json230
-rw-r--r--app/javascript/mastodon/locales/cy.json16
-rw-r--r--app/javascript/mastodon/locales/da.json16
-rw-r--r--app/javascript/mastodon/locales/de.json20
-rw-r--r--app/javascript/mastodon/locales/defaultMessages.json53
-rw-r--r--app/javascript/mastodon/locales/el.json20
-rw-r--r--app/javascript/mastodon/locales/en.json7
-rw-r--r--app/javascript/mastodon/locales/eo.json18
-rw-r--r--app/javascript/mastodon/locales/es-AR.json18
-rw-r--r--app/javascript/mastodon/locales/es.json18
-rw-r--r--app/javascript/mastodon/locales/et.json100
-rw-r--r--app/javascript/mastodon/locales/eu.json26
-rw-r--r--app/javascript/mastodon/locales/fa.json370
-rw-r--r--app/javascript/mastodon/locales/fi.json16
-rw-r--r--app/javascript/mastodon/locales/fr.json26
-rw-r--r--app/javascript/mastodon/locales/ga.json16
-rw-r--r--app/javascript/mastodon/locales/gl.json534
-rw-r--r--app/javascript/mastodon/locales/he.json16
-rw-r--r--app/javascript/mastodon/locales/hi.json16
-rw-r--r--app/javascript/mastodon/locales/hr.json16
-rw-r--r--app/javascript/mastodon/locales/hu.json16
-rw-r--r--app/javascript/mastodon/locales/hy.json284
-rw-r--r--app/javascript/mastodon/locales/id.json22
-rw-r--r--app/javascript/mastodon/locales/io.json16
-rw-r--r--app/javascript/mastodon/locales/is.json439
-rw-r--r--app/javascript/mastodon/locales/it.json18
-rw-r--r--app/javascript/mastodon/locales/ja.json20
-rw-r--r--app/javascript/mastodon/locales/ka.json18
-rw-r--r--app/javascript/mastodon/locales/kab.json439
-rw-r--r--app/javascript/mastodon/locales/kk.json16
-rw-r--r--app/javascript/mastodon/locales/kn.json16
-rw-r--r--app/javascript/mastodon/locales/ko.json30
-rw-r--r--app/javascript/mastodon/locales/lt.json16
-rw-r--r--app/javascript/mastodon/locales/lv.json16
-rw-r--r--app/javascript/mastodon/locales/mk.json16
-rw-r--r--app/javascript/mastodon/locales/ml.json286
-rw-r--r--app/javascript/mastodon/locales/mr.json16
-rw-r--r--app/javascript/mastodon/locales/ms.json16
-rw-r--r--app/javascript/mastodon/locales/nl.json20
-rw-r--r--app/javascript/mastodon/locales/nn.json614
-rw-r--r--app/javascript/mastodon/locales/no.json326
-rw-r--r--app/javascript/mastodon/locales/oc.json18
-rw-r--r--app/javascript/mastodon/locales/pl.json16
-rw-r--r--app/javascript/mastodon/locales/pt-BR.json406
-rw-r--r--app/javascript/mastodon/locales/pt-PT.json20
-rw-r--r--app/javascript/mastodon/locales/ro.json16
-rw-r--r--app/javascript/mastodon/locales/ru.json204
-rw-r--r--app/javascript/mastodon/locales/sk.json34
-rw-r--r--app/javascript/mastodon/locales/sl.json16
-rw-r--r--app/javascript/mastodon/locales/sq.json16
-rw-r--r--app/javascript/mastodon/locales/sr-Latn.json16
-rw-r--r--app/javascript/mastodon/locales/sr.json16
-rw-r--r--app/javascript/mastodon/locales/sv.json38
-rw-r--r--app/javascript/mastodon/locales/ta.json70
-rw-r--r--app/javascript/mastodon/locales/te.json16
-rw-r--r--app/javascript/mastodon/locales/th.json64
-rw-r--r--app/javascript/mastodon/locales/tr.json22
-rw-r--r--app/javascript/mastodon/locales/uk.json16
-rw-r--r--app/javascript/mastodon/locales/ur.json274
-rw-r--r--app/javascript/mastodon/locales/vi.json439
-rw-r--r--app/javascript/mastodon/locales/whitelist_is.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_kab.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_vi.json2
-rw-r--r--app/javascript/mastodon/locales/zh-CN.json24
-rw-r--r--app/javascript/mastodon/locales/zh-HK.json306
-rw-r--r--app/javascript/mastodon/locales/zh-TW.json32
-rw-r--r--app/javascript/mastodon/reducers/announcements.js102
-rw-r--r--app/javascript/mastodon/reducers/index.js2
-rw-r--r--app/javascript/mastodon/reducers/statuses.js6
-rw-r--r--app/javascript/mastodon/selectors/index.js1
-rw-r--r--app/javascript/mastodon/stream.js55
-rw-r--r--app/javascript/mastodon/utils/numbers.js8
-rw-r--r--app/javascript/packs/public.js9
-rw-r--r--app/javascript/styles/mastodon/admin.scss58
-rw-r--r--app/javascript/styles/mastodon/components.scss258
-rw-r--r--app/javascript/styles/mastodon/forms.scss6
-rw-r--r--app/lib/activitypub/activity.rb2
-rw-r--r--app/lib/entity_cache.rb2
-rw-r--r--app/lib/feed_manager.rb4
-rw-r--r--app/lib/formatter.rb5
-rw-r--r--app/lib/inline_renderer.rb4
-rw-r--r--app/lib/request.rb6
-rw-r--r--app/lib/sanitize_config.rb2
-rw-r--r--app/lib/search_query_transformer.rb2
-rw-r--r--app/lib/spam_check.rb2
-rw-r--r--app/mailers/user_mailer.rb1
-rw-r--r--app/models/account.rb19
-rw-r--r--app/models/account_filter.rb17
-rw-r--r--app/models/announcement.rb86
-rw-r--r--app/models/announcement_filter.rb39
-rw-r--r--app/models/announcement_mute.rb19
-rw-r--r--app/models/announcement_reaction.rb37
-rw-r--r--app/models/backup.rb2
-rw-r--r--app/models/bookmark.rb6
-rw-r--r--app/models/concerns/account_finder_concern.rb8
-rw-r--r--app/models/concerns/account_interactions.rb1
-rw-r--r--app/models/concerns/attachmentable.rb11
-rw-r--r--app/models/concerns/status_threading_concern.rb12
-rw-r--r--app/models/custom_emoji.rb2
-rw-r--r--app/models/custom_emoji_filter.rb7
-rw-r--r--app/models/custom_filter.rb1
-rw-r--r--app/models/domain_block.rb2
-rw-r--r--app/models/instance_filter.rb5
-rw-r--r--app/models/invite_filter.rb5
-rw-r--r--app/models/media_attachment.rb4
-rw-r--r--app/models/relationship_filter.rb120
-rw-r--r--app/models/report_filter.rb7
-rw-r--r--app/models/status.rb7
-rw-r--r--app/models/tag_filter.rb10
-rw-r--r--app/models/user.rb22
-rw-r--r--app/policies/announcement_policy.rb19
-rw-r--r--app/serializers/rest/account_serializer.rb8
-rw-r--r--app/serializers/rest/announcement_serializer.rb45
-rw-r--r--app/serializers/rest/reaction_serializer.rb31
-rw-r--r--app/services/activitypub/process_collection_service.rb2
-rw-r--r--app/services/backup_service.rb2
-rw-r--r--app/services/fetch_resource_service.rb2
-rw-r--r--app/services/process_mentions_service.rb11
-rw-r--r--app/services/resolve_url_service.rb18
-rw-r--r--app/services/vote_service.rb2
-rw-r--r--app/validators/reaction_validator.rb28
-rw-r--r--app/validators/unique_username_validator.rb3
-rw-r--r--app/validators/vote_validator.rb8
-rw-r--r--app/views/accounts/show.html.haml2
-rw-r--r--app/views/admin/accounts/index.html.haml2
-rw-r--r--app/views/admin/accounts/show.html.haml14
-rw-r--r--app/views/admin/announcements/_announcement.html.haml19
-rw-r--r--app/views/admin/announcements/edit.html.haml22
-rw-r--r--app/views/admin/announcements/index.html.haml22
-rw-r--r--app/views/admin/announcements/new.html.haml21
-rw-r--r--app/views/admin/custom_emojis/index.html.haml10
-rw-r--r--app/views/admin/email_domain_blocks/index.html.haml23
-rw-r--r--app/views/admin/followers/index.html.haml28
-rw-r--r--app/views/admin/instances/index.html.haml5
-rw-r--r--app/views/admin/relationships/index.html.haml39
-rw-r--r--app/views/admin/reports/_status.html.haml7
-rw-r--r--app/views/admin/reports/index.html.haml5
-rw-r--r--app/views/admin/reports/show.html.haml2
-rw-r--r--app/views/admin/tags/_tag.html.haml5
-rw-r--r--app/views/admin/tags/index.html.haml20
-rw-r--r--app/views/directories/index.html.haml2
-rw-r--r--app/views/filters/index.html.haml36
-rw-r--r--app/views/layouts/admin.html.haml7
-rw-r--r--app/views/layouts/embedded.html.haml4
-rw-r--r--app/views/relationships/_account.html.haml2
-rw-r--r--app/views/relationships/show.html.haml7
-rw-r--r--app/views/settings/applications/index.html.haml33
-rw-r--r--app/views/settings/preferences/appearance/show.html.haml4
-rw-r--r--app/views/statuses/_detailed_status.html.haml4
-rw-r--r--app/views/statuses/_simple_status.html.haml8
-rw-r--r--app/views/statuses/embed.html.haml5
-rw-r--r--app/workers/activitypub/delivery_worker.rb10
-rw-r--r--app/workers/publish_announcement_reaction_worker.rb22
-rw-r--r--app/workers/publish_scheduled_announcement_worker.rb19
-rw-r--r--app/workers/refollow_worker.rb9
-rw-r--r--app/workers/scheduler/scheduled_statuses_scheduler.rb28
-rw-r--r--app/workers/unpublish_announcement_worker.rb14
315 files changed, 8958 insertions, 2807 deletions
diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb
index 68b6352f8..7b1783542 100644
--- a/app/controllers/admin/accounts_controller.rb
+++ b/app/controllers/admin/accounts_controller.rb
@@ -109,21 +109,7 @@ module Admin
     end
 
     def filter_params
-      params.permit(
-        :local,
-        :remote,
-        :by_domain,
-        :active,
-        :pending,
-        :disabled,
-        :silenced,
-        :suspended,
-        :username,
-        :display_name,
-        :email,
-        :ip,
-        :staff
-      )
+      params.slice(*AccountFilter::KEYS).permit(*AccountFilter::KEYS)
     end
   end
 end
diff --git a/app/controllers/admin/announcements_controller.rb b/app/controllers/admin/announcements_controller.rb
new file mode 100644
index 000000000..494fd13d0
--- /dev/null
+++ b/app/controllers/admin/announcements_controller.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+
+class Admin::AnnouncementsController < Admin::BaseController
+  before_action :set_announcements, only: :index
+  before_action :set_announcement, except: [:index, :new, :create]
+
+  def index
+    authorize :announcement, :index?
+  end
+
+  def new
+    authorize :announcement, :create?
+
+    @announcement = Announcement.new
+  end
+
+  def create
+    authorize :announcement, :create?
+
+    @announcement = Announcement.new(resource_params)
+
+    if @announcement.save
+      PublishScheduledAnnouncementWorker.perform_async(@announcement.id) if @announcement.published?
+      log_action :create, @announcement
+      redirect_to admin_announcements_path, notice: @announcement.published? ? I18n.t('admin.announcements.published_msg') : I18n.t('admin.announcements.scheduled_msg')
+    else
+      render :new
+    end
+  end
+
+  def edit
+    authorize :announcement, :update?
+  end
+
+  def update
+    authorize :announcement, :update?
+
+    if @announcement.update(resource_params)
+      PublishScheduledAnnouncementWorker.perform_async(@announcement.id) if @announcement.published?
+      log_action :update, @announcement
+      redirect_to admin_announcements_path, notice: I18n.t('admin.announcements.updated_msg')
+    else
+      render :edit
+    end
+  end
+
+  def publish
+    authorize :announcement, :update?
+    @announcement.publish!
+    PublishScheduledAnnouncementWorker.perform_async(@announcement.id)
+    log_action :update, @announcement
+    redirect_to admin_announcements_path, notice: I18n.t('admin.announcements.published_msg')
+  end
+
+  def unpublish
+    authorize :announcement, :update?
+    @announcement.unpublish!
+    UnpublishAnnouncementWorker.perform_async(@announcement.id)
+    log_action :update, @announcement
+    redirect_to admin_announcements_path, notice: I18n.t('admin.announcements.unpublished_msg')
+  end
+
+  def destroy
+    authorize :announcement, :destroy?
+    @announcement.destroy!
+    UnpublishAnnouncementWorker.perform_async(@announcement.id) if @announcement.published?
+    log_action :destroy, @announcement
+    redirect_to admin_announcements_path, notice: I18n.t('admin.announcements.destroyed_msg')
+  end
+
+  private
+
+  def set_announcements
+    @announcements = AnnouncementFilter.new(filter_params).results.page(params[:page])
+  end
+
+  def set_announcement
+    @announcement = Announcement.find(params[:id])
+  end
+
+  def filter_params
+    params.slice(*AnnouncementFilter::KEYS).permit(*AnnouncementFilter::KEYS)
+  end
+
+  def resource_params
+    params.require(:announcement).permit(:text, :scheduled_at, :starts_at, :ends_at, :all_day)
+  end
+end
diff --git a/app/controllers/admin/custom_emojis_controller.rb b/app/controllers/admin/custom_emojis_controller.rb
index 2af90f051..efa8f2950 100644
--- a/app/controllers/admin/custom_emojis_controller.rb
+++ b/app/controllers/admin/custom_emojis_controller.rb
@@ -2,10 +2,6 @@
 
 module Admin
   class CustomEmojisController < BaseController
-    include ObfuscateFilename
-
-    obfuscate_filename [:custom_emoji, :image]
-
     def index
       authorize :custom_emoji, :index?
 
@@ -52,7 +48,7 @@ module Admin
     end
 
     def filter_params
-      params.slice(:local, :remote, :by_domain, :shortcode, :page).permit(:local, :remote, :by_domain, :shortcode, :page)
+      params.slice(:page, *CustomEmojiFilter::KEYS).permit(:page, *CustomEmojiFilter::KEYS)
     end
 
     def action_from_button
diff --git a/app/controllers/admin/followers_controller.rb b/app/controllers/admin/followers_controller.rb
deleted file mode 100644
index d826f47c5..000000000
--- a/app/controllers/admin/followers_controller.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: true
-
-module Admin
-  class FollowersController < BaseController
-    before_action :set_account
-
-    PER_PAGE = 40
-
-    def index
-      authorize :account, :index?
-      @followers = @account.followers.local.recent.page(params[:page]).per(PER_PAGE)
-    end
-
-    def set_account
-      @account = Account.find(params[:account_id])
-    end
-  end
-end
diff --git a/app/controllers/admin/instances_controller.rb b/app/controllers/admin/instances_controller.rb
index b47b18f8e..2fc041207 100644
--- a/app/controllers/admin/instances_controller.rb
+++ b/app/controllers/admin/instances_controller.rb
@@ -62,7 +62,7 @@ module Admin
     end
 
     def filter_params
-      params.permit(:limited, :by_domain)
+      params.slice(*InstanceFilter::KEYS).permit(*InstanceFilter::KEYS)
     end
   end
 end
diff --git a/app/controllers/admin/invites_controller.rb b/app/controllers/admin/invites_controller.rb
index 44a8eec77..dabfe9765 100644
--- a/app/controllers/admin/invites_controller.rb
+++ b/app/controllers/admin/invites_controller.rb
@@ -47,7 +47,7 @@ module Admin
     end
 
     def filter_params
-      params.permit(:available, :expired)
+      params.slice(*InviteFilter::KEYS).permit(*InviteFilter::KEYS)
     end
   end
 end
diff --git a/app/controllers/admin/relationships_controller.rb b/app/controllers/admin/relationships_controller.rb
new file mode 100644
index 000000000..f8a95cfc8
--- /dev/null
+++ b/app/controllers/admin/relationships_controller.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Admin
+  class RelationshipsController < BaseController
+    before_action :set_account
+
+    PER_PAGE = 40
+
+    def index
+      authorize :account, :index?
+
+      @accounts = RelationshipFilter.new(@account, filter_params).results.page(params[:page]).per(PER_PAGE)
+    end
+
+    private
+
+    def set_account
+      @account = Account.find(params[:account_id])
+    end
+
+    def filter_params
+      params.slice(*RelationshipFilter::KEYS).permit(*RelationshipFilter::KEYS)
+    end
+  end
+end
diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb
index 09ce1761c..7c831b3d4 100644
--- a/app/controllers/admin/reports_controller.rb
+++ b/app/controllers/admin/reports_controller.rb
@@ -52,12 +52,7 @@ module Admin
     end
 
     def filter_params
-      params.permit(
-        :account_id,
-        :resolved,
-        :target_account_id,
-        :by_target_domain
-      )
+      params.slice(*ReportFilter::KEYS).permit(*ReportFilter::KEYS)
     end
 
     def set_report
diff --git a/app/controllers/admin/tags_controller.rb b/app/controllers/admin/tags_controller.rb
index 65341bbfb..59df4470e 100644
--- a/app/controllers/admin/tags_controller.rb
+++ b/app/controllers/admin/tags_controller.rb
@@ -73,7 +73,7 @@ module Admin
     end
 
     def filter_params
-      params.slice(:directory, :reviewed, :unreviewed, :pending_review, :page, :popular, :active, :name).permit(:directory, :reviewed, :unreviewed, :pending_review, :page, :popular, :active, :name)
+      params.slice(:page, *TagFilter::KEYS).permit(:page, *TagFilter::KEYS)
     end
 
     def tag_params
diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb
index 144fdd6ac..68bf425f4 100644
--- a/app/controllers/api/base_controller.rb
+++ b/app/controllers/api/base_controller.rb
@@ -85,7 +85,7 @@ class Api::BaseController < ApplicationController
   end
 
   def require_authenticated_user!
-    render json: { error: 'This API requires an authenticated user' }, status: 401 unless current_user
+    render json: { error: 'This method requires an authenticated user' }, status: 401 unless current_user
   end
 
   def require_user!
diff --git a/app/controllers/api/oembed_controller.rb b/app/controllers/api/oembed_controller.rb
index 37a163cd3..66da65bed 100644
--- a/app/controllers/api/oembed_controller.rb
+++ b/app/controllers/api/oembed_controller.rb
@@ -1,15 +1,25 @@
 # frozen_string_literal: true
 
 class Api::OEmbedController < Api::BaseController
-  respond_to :json
+  skip_before_action :require_authenticated_user!
+
+  before_action :set_status
+  before_action :require_public_status!
 
   def show
-    @status = status_finder.status
     render json: @status, serializer: OEmbedSerializer, width: maxwidth_or_default, height: maxheight_or_default
   end
 
   private
 
+  def set_status
+    @status = status_finder.status
+  end
+
+  def require_public_status!
+    not_found if @status.hidden?
+  end
+
   def status_finder
     StatusFinder.new(params[:url])
   end
diff --git a/app/controllers/api/v1/accounts/follower_accounts_controller.rb b/app/controllers/api/v1/accounts/follower_accounts_controller.rb
index 2dabb8398..e360b8a92 100644
--- a/app/controllers/api/v1/accounts/follower_accounts_controller.rb
+++ b/app/controllers/api/v1/accounts/follower_accounts_controller.rb
@@ -21,11 +21,13 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
   def load_accounts
     return [] if hide_results?
 
-    default_accounts.merge(paginated_follows).to_a
+    scope = default_accounts
+    scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil?
+    scope.merge(paginated_follows).to_a
   end
 
   def hide_results?
-    (@account.user_hides_network? && current_account.id != @account.id) || (current_account && @account.blocking?(current_account))
+    (@account.user_hides_network? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account))
   end
 
   def default_accounts
diff --git a/app/controllers/api/v1/accounts/following_accounts_controller.rb b/app/controllers/api/v1/accounts/following_accounts_controller.rb
index 44e89804b..a405b365f 100644
--- a/app/controllers/api/v1/accounts/following_accounts_controller.rb
+++ b/app/controllers/api/v1/accounts/following_accounts_controller.rb
@@ -21,11 +21,13 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
   def load_accounts
     return [] if hide_results?
 
-    default_accounts.merge(paginated_follows).to_a
+    scope = default_accounts
+    scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil?
+    scope.merge(paginated_follows).to_a
   end
 
   def hide_results?
-    (@account.user_hides_network? && current_account.id != @account.id) || (current_account && @account.blocking?(current_account))
+    (@account.user_hides_network? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account))
   end
 
   def default_accounts
diff --git a/app/controllers/api/v1/announcements/reactions_controller.rb b/app/controllers/api/v1/announcements/reactions_controller.rb
new file mode 100644
index 000000000..e4a72e595
--- /dev/null
+++ b/app/controllers/api/v1/announcements/reactions_controller.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class Api::V1::Announcements::ReactionsController < Api::BaseController
+  before_action -> { doorkeeper_authorize! :write, :'write:favourites' }
+  before_action :require_user!
+
+  before_action :set_announcement
+  before_action :set_reaction, except: :update
+
+  def update
+    @announcement.announcement_reactions.create!(account: current_account, name: params[:id])
+    render_empty
+  end
+
+  def destroy
+    @reaction.destroy!
+    render_empty
+  end
+
+  private
+
+  def set_reaction
+    @reaction = @announcement.announcement_reactions.where(account: current_account).find_by!(name: params[:id])
+  end
+
+  def set_announcement
+    @announcement = Announcement.published.find(params[:announcement_id])
+  end
+end
diff --git a/app/controllers/api/v1/announcements_controller.rb b/app/controllers/api/v1/announcements_controller.rb
new file mode 100644
index 000000000..1e692ff75
--- /dev/null
+++ b/app/controllers/api/v1/announcements_controller.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class Api::V1::AnnouncementsController < Api::BaseController
+  before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: :dismiss
+  before_action :require_user!
+  before_action :set_announcements, only: :index
+  before_action :set_announcement, except: :index
+
+  def index
+    render json: @announcements, each_serializer: REST::AnnouncementSerializer
+  end
+
+  def dismiss
+    AnnouncementMute.create!(account: current_account, announcement: @announcement)
+    render_empty
+  end
+
+  private
+
+  def set_announcements
+    @announcements = begin
+      Announcement.published.chronological
+    end
+  end
+
+  def set_announcement
+    @announcement = Announcement.published.find(params[:id])
+  end
+end
diff --git a/app/controllers/api/v1/media_controller.rb b/app/controllers/api/v1/media_controller.rb
index aaa93b615..81825db15 100644
--- a/app/controllers/api/v1/media_controller.rb
+++ b/app/controllers/api/v1/media_controller.rb
@@ -4,9 +4,6 @@ class Api::V1::MediaController < Api::BaseController
   before_action -> { doorkeeper_authorize! :write, :'write:media' }
   before_action :require_user!
 
-  include ObfuscateFilename
-  obfuscate_filename :file
-
   respond_to :json
 
   def create
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 657e57831..99eff360e 100644
--- a/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb
+++ b/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb
@@ -17,7 +17,9 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController
   private
 
   def load_accounts
-    default_accounts.merge(paginated_favourites).to_a
+    scope = default_accounts
+    scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil?
+    scope.merge(paginated_favourites).to_a
   end
 
   def default_accounts
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 6851099f6..cc285ad23 100644
--- a/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb
+++ b/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb
@@ -17,7 +17,9 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController
   private
 
   def load_accounts
-    default_accounts.merge(paginated_statuses).to_a
+    scope = default_accounts
+    scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil?
+    scope.merge(paginated_statuses).to_a
   end
 
   def default_accounts
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index f1a4f0d02..c882d40ab 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -25,6 +25,7 @@ class ApplicationController < ActionController::Base
   rescue_from ActionController::InvalidAuthenticityToken, with: :unprocessable_entity
   rescue_from ActionController::UnknownFormat, with: :not_acceptable
   rescue_from ActionController::ParameterMissing, with: :bad_request
+  rescue_from Paperclip::AdapterRegistry::NoHandlerError, with: :bad_request
   rescue_from ActiveRecord::RecordNotFound, with: :not_found
   rescue_from Mastodon::NotPermittedError, with: :forbidden
   rescue_from HTTP::Error, OpenSSL::SSL::SSLError, with: :internal_server_error
@@ -211,7 +212,12 @@ class ApplicationController < ActionController::Base
   end
 
   def respond_with_error(code)
-    use_pack 'error'
-    render "errors/#{code}", layout: 'error', status: code, formats: [:html]
+    respond_to do |format|
+      format.any do
+        use_pack 'error'
+        render "errors/#{code}", layout: 'error', status: code, formats: [:html]
+      end
+      format.json { render json: { error: Rack::Utils::HTTP_STATUS_CODES[code] }, status: code }
+    end
   end
 end
diff --git a/app/controllers/auth/passwords_controller.rb b/app/controllers/auth/passwords_controller.rb
index a59806f0d..c224e1a03 100644
--- a/app/controllers/auth/passwords_controller.rb
+++ b/app/controllers/auth/passwords_controller.rb
@@ -7,6 +7,12 @@ class Auth::PasswordsController < Devise::PasswordsController
 
   layout 'auth'
 
+  def update
+    super do |resource|
+      resource.session_activations.destroy_all if resource.errors.empty?
+    end
+  end
+
   private
 
   def check_validity_of_reset_password_token
diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb
index 068375843..531df7751 100644
--- a/app/controllers/auth/registrations_controller.rb
+++ b/app/controllers/auth/registrations_controller.rb
@@ -11,6 +11,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
   before_action :set_instance_presenter, only: [:new, :create, :update]
   before_action :set_body_classes, only: [:new, :create, :edit, :update]
   before_action :require_not_suspended!, only: [:update]
+  before_action :set_cache_headers, only: [:edit, :update]
 
   skip_before_action :require_functional!, only: [:edit, :update]
 
@@ -22,10 +23,17 @@ class Auth::RegistrationsController < Devise::RegistrationsController
     not_found
   end
 
+  def update
+    super do |resource|
+      resource.clear_other_sessions(current_session.session_id) if resource.saved_change_to_encrypted_password?
+    end
+  end
+
   protected
 
   def update_resource(resource, params)
     params[:password] = nil if Devise.pam_authentication && resource.encrypted_password.blank?
+
     super
   end
 
@@ -114,4 +122,8 @@ class Auth::RegistrationsController < Devise::RegistrationsController
   def require_not_suspended!
     forbidden if current_account.suspended?
   end
+
+  def set_cache_headers
+    response.headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
+  end
 end
diff --git a/app/controllers/concerns/obfuscate_filename.rb b/app/controllers/concerns/obfuscate_filename.rb
deleted file mode 100644
index 22736ec3a..000000000
--- a/app/controllers/concerns/obfuscate_filename.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# frozen_string_literal: true
-
-module ObfuscateFilename
-  extend ActiveSupport::Concern
-
-  class_methods do
-    def obfuscate_filename(path)
-      before_action do
-        file = params.dig(*path)
-        next if file.nil?
-
-        file.original_filename = SecureRandom.hex(8) + File.extname(file.original_filename)
-      end
-    end
-  end
-end
diff --git a/app/controllers/filters_controller.rb b/app/controllers/filters_controller.rb
index f1e110d87..76be03e53 100644
--- a/app/controllers/filters_controller.rb
+++ b/app/controllers/filters_controller.rb
@@ -1,10 +1,9 @@
 # frozen_string_literal: true
 
 class FiltersController < ApplicationController
-  include Authorization
-
   layout 'admin'
 
+  before_action :authenticate_user!
   before_action :set_filters, only: :index
   before_action :set_filter, only: [:edit, :update, :destroy]
   before_action :set_pack
diff --git a/app/controllers/follower_accounts_controller.rb b/app/controllers/follower_accounts_controller.rb
index df46f5f72..a5dfffd6d 100644
--- a/app/controllers/follower_accounts_controller.rb
+++ b/app/controllers/follower_accounts_controller.rb
@@ -19,7 +19,6 @@ class FollowerAccountsController < ApplicationController
         next if @account.user_hides_network?
 
         follows
-        @relationships = AccountRelationshipsPresenter.new(follows.map(&:account_id), current_user.account_id) if user_signed_in?
       end
 
       format.json do
@@ -38,7 +37,11 @@ class FollowerAccountsController < ApplicationController
   private
 
   def follows
-    @follows ||= Follow.where(target_account: @account).recent.page(params[:page]).per(FOLLOW_PER_PAGE).preload(:account)
+    return @follows if defined?(@follows)
+
+    scope = Follow.where(target_account: @account)
+    scope = scope.where.not(account_id: current_account.excluded_from_timeline_account_ids) if user_signed_in?
+    @follows = scope.recent.page(params[:page]).per(FOLLOW_PER_PAGE).preload(:account)
   end
 
   def page_requested?
diff --git a/app/controllers/following_accounts_controller.rb b/app/controllers/following_accounts_controller.rb
index 8cab67ff5..ff23d97f9 100644
--- a/app/controllers/following_accounts_controller.rb
+++ b/app/controllers/following_accounts_controller.rb
@@ -19,7 +19,6 @@ class FollowingAccountsController < ApplicationController
         next if @account.user_hides_network?
 
         follows
-        @relationships = AccountRelationshipsPresenter.new(follows.map(&:target_account_id), current_user.account_id) if user_signed_in?
       end
 
       format.json do
@@ -38,7 +37,11 @@ class FollowingAccountsController < ApplicationController
   private
 
   def follows
-    @follows ||= Follow.where(account: @account).recent.page(params[:page]).per(FOLLOW_PER_PAGE).preload(:target_account)
+    return @follows if defined?(@follows)
+
+    scope = Follow.where(account: @account)
+    scope = scope.where.not(target_account_id: current_account.excluded_from_timeline_account_ids) if user_signed_in?
+    @follows = scope.recent.page(params[:page]).per(FOLLOW_PER_PAGE).preload(:target_account)
   end
 
   def page_requested?
diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb
index f6f5d1ecc..137346ed0 100644
--- a/app/controllers/oauth/authorizations_controller.rb
+++ b/app/controllers/oauth/authorizations_controller.rb
@@ -6,6 +6,7 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
   before_action :store_current_location
   before_action :authenticate_resource_owner!
   before_action :set_pack
+  before_action :set_cache_headers
 
   include Localized
 
@@ -32,4 +33,8 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
   def truthy_param?(key)
     ActiveModel::Type::Boolean.new.cast(params[key])
   end
+
+  def set_cache_headers
+    response.headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
+  end
 end
diff --git a/app/controllers/relationships_controller.rb b/app/controllers/relationships_controller.rb
index c87a0cf13..f1ab980c8 100644
--- a/app/controllers/relationships_controller.rb
+++ b/app/controllers/relationships_controller.rb
@@ -20,53 +20,13 @@ class RelationshipsController < ApplicationController
   rescue ActionController::ParameterMissing
     # Do nothing
   ensure
-    redirect_to relationships_path(current_params)
+    redirect_to relationships_path(filter_params)
   end
 
   private
 
   def set_accounts
-    @accounts = relationships_scope.page(params[:page]).per(40)
-  end
-
-  def relationships_scope
-    scope = begin
-      if following_relationship?
-        current_account.following.eager_load(:account_stat).reorder(nil)
-      else
-        current_account.followers.eager_load(:account_stat).reorder(nil)
-      end
-    end
-
-    scope.merge!(Follow.recent)             if params[:order].blank? || params[:order] == 'recent'
-    scope.merge!(Account.by_recent_status)  if params[:order] == 'active'
-    scope.merge!(mutual_relationship_scope) if mutual_relationship?
-    scope.merge!(moved_account_scope)       if params[:status] == 'moved'
-    scope.merge!(primary_account_scope)     if params[:status] == 'primary'
-    scope.merge!(by_domain_scope)           if params[:by_domain].present?
-    scope.merge!(dormant_account_scope)     if params[:activity] == 'dormant'
-
-    scope
-  end
-
-  def mutual_relationship_scope
-    Account.where(id: current_account.following)
-  end
-
-  def moved_account_scope
-    Account.where.not(moved_to_account_id: nil)
-  end
-
-  def primary_account_scope
-    Account.where(moved_to_account_id: nil)
-  end
-
-  def dormant_account_scope
-    AccountStat.where(last_status_at: nil).or(AccountStat.where(AccountStat.arel_table[:last_status_at].lt(1.month.ago)))
-  end
-
-  def by_domain_scope
-    Account.where(domain: params[:by_domain])
+    @accounts = RelationshipFilter.new(current_account, filter_params).results.page(params[:page]).per(40)
   end
 
   def form_account_batch_params
@@ -85,8 +45,8 @@ class RelationshipsController < ApplicationController
     params[:relationship] == 'followed_by'
   end
 
-  def current_params
-    params.slice(:page, :status, :relationship, :by_domain, :activity, :order).permit(:page, :status, :relationship, :by_domain, :activity, :order)
+  def filter_params
+    params.slice(:page, *RelationshipFilter::KEYS).permit(:page, *RelationshipFilter::KEYS)
   end
 
   def action_from_button
diff --git a/app/controllers/settings/base_controller.rb b/app/controllers/settings/base_controller.rb
index 8c394a6d3..b97603af6 100644
--- a/app/controllers/settings/base_controller.rb
+++ b/app/controllers/settings/base_controller.rb
@@ -3,6 +3,7 @@
 class Settings::BaseController < ApplicationController
   before_action :set_pack
   before_action :set_body_classes
+  before_action :set_cache_headers
 
   private
 
@@ -13,4 +14,8 @@ class Settings::BaseController < ApplicationController
   def set_body_classes
     @body_classes = 'admin'
   end
+
+  def set_cache_headers
+    response.headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
+  end
 end
diff --git a/app/controllers/settings/profiles_controller.rb b/app/controllers/settings/profiles_controller.rb
index 8b640cdca..19a7ce157 100644
--- a/app/controllers/settings/profiles_controller.rb
+++ b/app/controllers/settings/profiles_controller.rb
@@ -1,16 +1,11 @@
 # frozen_string_literal: true
 
 class Settings::ProfilesController < Settings::BaseController
-  include ObfuscateFilename
-
   layout 'admin'
 
   before_action :authenticate_user!
   before_action :set_account
 
-  obfuscate_filename [:account, :avatar]
-  obfuscate_filename [:account, :header]
-
   def show
     @account.build_fields
   end
diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb
index 1b00d38c9..588063d01 100644
--- a/app/controllers/statuses_controller.rb
+++ b/app/controllers/statuses_controller.rb
@@ -49,7 +49,7 @@ class StatusesController < ApplicationController
 
   def embed
     use_pack 'embed'
-    raise ActiveRecord::RecordNotFound if @status.hidden?
+    return not_found if @status.hidden?
 
     expires_in 180, public: true
     response.headers['X-Frame-Options'] = 'ALLOWALL'
@@ -71,7 +71,7 @@ class StatusesController < ApplicationController
     @status = @account.statuses.find(params[:id])
     authorize @status, :show?
   rescue Mastodon::NotPermittedError
-    raise ActiveRecord::RecordNotFound
+    not_found
   end
 
   def set_instance_presenter
diff --git a/app/controllers/well_known/host_meta_controller.rb b/app/controllers/well_known/host_meta_controller.rb
index 2e9298c4a..2fd6bc7cc 100644
--- a/app/controllers/well_known/host_meta_controller.rb
+++ b/app/controllers/well_known/host_meta_controller.rb
@@ -8,12 +8,8 @@ module WellKnown
 
     def show
       @webfinger_template = "#{webfinger_url}?resource={uri}"
-
-      respond_to do |format|
-        format.xml { render content_type: 'application/xrd+xml' }
-      end
-
       expires_in 3.days, public: true
+      render content_type: 'application/xrd+xml', formats: [:xml]
     end
   end
 end
diff --git a/app/helpers/accounts_helper.rb b/app/helpers/accounts_helper.rb
index 7fcc4e816..c9e95d8d8 100644
--- a/app/helpers/accounts_helper.rb
+++ b/app/helpers/accounts_helper.rb
@@ -3,7 +3,7 @@
 module AccountsHelper
   def display_name(account, **options)
     if options[:custom_emojify]
-      Formatter.instance.format_display_name(account, options)
+      Formatter.instance.format_display_name(account, **options)
     else
       account.display_name.presence || account.username
     end
@@ -13,7 +13,7 @@ module AccountsHelper
     if account.local?
       "@#{account.acct}@#{Rails.configuration.x.local_domain}"
     else
-      "@#{account.acct}"
+      "@#{account.pretty_acct}"
     end
   end
 
diff --git a/app/helpers/admin/action_logs_helper.rb b/app/helpers/admin/action_logs_helper.rb
index 608a99dd5..6bc75aa56 100644
--- a/app/helpers/admin/action_logs_helper.rb
+++ b/app/helpers/admin/action_logs_helper.rb
@@ -22,6 +22,8 @@ module Admin::ActionLogsHelper
       log.recorded_changes.slice('severity', 'reject_media')
     elsif log.target_type == 'Status' && log.action == :update
       log.recorded_changes.slice('sensitive')
+    elsif log.target_type == 'Announcement' && log.action == :update
+      log.recorded_changes.slice('text', 'starts_at', 'ends_at', 'all_day')
     end
   end
 
@@ -52,6 +54,8 @@ module Admin::ActionLogsHelper
       'pencil'
     when 'AccountWarning'
       'warning'
+    when 'Announcement'
+      'bullhorn'
     end
   end
 
@@ -94,6 +98,8 @@ module Admin::ActionLogsHelper
       link_to record.account.acct, ActivityPub::TagManager.instance.url_for(record)
     when 'AccountWarning'
       link_to record.target_account.acct, admin_account_path(record.target_account_id)
+    when 'Announcement'
+      link_to "##{record.id}", edit_admin_announcement_path(record.id)
     end
   end
 
@@ -111,6 +117,8 @@ module Admin::ActionLogsHelper
       else
         I18n.t('admin.action_logs.deleted_status')
       end
+    when 'Announcement'
+      "##{attributes['id']}"
     end
   end
 end
diff --git a/app/helpers/admin/announcements_helper.rb b/app/helpers/admin/announcements_helper.rb
new file mode 100644
index 000000000..0c053ddec
--- /dev/null
+++ b/app/helpers/admin/announcements_helper.rb
@@ -0,0 +1,11 @@
+# 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/filter_helper.rb b/app/helpers/admin/filter_helper.rb
index fc4f15985..6ab92939d 100644
--- a/app/helpers/admin/filter_helper.rb
+++ b/app/helpers/admin/filter_helper.rb
@@ -1,15 +1,16 @@
 # frozen_string_literal: true
 
 module Admin::FilterHelper
-  ACCOUNT_FILTERS      = %i(local remote by_domain active pending silenced suspended username display_name email ip staff).freeze
-  REPORT_FILTERS       = %i(resolved account_id target_account_id by_target_domain).freeze
-  INVITE_FILTER        = %i(available expired).freeze
-  CUSTOM_EMOJI_FILTERS = %i(local remote by_domain shortcode).freeze
-  TAGS_FILTERS         = %i(directory reviewed unreviewed pending_review popular active name).freeze
-  INSTANCES_FILTERS    = %i(limited by_domain).freeze
-  FOLLOWERS_FILTERS    = %i(relationship status by_domain activity order).freeze
-
-  FILTERS = ACCOUNT_FILTERS + REPORT_FILTERS + INVITE_FILTER + CUSTOM_EMOJI_FILTERS + TAGS_FILTERS + INSTANCES_FILTERS + FOLLOWERS_FILTERS
+  FILTERS = [
+    AccountFilter::KEYS,
+    CustomEmojiFilter::KEYS,
+    ReportFilter::KEYS,
+    TagFilter::KEYS,
+    InstanceFilter::KEYS,
+    InviteFilter::KEYS,
+    RelationshipFilter::KEYS,
+    AnnouncementFilter::KEYS,
+  ].flatten.freeze
 
   def filter_link_to(text, link_to_params, link_class_params = link_to_params)
     new_url   = filtered_url_for(link_to_params)
diff --git a/app/helpers/routing_helper.rb b/app/helpers/routing_helper.rb
index 998b7566f..fb24a1b28 100644
--- a/app/helpers/routing_helper.rb
+++ b/app/helpers/routing_helper.rb
@@ -13,13 +13,13 @@ module RoutingHelper
   end
 
   def full_asset_url(source, **options)
-    source = ActionController::Base.helpers.asset_url(source, options) unless use_storage?
+    source = ActionController::Base.helpers.asset_url(source, **options) unless use_storage?
 
     URI.join(root_url, source).to_s
   end
 
   def full_pack_url(source, **options)
-    full_asset_url(asset_pack_path(source, options))
+    full_asset_url(asset_pack_path(source, **options))
   end
 
   private
diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb
index 39eb4180e..825aa974d 100644
--- a/app/helpers/settings_helper.rb
+++ b/app/helpers/settings_helper.rb
@@ -32,9 +32,11 @@ module SettingsHelper
     hy: 'Հայերեն',
     id: 'Bahasa Indonesia',
     io: 'Ido',
+    is: 'Íslenska',
     it: 'Italiano',
     ja: '日本語',
     ka: 'ქართული',
+    kab: 'Taqbaylit',
     kk: 'Қазақша',
     kn: 'ಕನ್ನಡ',
     ko: '한국어',
diff --git a/app/javascript/flavours/glitch/actions/announcements.js b/app/javascript/flavours/glitch/actions/announcements.js
new file mode 100644
index 000000000..871409d43
--- /dev/null
+++ b/app/javascript/flavours/glitch/actions/announcements.js
@@ -0,0 +1,180 @@
+import api from 'flavours/glitch/util/api';
+import { normalizeAnnouncement } from './importer/normalizer';
+
+export const ANNOUNCEMENTS_FETCH_REQUEST = 'ANNOUNCEMENTS_FETCH_REQUEST';
+export const ANNOUNCEMENTS_FETCH_SUCCESS = 'ANNOUNCEMENTS_FETCH_SUCCESS';
+export const ANNOUNCEMENTS_FETCH_FAIL    = 'ANNOUNCEMENTS_FETCH_FAIL';
+export const ANNOUNCEMENTS_UPDATE        = 'ANNOUNCEMENTS_UPDATE';
+export const ANNOUNCEMENTS_DELETE        = 'ANNOUNCEMENTS_DELETE';
+
+export const ANNOUNCEMENTS_DISMISS_REQUEST = 'ANNOUNCEMENTS_DISMISS_REQUEST';
+export const ANNOUNCEMENTS_DISMISS_SUCCESS = 'ANNOUNCEMENTS_DISMISS_SUCCESS';
+export const ANNOUNCEMENTS_DISMISS_FAIL    = 'ANNOUNCEMENTS_DISMISS_FAIL';
+
+export const ANNOUNCEMENTS_REACTION_ADD_REQUEST = 'ANNOUNCEMENTS_REACTION_ADD_REQUEST';
+export const ANNOUNCEMENTS_REACTION_ADD_SUCCESS = 'ANNOUNCEMENTS_REACTION_ADD_SUCCESS';
+export const ANNOUNCEMENTS_REACTION_ADD_FAIL    = 'ANNOUNCEMENTS_REACTION_ADD_FAIL';
+
+export const ANNOUNCEMENTS_REACTION_REMOVE_REQUEST = 'ANNOUNCEMENTS_REACTION_REMOVE_REQUEST';
+export const ANNOUNCEMENTS_REACTION_REMOVE_SUCCESS = 'ANNOUNCEMENTS_REACTION_REMOVE_SUCCESS';
+export const ANNOUNCEMENTS_REACTION_REMOVE_FAIL    = 'ANNOUNCEMENTS_REACTION_REMOVE_FAIL';
+
+export const ANNOUNCEMENTS_REACTION_UPDATE = 'ANNOUNCEMENTS_REACTION_UPDATE';
+
+export const ANNOUNCEMENTS_TOGGLE_SHOW = 'ANNOUNCEMENTS_TOGGLE_SHOW';
+
+const noOp = () => {};
+
+export const fetchAnnouncements = (done = noOp) => (dispatch, getState) => {
+  dispatch(fetchAnnouncementsRequest());
+
+  api(getState).get('/api/v1/announcements').then(response => {
+    dispatch(fetchAnnouncementsSuccess(response.data.map(x => normalizeAnnouncement(x))));
+  }).catch(error => {
+    dispatch(fetchAnnouncementsFail(error));
+  }).finally(() => {
+    done();
+  });
+};
+
+export const fetchAnnouncementsRequest = () => ({
+  type: ANNOUNCEMENTS_FETCH_REQUEST,
+  skipLoading: true,
+});
+
+export const fetchAnnouncementsSuccess = announcements => ({
+  type: ANNOUNCEMENTS_FETCH_SUCCESS,
+  announcements,
+  skipLoading: true,
+});
+
+export const fetchAnnouncementsFail= error => ({
+  type: ANNOUNCEMENTS_FETCH_FAIL,
+  error,
+  skipLoading: true,
+  skipAlert: true,
+});
+
+export const updateAnnouncements = announcement => ({
+  type: ANNOUNCEMENTS_UPDATE,
+  announcement: normalizeAnnouncement(announcement),
+});
+
+export const dismissAnnouncement = announcementId => (dispatch, getState) => {
+  dispatch(dismissAnnouncementRequest(announcementId));
+
+  api(getState).post(`/api/v1/announcements/${announcementId}/dismiss`).then(() => {
+    dispatch(dismissAnnouncementSuccess(announcementId));
+  }).catch(error => {
+    dispatch(dismissAnnouncementFail(announcementId, error));
+  });
+};
+
+export const dismissAnnouncementRequest = announcementId => ({
+  type: ANNOUNCEMENTS_DISMISS_REQUEST,
+  id: announcementId,
+});
+
+export const dismissAnnouncementSuccess = announcementId => ({
+  type: ANNOUNCEMENTS_DISMISS_SUCCESS,
+  id: announcementId,
+});
+
+export const dismissAnnouncementFail = (announcementId, error) => ({
+  type: ANNOUNCEMENTS_DISMISS_FAIL,
+  id: announcementId,
+  error,
+});
+
+export const addReaction = (announcementId, name) => (dispatch, getState) => {
+  const announcement = getState().getIn(['announcements', 'items']).find(x => x.get('id') === announcementId);
+
+  let alreadyAdded = false;
+
+  if (announcement) {
+    const reaction = announcement.get('reactions').find(x => x.get('name') === name);
+    if (reaction && reaction.get('me')) {
+      alreadyAdded = true;
+    }
+  }
+
+  if (!alreadyAdded) {
+    dispatch(addReactionRequest(announcementId, name, alreadyAdded));
+  }
+
+  api(getState).put(`/api/v1/announcements/${announcementId}/reactions/${name}`).then(() => {
+    dispatch(addReactionSuccess(announcementId, name, alreadyAdded));
+  }).catch(err => {
+    if (!alreadyAdded) {
+      dispatch(addReactionFail(announcementId, name, err));
+    }
+  });
+};
+
+export const addReactionRequest = (announcementId, name) => ({
+  type: ANNOUNCEMENTS_REACTION_ADD_REQUEST,
+  id: announcementId,
+  name,
+  skipLoading: true,
+});
+
+export const addReactionSuccess = (announcementId, name) => ({
+  type: ANNOUNCEMENTS_REACTION_ADD_SUCCESS,
+  id: announcementId,
+  name,
+  skipLoading: true,
+});
+
+export const addReactionFail = (announcementId, name, error) => ({
+  type: ANNOUNCEMENTS_REACTION_ADD_FAIL,
+  id: announcementId,
+  name,
+  error,
+  skipLoading: true,
+});
+
+export const removeReaction = (announcementId, name) => (dispatch, getState) => {
+  dispatch(removeReactionRequest(announcementId, name));
+
+  api(getState).delete(`/api/v1/announcements/${announcementId}/reactions/${name}`).then(() => {
+    dispatch(removeReactionSuccess(announcementId, name));
+  }).catch(err => {
+    dispatch(removeReactionFail(announcementId, name, err));
+  });
+};
+
+export const removeReactionRequest = (announcementId, name) => ({
+  type: ANNOUNCEMENTS_REACTION_REMOVE_REQUEST,
+  id: announcementId,
+  name,
+  skipLoading: true,
+});
+
+export const removeReactionSuccess = (announcementId, name) => ({
+  type: ANNOUNCEMENTS_REACTION_REMOVE_SUCCESS,
+  id: announcementId,
+  name,
+  skipLoading: true,
+});
+
+export const removeReactionFail = (announcementId, name, error) => ({
+  type: ANNOUNCEMENTS_REACTION_REMOVE_FAIL,
+  id: announcementId,
+  name,
+  error,
+  skipLoading: true,
+});
+
+export const updateReaction = reaction => ({
+  type: ANNOUNCEMENTS_REACTION_UPDATE,
+  reaction,
+});
+
+export const toggleShowAnnouncements = () => ({
+  type: ANNOUNCEMENTS_TOGGLE_SHOW,
+});
+
+export const deleteAnnouncement = id => ({
+  type: ANNOUNCEMENTS_DELETE,
+  id,
+});
diff --git a/app/javascript/flavours/glitch/actions/compose.js b/app/javascript/flavours/glitch/actions/compose.js
index f80642bd8..0be746048 100644
--- a/app/javascript/flavours/glitch/actions/compose.js
+++ b/app/javascript/flavours/glitch/actions/compose.js
@@ -91,9 +91,11 @@ export function cycleElefriendCompose() {
 
 export function replyCompose(status, routerHistory) {
   return (dispatch, getState) => {
+    const prependCWRe = getState().getIn(['local_settings', 'prepend_cw_re']);
     dispatch({
       type: COMPOSE_REPLY,
       status: status,
+      prependCWRe: prependCWRe,
     });
 
     ensureComposeIsVisible(getState, routerHistory);
diff --git a/app/javascript/flavours/glitch/actions/importer/normalizer.js b/app/javascript/flavours/glitch/actions/importer/normalizer.js
index 2bc603930..52ad17779 100644
--- a/app/javascript/flavours/glitch/actions/importer/normalizer.js
+++ b/app/javascript/flavours/glitch/actions/importer/normalizer.js
@@ -74,7 +74,6 @@ export function normalizeStatus(status, normalOldStatus) {
 
 export function normalizePoll(poll) {
   const normalPoll = { ...poll };
-
   const emojiMap = makeEmojiMap(normalPoll);
 
   normalPoll.options = poll.options.map((option, index) => ({
@@ -85,3 +84,12 @@ export function normalizePoll(poll) {
 
   return normalPoll;
 }
+
+export function normalizeAnnouncement(announcement) {
+  const normalAnnouncement = { ...announcement };
+  const emojiMap = makeEmojiMap(normalAnnouncement);
+
+  normalAnnouncement.contentHtml = emojify(normalAnnouncement.content, emojiMap);
+
+  return normalAnnouncement;
+}
diff --git a/app/javascript/flavours/glitch/actions/markers.js b/app/javascript/flavours/glitch/actions/markers.js
index c3a5fe86f..7ffab404d 100644
--- a/app/javascript/flavours/glitch/actions/markers.js
+++ b/app/javascript/flavours/glitch/actions/markers.js
@@ -1,9 +1,15 @@
+import api from 'flavours/glitch/util/api';
+
+export const MARKERS_FETCH_REQUEST = 'MARKERS_FETCH_REQUEST';
+export const MARKERS_FETCH_SUCCESS = 'MARKERS_FETCH_SUCCESS';
+export const MARKERS_FETCH_FAIL    = 'MARKERS_FETCH_FAIL';
+
 export const submitMarkers = () => (dispatch, getState) => {
   const accessToken = getState().getIn(['meta', 'access_token'], '');
   const params      = {};
 
   const lastHomeId         = getState().getIn(['timelines', 'home', 'items', 0]);
-  const lastNotificationId = getState().getIn(['notifications', 'items', 0, 'id']);
+  const lastNotificationId = getState().getIn(['notifications', 'lastReadId']);
 
   if (lastHomeId) {
     params.home = {
@@ -11,7 +17,7 @@ export const submitMarkers = () => (dispatch, getState) => {
     };
   }
 
-  if (lastNotificationId) {
+  if (lastNotificationId && lastNotificationId !== '0') {
     params.notifications = {
       last_read_id: lastNotificationId,
     };
@@ -28,3 +34,39 @@ export const submitMarkers = () => (dispatch, getState) => {
   client.setRequestHeader('Authorization', `Bearer ${accessToken}`);
   client.send(JSON.stringify(params));
 };
+
+export const fetchMarkers = () => (dispatch, getState) => {
+    const params = { timeline: ['notifications'] };
+
+    dispatch(fetchMarkersRequest());
+
+    api(getState).get('/api/v1/markers', { params }).then(response => {
+      dispatch(fetchMarkersSuccess(response.data));
+    }).catch(error => {
+      dispatch(fetchMarkersFail(error));
+    });
+};
+
+export function fetchMarkersRequest() {
+  return {
+    type: MARKERS_FETCH_REQUEST,
+    skipLoading: true,
+  };
+};
+
+export function fetchMarkersSuccess(markers) {
+  return {
+    type: MARKERS_FETCH_SUCCESS,
+    markers,
+    skipLoading: true,
+  };
+};
+
+export function fetchMarkersFail(error) {
+  return {
+    type: MARKERS_FETCH_FAIL,
+    error,
+    skipLoading: true,
+    skipAlert: true,
+  };
+};
diff --git a/app/javascript/flavours/glitch/actions/notifications.js b/app/javascript/flavours/glitch/actions/notifications.js
index 940f3c3d4..b3de7b5bf 100644
--- a/app/javascript/flavours/glitch/actions/notifications.js
+++ b/app/javascript/flavours/glitch/actions/notifications.js
@@ -168,9 +168,9 @@ export function expandNotifications({ maxId } = {}, done = noOp) {
 
       dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null, isLoadingMore, isLoadingRecent, isLoadingRecent && preferPendingItems));
       fetchRelatedRelationships(dispatch, response.data);
-      done();
     }).catch(error => {
       dispatch(expandNotificationsFail(error, isLoadingMore));
+    }).finally(() => {
       done();
     });
   };
@@ -199,6 +199,7 @@ export function expandNotificationsFail(error, isLoadingMore) {
     type: NOTIFICATIONS_EXPAND_FAIL,
     error,
     skipLoading: !isLoadingMore,
+    skipAlert: !isLoadingMore,
   };
 };
 
diff --git a/app/javascript/flavours/glitch/actions/streaming.js b/app/javascript/flavours/glitch/actions/streaming.js
index 21379f492..2f82ea805 100644
--- a/app/javascript/flavours/glitch/actions/streaming.js
+++ b/app/javascript/flavours/glitch/actions/streaming.js
@@ -8,6 +8,12 @@ import {
 } from './timelines';
 import { updateNotifications, expandNotifications } from './notifications';
 import { updateConversations } from './conversations';
+import {
+  fetchAnnouncements,
+  updateAnnouncements,
+  updateReaction as updateAnnouncementsReaction,
+  deleteAnnouncement,
+} from './announcements';
 import { fetchFilters } from './filters';
 import { getLocale } from 'mastodon/locales';
 
@@ -44,6 +50,15 @@ export function connectTimelineStream (timelineId, path, pollingRefresh = null,
         case 'filters_changed':
           dispatch(fetchFilters());
           break;
+        case 'announcement':
+          dispatch(updateAnnouncements(JSON.parse(data.payload)));
+          break;
+        case 'announcement.reaction':
+          dispatch(updateAnnouncementsReaction(JSON.parse(data.payload)));
+          break;
+        case 'announcement.delete':
+          dispatch(deleteAnnouncement(data.payload));
+          break;
         }
       },
     };
@@ -51,7 +66,9 @@ export function connectTimelineStream (timelineId, path, pollingRefresh = null,
 }
 
 const refreshHomeTimelineAndNotification = (dispatch, done) => {
-  dispatch(expandHomeTimeline({}, () => dispatch(expandNotifications({}, done))));
+  dispatch(expandHomeTimeline({}, () =>
+    dispatch(expandNotifications({}, () =>
+      dispatch(fetchAnnouncements(done))))));
 };
 
 export const connectUserStream      = () => connectTimelineStream('home', 'user', refreshHomeTimelineAndNotification);
diff --git a/app/javascript/flavours/glitch/actions/timelines.js b/app/javascript/flavours/glitch/actions/timelines.js
index 097878c3b..2ef78025e 100644
--- a/app/javascript/flavours/glitch/actions/timelines.js
+++ b/app/javascript/flavours/glitch/actions/timelines.js
@@ -112,9 +112,9 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
 
       dispatch(importFetchedStatuses(response.data));
       dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.status === 206, isLoadingRecent, isLoadingMore, isLoadingRecent && preferPendingItems));
-      done();
     }).catch(error => {
       dispatch(expandTimelineFail(timelineId, error, isLoadingMore));
+    }).finally(() => {
       done();
     });
   };
diff --git a/app/javascript/flavours/glitch/components/animated_number.js b/app/javascript/flavours/glitch/components/animated_number.js
new file mode 100644
index 000000000..e3235e368
--- /dev/null
+++ b/app/javascript/flavours/glitch/components/animated_number.js
@@ -0,0 +1,65 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { FormattedNumber } from 'react-intl';
+import TransitionMotion from 'react-motion/lib/TransitionMotion';
+import spring from 'react-motion/lib/spring';
+import { reduceMotion } from 'flavours/glitch/util/initial_state';
+
+export default class AnimatedNumber extends React.PureComponent {
+
+  static propTypes = {
+    value: PropTypes.number.isRequired,
+  };
+
+  state = {
+    direction: 1,
+  };
+
+  componentWillReceiveProps (nextProps) {
+    if (nextProps.value > this.props.value) {
+      this.setState({ direction: 1 });
+    } else if (nextProps.value < this.props.value) {
+      this.setState({ direction: -1 });
+    }
+  }
+
+  willEnter = () => {
+    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 } = this.props;
+    const { direction } = this.state;
+
+    if (reduceMotion) {
+      return <FormattedNumber value={value} />;
+    }
+
+    const styles = [{
+      key: `${value}`,
+      data: value,
+      style: { y: spring(0, { damping: 35, stiffness: 400 }) },
+    }];
+
+    return (
+      <TransitionMotion styles={styles} willEnter={this.willEnter} willLeave={this.willLeave}>
+        {items => (
+          <span className='animated-number'>
+            {items.map(({ key, data, style }) => (
+              <span key={key} style={{ position: (direction * style.y) > 0 ? 'absolute' : 'static', transform: `translateY(${style.y * 100}%)` }}><FormattedNumber value={data} /></span>
+            ))}
+          </span>
+        )}
+      </TransitionMotion>
+    );
+  }
+
+}
diff --git a/app/javascript/flavours/glitch/components/column_header.js b/app/javascript/flavours/glitch/components/column_header.js
index dd1162429..01bd4a246 100644
--- a/app/javascript/flavours/glitch/components/column_header.js
+++ b/app/javascript/flavours/glitch/components/column_header.js
@@ -2,18 +2,14 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { createPortal } from 'react-dom';
 import classNames from 'classnames';
-import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
-import ImmutablePropTypes from 'react-immutable-proptypes';
+import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
 import Icon from 'flavours/glitch/components/icon';
 
-import NotificationPurgeButtonsContainer from 'flavours/glitch/containers/notification_purge_buttons_container';
-
 const messages = defineMessages({
   show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
   hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' },
   moveLeft: { id: 'column_header.moveLeft_settings', defaultMessage: 'Move column to the left' },
   moveRight: { id: 'column_header.moveRight_settings', defaultMessage: 'Move column to the right' },
-  enterNotifCleaning : { id: 'notification_purge.start', defaultMessage: 'Enter notification cleaning mode' },
 });
 
 export default @injectIntl
@@ -28,26 +24,21 @@ class ColumnHeader extends React.PureComponent {
     title: PropTypes.node,
     icon: PropTypes.string,
     active: PropTypes.bool,
-    localSettings : ImmutablePropTypes.map,
     multiColumn: PropTypes.bool,
     extraButton: PropTypes.node,
     showBackButton: PropTypes.bool,
-    notifCleaning: PropTypes.bool, // true only for the notification column
-    notifCleaningActive: PropTypes.bool,
-    onEnterCleaningMode: PropTypes.func,
     children: PropTypes.node,
     pinned: PropTypes.bool,
     placeholder: PropTypes.bool,
     onPin: PropTypes.func,
     onMove: PropTypes.func,
     onClick: PropTypes.func,
-    intl: PropTypes.object.isRequired,
+    appendContent: PropTypes.node,
   };
 
   state = {
     collapsed: true,
     animating: false,
-    animatingNCD: false,
   };
 
   historyBack = (skip) => {
@@ -89,10 +80,6 @@ class ColumnHeader extends React.PureComponent {
     this.setState({ animating: false });
   }
 
-  handleTransitionEndNCD = () => {
-    this.setState({ animatingNCD: false });
-  }
-
   handlePin = () => {
     if (!this.props.pinned) {
       this.historyBack();
@@ -100,16 +87,9 @@ class ColumnHeader extends React.PureComponent {
     this.props.onPin();
   }
 
-  onEnterCleaningMode = () => {
-    this.setState({ animatingNCD: true });
-    this.props.onEnterCleaningMode(!this.props.notifCleaningActive);
-  }
-
   render () {
-    const { intl, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, notifCleaning, notifCleaningActive, placeholder } = this.props;
-    const { collapsed, animating, animatingNCD } = this.state;
-
-    let title = this.props.title;
+    const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder, appendContent } = this.props;
+    const { collapsed, animating } = this.state;
 
     const wrapperClassName = classNames('column-header__wrapper', {
       'active': active,
@@ -128,20 +108,8 @@ class ColumnHeader extends React.PureComponent {
       'active': !collapsed,
     });
 
-    const notifCleaningButtonClassName = classNames('column-header__button', {
-      'active': notifCleaningActive,
-    });
-
-    const notifCleaningDrawerClassName = classNames('ncd column-header__collapsible', {
-      'collapsed': !notifCleaningActive,
-      'animating': animatingNCD,
-    });
-
     let extraContent, pinButton, moveButtons, backButton, collapseButton;
 
-    //*glitch
-    const msgEnterNotifCleaning = intl.formatMessage(messages.enterNotifCleaning);
-
     if (children) {
       extraContent = (
         <div key='extra-content' className='column-header__collapsible__extra'>
@@ -202,33 +170,17 @@ class ColumnHeader extends React.PureComponent {
           <div className='column-header__buttons'>
             {hasTitle && backButton}
             {extraButton}
-            { notifCleaning ? (
-              <button
-                aria-label={msgEnterNotifCleaning}
-                title={msgEnterNotifCleaning}
-                onClick={this.onEnterCleaningMode}
-                className={notifCleaningButtonClassName}
-              >
-                <Icon id='eraser' />
-              </button>
-            ) : null}
             {collapseButton}
           </div>
         </h1>
 
-        { notifCleaning ? (
-          <div className={notifCleaningDrawerClassName} onTransitionEnd={this.handleTransitionEndNCD}>
-            <div className='column-header__collapsible-inner nopad-drawer'>
-              {(notifCleaningActive || animatingNCD) ? (<NotificationPurgeButtonsContainer />) : null }
-            </div>
-          </div>
-        ) : null}
-
         <div className={collapsibleClassName} tabIndex={collapsed ? -1 : null} onTransitionEnd={this.handleTransitionEnd}>
           <div className='column-header__collapsible-inner'>
             {(!collapsed || animating) && collapsedContent}
           </div>
         </div>
+
+        {appendContent}
       </div>
     );
 
diff --git a/app/javascript/flavours/glitch/components/dropdown_menu.js b/app/javascript/flavours/glitch/components/dropdown_menu.js
index ab5b7a572..cc4d714e8 100644
--- a/app/javascript/flavours/glitch/components/dropdown_menu.js
+++ b/app/javascript/flavours/glitch/components/dropdown_menu.js
@@ -184,7 +184,7 @@ export default class Dropdown extends React.PureComponent {
     icon: PropTypes.string.isRequired,
     items: PropTypes.array.isRequired,
     size: PropTypes.number.isRequired,
-    ariaLabel: PropTypes.string,
+    title: PropTypes.string,
     disabled: PropTypes.bool,
     status: ImmutablePropTypes.map,
     isUserTouching: PropTypes.func,
@@ -197,7 +197,7 @@ export default class Dropdown extends React.PureComponent {
   };
 
   static defaultProps = {
-    ariaLabel: 'Menu',
+    title: 'Menu',
   };
 
   state = {
@@ -277,14 +277,14 @@ export default class Dropdown extends React.PureComponent {
   }
 
   render () {
-    const { icon, items, size, ariaLabel, disabled, dropdownPlacement, openDropdownId, openedViaKeyboard } = this.props;
+    const { icon, items, size, title, disabled, dropdownPlacement, openDropdownId, openedViaKeyboard } = this.props;
     const open = this.state.id === openDropdownId;
 
     return (
       <div>
         <IconButton
           icon={icon}
-          title={ariaLabel}
+          title={title}
           active={open}
           disabled={disabled}
           size={size}
diff --git a/app/javascript/flavours/glitch/components/media_gallery.js b/app/javascript/flavours/glitch/components/media_gallery.js
index 85ee79e11..9754c73dc 100644
--- a/app/javascript/flavours/glitch/components/media_gallery.js
+++ b/app/javascript/flavours/glitch/components/media_gallery.js
@@ -43,6 +43,7 @@ class Item extends React.PureComponent {
     onClick: PropTypes.func.isRequired,
     displayWidth: PropTypes.number,
     visible: PropTypes.bool.isRequired,
+    autoplay: PropTypes.bool,
   };
 
   static defaultProps = {
@@ -68,9 +69,13 @@ class Item extends React.PureComponent {
     }
   }
 
+  getAutoPlay() {
+    return this.props.autoplay || autoPlayGif;
+  }
+
   hoverToPlay () {
     const { attachment } = this.props;
-    return !autoPlayGif && attachment.get('type') === 'gifv';
+    return !this.getAutoPlay() && attachment.get('type') === 'gifv';
   }
 
   handleClick = (e) => {
@@ -222,7 +227,7 @@ class Item extends React.PureComponent {
         </a>
       );
     } else if (attachment.get('type') === 'gifv') {
-      const autoPlay = !isIOS() && autoPlayGif;
+      const autoPlay = !isIOS() && this.getAutoPlay();
 
       thumbnail = (
         <div className={classNames('media-gallery__gifv', { autoplay: autoPlay })}>
@@ -271,6 +276,7 @@ class MediaGallery extends React.PureComponent {
     defaultWidth: PropTypes.number,
     cacheWidth: PropTypes.func,
     visible: PropTypes.bool,
+    autoplay: PropTypes.bool,
     onToggleVisibility: PropTypes.func,
   };
 
@@ -328,7 +334,7 @@ class MediaGallery extends React.PureComponent {
   }
 
   render () {
-    const { media, intl, sensitive, letterbox, fullwidth, defaultWidth } = this.props;
+    const { media, 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');
@@ -350,9 +356,9 @@ class MediaGallery extends React.PureComponent {
     }
 
     if (this.isStandaloneEligible()) {
-      children = <Item standalone onClick={this.handleClick} attachment={media.get(0)} displayWidth={width} visible={visible} />;
+      children = <Item standalone autoplay={autoplay} onClick={this.handleClick} attachment={media.get(0)} displayWidth={width} visible={visible} />;
     } else {
-      children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} 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} size={size} letterbox={letterbox} displayWidth={width} visible={visible || uncached} />);
     }
 
     if (uncached) {
diff --git a/app/javascript/flavours/glitch/components/relative_timestamp.js b/app/javascript/flavours/glitch/components/relative_timestamp.js
index aa4b73cfe..711181dcd 100644
--- a/app/javascript/flavours/glitch/components/relative_timestamp.js
+++ b/app/javascript/flavours/glitch/components/relative_timestamp.js
@@ -3,6 +3,7 @@ import { injectIntl, defineMessages } from 'react-intl';
 import PropTypes from 'prop-types';
 
 const messages = defineMessages({
+  today: { id: 'relative_time.today', defaultMessage: 'today' },
   just_now: { id: 'relative_time.just_now', defaultMessage: 'now' },
   seconds: { id: 'relative_time.seconds', defaultMessage: '{number}s' },
   minutes: { id: 'relative_time.minutes', defaultMessage: '{number}m' },
@@ -65,12 +66,14 @@ const getUnitDelay = units => {
   }
 };
 
-export const timeAgoString = (intl, date, now, year) => {
+export const timeAgoString = (intl, date, now, year, timeGiven = true) => {
   const delta = now - date.getTime();
 
   let relativeTime;
 
-  if (delta < 10 * SECOND) {
+  if (delta < DAY && !timeGiven) {
+    relativeTime = intl.formatMessage(messages.today);
+  } else if (delta < 10 * SECOND) {
     relativeTime = intl.formatMessage(messages.just_now);
   } else if (delta < 7 * DAY) {
     if (delta < MINUTE) {
@@ -91,12 +94,14 @@ export const timeAgoString = (intl, date, now, year) => {
   return relativeTime;
 };
 
-const timeRemainingString = (intl, date, now) => {
+const timeRemainingString = (intl, date, now, timeGiven = true) => {
   const delta = date.getTime() - now;
 
   let relativeTime;
 
-  if (delta < 10 * SECOND) {
+  if (delta < DAY && !timeGiven) {
+    relativeTime = intl.formatMessage(messages.today);
+  } else if (delta < 10 * SECOND) {
     relativeTime = intl.formatMessage(messages.moments_remaining);
   } else if (delta < MINUTE) {
     relativeTime = intl.formatMessage(messages.seconds_remaining, { number: Math.floor(delta / SECOND) });
@@ -173,8 +178,9 @@ class RelativeTimestamp extends React.Component {
   render () {
     const { timestamp, intl, year, futureDate } = this.props;
 
+    const timeGiven    = timestamp.includes('T');
     const date         = new Date(timestamp);
-    const relativeTime = futureDate ? timeRemainingString(intl, date, this.state.now) : timeAgoString(intl, date, this.state.now, year);
+    const relativeTime = futureDate ? timeRemainingString(intl, date, this.state.now, timeGiven) : timeAgoString(intl, date, this.state.now, year, timeGiven);
 
     return (
       <time dateTime={timestamp} title={intl.formatDate(date, dateFormatOptions)}>
diff --git a/app/javascript/flavours/glitch/components/status_content.js b/app/javascript/flavours/glitch/components/status_content.js
index 2c79de4db..a5822866a 100644
--- a/app/javascript/flavours/glitch/components/status_content.js
+++ b/app/javascript/flavours/glitch/components/status_content.js
@@ -42,6 +42,14 @@ const isLinkMisleading = (link) => {
   const linkText = linkTextParts.join('');
   const targetURL = new URL(link.href);
 
+  if (targetURL.protocol === 'magnet:') {
+    return !linkText.startsWith('magnet:');
+  }
+
+  if (targetURL.protocol === 'xmpp:') {
+    return !(linkText === targetURL.href || 'xmpp:' + linkText === targetURL.href);
+  }
+
   // The following may not work with international domain names
   if (textMatchesTarget(linkText, targetURL.origin, targetURL.host) || textMatchesTarget(linkText.toLowerCase(), targetURL.origin, targetURL.host)) {
     return false;
@@ -120,9 +128,19 @@ export default class StatusContent extends React.PureComponent {
           if (tagLinks && isLinkMisleading(link)) {
             // Add a tag besides the link to display its origin
 
+            const url = new URL(link.href);
             const tag = document.createElement('span');
             tag.classList.add('link-origin-tag');
-            tag.textContent = `[${new URL(link.href).host}]`;
+            switch (url.protocol) {
+            case 'xmpp:':
+              tag.textContent = `[${url.href}]`;
+              break;
+            case 'magnet:':
+              tag.textContent = '(magnet)';
+              break;
+            default:
+              tag.textContent = `[${url.host}]`;
+            }
             link.insertAdjacentText('beforeend', ' ');
             link.insertAdjacentElement('beforeend', tag);
           }
diff --git a/app/javascript/flavours/glitch/features/account_timeline/index.js b/app/javascript/flavours/glitch/features/account_timeline/index.js
index 2ef4ff602..f25c82a00 100644
--- a/app/javascript/flavours/glitch/features/account_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/account_timeline/index.js
@@ -112,6 +112,7 @@ class AccountTimeline extends ImmutablePureComponent {
           onLoadMore={this.handleLoadMore}
           emptyMessage={<FormattedMessage id='empty_column.account_timeline' defaultMessage='No toots here!' />}
           bindToDocument={!multiColumn}
+          timelineId='account'
         />
       </Column>
     );
diff --git a/app/javascript/flavours/glitch/features/directory/components/account_card.js b/app/javascript/flavours/glitch/features/directory/components/account_card.js
index d1c406933..557120960 100644
--- a/app/javascript/flavours/glitch/features/directory/components/account_card.js
+++ b/app/javascript/flavours/glitch/features/directory/components/account_card.js
@@ -22,6 +22,7 @@ const messages = defineMessages({
   requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' },
   unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
   unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
+  unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' },
 });
 
 const makeMapStateToProps = () => {
diff --git a/app/javascript/flavours/glitch/features/emoji_picker/index.js b/app/javascript/flavours/glitch/features/emoji_picker/index.js
index 6e5518b0c..3717fcd82 100644
--- a/app/javascript/flavours/glitch/features/emoji_picker/index.js
+++ b/app/javascript/flavours/glitch/features/emoji_picker/index.js
@@ -372,6 +372,7 @@ class EmojiPickerDropdown extends React.PureComponent {
     onPickEmoji: PropTypes.func.isRequired,
     onSkinTone: PropTypes.func.isRequired,
     skinTone: PropTypes.number.isRequired,
+    button: PropTypes.node,
   };
 
   state = {
@@ -432,18 +433,18 @@ class EmojiPickerDropdown extends React.PureComponent {
   }
 
   render () {
-    const { intl, onPickEmoji, onSkinTone, skinTone, frequentlyUsedEmojis } = this.props;
+    const { intl, onPickEmoji, onSkinTone, skinTone, frequentlyUsedEmojis, button } = this.props;
     const title = intl.formatMessage(messages.emoji);
     const { active, loading, placement } = this.state;
 
     return (
       <div className='emoji-picker-dropdown' onKeyDown={this.handleKeyDown}>
         <div ref={this.setTargetRef} className='emoji-button' title={title} aria-label={title} aria-expanded={active} role='button' onClick={this.onToggle} onKeyDown={this.onToggle} tabIndex={0}>
-          <img
+          {button || <img
             className={classNames('emojione', { 'pulse-loading': active && loading })}
             alt='🙂'
             src={`${assetHost}/emoji/1f602.svg`}
-          />
+          />}
         </div>
 
         <Overlay show={active} placement={placement} target={this.findTarget}>
diff --git a/app/javascript/flavours/glitch/features/getting_started/components/announcements.js b/app/javascript/flavours/glitch/features/getting_started/components/announcements.js
new file mode 100644
index 000000000..e34c9009b
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/getting_started/components/announcements.js
@@ -0,0 +1,436 @@
+import React from 'react';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import ReactSwipeableViews from 'react-swipeable-views';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+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 } from 'flavours/glitch/util/initial_state';
+import elephantUIPlane from 'mastodon/../images/elephant_ui_plane.svg';
+import { mascot } from 'flavours/glitch/util/initial_state';
+import unicodeMapping from 'flavours/glitch/util/emoji/emoji_unicode_mapping_light';
+import classNames from 'classnames';
+import EmojiPickerDropdown from 'flavours/glitch/features/emoji_picker';
+import AnimatedNumber from 'flavours/glitch/components/animated_number';
+import TransitionMotion from 'react-motion/lib/TransitionMotion';
+import spring from 'react-motion/lib/spring';
+
+const messages = defineMessages({
+  close: { id: 'lightbox.close', defaultMessage: 'Close' },
+  previous: { id: 'lightbox.previous', defaultMessage: 'Previous' },
+  next: { id: 'lightbox.next', defaultMessage: 'Next' },
+});
+
+class Content extends ImmutablePureComponent {
+
+  static contextTypes = {
+    router: PropTypes.object,
+  };
+
+  static propTypes = {
+    announcement: ImmutablePropTypes.map.isRequired,
+  };
+
+  setRef = c => {
+    this.node = c;
+  }
+
+  componentDidMount () {
+    this._updateLinks();
+    this._updateEmojis();
+  }
+
+  componentDidUpdate () {
+    this._updateLinks();
+    this._updateEmojis();
+  }
+
+  _updateEmojis () {
+    const node = this.node;
+
+    if (!node || autoPlayGif) {
+      return;
+    }
+
+    const emojis = node.querySelectorAll('.custom-emoji');
+
+    for (var i = 0; i < emojis.length; i++) {
+      let emoji = emojis[i];
+
+      if (emoji.classList.contains('status-emoji')) {
+        continue;
+      }
+
+      emoji.classList.add('status-emoji');
+
+      emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false);
+      emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false);
+    }
+  }
+
+  _updateLinks () {
+    const node = this.node;
+
+    if (!node) {
+      return;
+    }
+
+    const links = node.querySelectorAll('a');
+
+    for (var i = 0; i < links.length; ++i) {
+      let link = links[i];
+
+      if (link.classList.contains('status-link')) {
+        continue;
+      }
+
+      link.classList.add('status-link');
+
+      let mention = this.props.announcement.get('mentions').find(item => link.href === item.get('url'));
+
+      if (mention) {
+        link.addEventListener('click', this.onMentionClick.bind(this, mention), false);
+        link.setAttribute('title', mention.get('acct'));
+      } else if (link.textContent[0] === '#' || (link.previousSibling && link.previousSibling.textContent && link.previousSibling.textContent[link.previousSibling.textContent.length - 1] === '#')) {
+        link.addEventListener('click', this.onHashtagClick.bind(this, link.text), false);
+      } else {
+        link.setAttribute('title', link.href);
+        link.classList.add('unhandled-link');
+      }
+
+      link.setAttribute('target', '_blank');
+      link.setAttribute('rel', 'noopener noreferrer');
+    }
+  }
+
+  onMentionClick = (mention, e) => {
+    if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
+      e.preventDefault();
+      this.context.router.history.push(`/accounts/${mention.get('id')}`);
+    }
+  }
+
+  onHashtagClick = (hashtag, e) => {
+    hashtag = hashtag.replace(/^#/, '');
+
+    if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
+      e.preventDefault();
+      this.context.router.history.push(`/timelines/tag/${hashtag}`);
+    }
+  }
+
+  handleEmojiMouseEnter = ({ target }) => {
+    target.src = target.getAttribute('data-original');
+  }
+
+  handleEmojiMouseLeave = ({ target }) => {
+    target.src = target.getAttribute('data-static');
+  }
+
+  render () {
+    const { announcement } = this.props;
+
+    return (
+      <div
+        className='announcements__item__content'
+        ref={this.setRef}
+        dangerouslySetInnerHTML={{ __html: announcement.get('contentHtml') }}
+      />
+    );
+  }
+
+}
+
+const assetHost = process.env.CDN_HOST || '';
+
+class Emoji extends React.PureComponent {
+
+  static propTypes = {
+    emoji: PropTypes.string.isRequired,
+    emojiMap: ImmutablePropTypes.map.isRequired,
+    hovered: PropTypes.bool.isRequired,
+  };
+
+  render () {
+    const { emoji, emojiMap, hovered } = this.props;
+
+    if (unicodeMapping[emoji]) {
+      const { filename, shortCode } = unicodeMapping[this.props.emoji];
+      const title = shortCode ? `:${shortCode}:` : '';
+
+      return (
+        <img
+          draggable='false'
+          className='emojione'
+          alt={emoji}
+          title={title}
+          src={`${assetHost}/emoji/${filename}.svg`}
+        />
+      );
+    } else if (emojiMap.get(emoji)) {
+      const filename  = (autoPlayGif || hovered) ? emojiMap.getIn([emoji, 'url']) : emojiMap.getIn([emoji, 'static_url']);
+      const shortCode = `:${emoji}:`;
+
+      return (
+        <img
+          draggable='false'
+          className='emojione custom-emoji'
+          alt={shortCode}
+          title={shortCode}
+          src={filename}
+        />
+      );
+    } else {
+      return null;
+    }
+  }
+
+}
+
+class Reaction extends ImmutablePureComponent {
+
+  static propTypes = {
+    announcementId: PropTypes.string.isRequired,
+    reaction: ImmutablePropTypes.map.isRequired,
+    addReaction: PropTypes.func.isRequired,
+    removeReaction: PropTypes.func.isRequired,
+    emojiMap: ImmutablePropTypes.map.isRequired,
+    style: PropTypes.object,
+  };
+
+  state = {
+    hovered: false,
+  };
+
+  handleClick = () => {
+    const { reaction, announcementId, addReaction, removeReaction } = this.props;
+
+    if (reaction.get('me')) {
+      removeReaction(announcementId, reaction.get('name'));
+    } else {
+      addReaction(announcementId, reaction.get('name'));
+    }
+  }
+
+  handleMouseEnter = () => this.setState({ hovered: true })
+
+  handleMouseLeave = () => this.setState({ hovered: false })
+
+  render () {
+    const { reaction } = this.props;
+
+    let shortCode = reaction.get('name');
+
+    if (unicodeMapping[shortCode]) {
+      shortCode = unicodeMapping[shortCode].shortCode;
+    }
+
+    return (
+      <button className={classNames('reactions-bar__item', { active: reaction.get('me') })} onClick={this.handleClick} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} title={`:${shortCode}:`} style={this.props.style}>
+        <span className='reactions-bar__item__emoji'><Emoji hovered={this.state.hovered} emoji={reaction.get('name')} emojiMap={this.props.emojiMap} /></span>
+        <span className='reactions-bar__item__count'><AnimatedNumber value={reaction.get('count')} /></span>
+      </button>
+    );
+  }
+
+}
+
+class ReactionsBar extends ImmutablePureComponent {
+
+  static propTypes = {
+    announcementId: PropTypes.string.isRequired,
+    reactions: ImmutablePropTypes.list.isRequired,
+    addReaction: PropTypes.func.isRequired,
+    removeReaction: PropTypes.func.isRequired,
+    emojiMap: ImmutablePropTypes.map.isRequired,
+  };
+
+  handleEmojiPick = data => {
+    const { addReaction, announcementId } = this.props;
+    addReaction(announcementId, data.native.replace(/:/g, ''));
+  }
+
+  willEnter () {
+    return { scale: reduceMotion ? 1 : 0 };
+  }
+
+  willLeave () {
+    return { scale: reduceMotion ? 0 : spring(0, { stiffness: 170, damping: 26 }) };
+  }
+
+  render () {
+    const { reactions } = this.props;
+    const visibleReactions = reactions.filter(x => x.get('count') > 0);
+
+    const styles = visibleReactions.map(reaction => ({
+      key: reaction.get('name'),
+      data: reaction,
+      style: { scale: reduceMotion ? 1 : spring(1, { stiffness: 150, damping: 13 }) },
+    })).toArray();
+
+    return (
+      <TransitionMotion styles={styles} willEnter={this.willEnter} willLeave={this.willLeave}>
+        {items => (
+          <div className={classNames('reactions-bar', { 'reactions-bar--empty': visibleReactions.isEmpty() })}>
+            {items.map(({ key, data, style }) => (
+              <Reaction
+                key={key}
+                reaction={data}
+                style={{ transform: `scale(${style.scale})`, position: style.scale < 0.5 ? 'absolute' : 'static' }}
+                announcementId={this.props.announcementId}
+                addReaction={this.props.addReaction}
+                removeReaction={this.props.removeReaction}
+                emojiMap={this.props.emojiMap}
+              />
+            ))}
+
+            {visibleReactions.size < 8 && <EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} button={<Icon id='plus' />} />}
+          </div>
+        )}
+      </TransitionMotion>
+    );
+  }
+
+}
+
+class Announcement extends ImmutablePureComponent {
+
+  static propTypes = {
+    announcement: ImmutablePropTypes.map.isRequired,
+    emojiMap: ImmutablePropTypes.map.isRequired,
+    addReaction: PropTypes.func.isRequired,
+    removeReaction: PropTypes.func.isRequired,
+    intl: PropTypes.object.isRequired,
+    selected: PropTypes.bool,
+  };
+
+  state = {
+    unread: !this.props.announcement.get('read'),
+  };
+
+  componentDidUpdate () {
+    const { selected, announcement } = this.props;
+    if (!selected && this.state.unread !== !announcement.get('read')) {
+      this.setState({ unread: !announcement.get('read') });
+    }
+  }
+
+  render () {
+    const { announcement } = this.props;
+    const { unread } = this.state;
+    const startsAt = announcement.get('starts_at') && new Date(announcement.get('starts_at'));
+    const endsAt = announcement.get('ends_at') && new Date(announcement.get('ends_at'));
+    const now = new Date();
+    const hasTimeRange = startsAt && endsAt;
+    const skipYear = hasTimeRange && startsAt.getFullYear() === endsAt.getFullYear() && endsAt.getFullYear() === now.getFullYear();
+    const skipEndDate = hasTimeRange && startsAt.getDate() === endsAt.getDate() && startsAt.getMonth() === endsAt.getMonth() && startsAt.getFullYear() === endsAt.getFullYear();
+    const skipTime = announcement.get('all_day');
+
+    return (
+      <div className='announcements__item'>
+        <strong className='announcements__item__range'>
+          <FormattedMessage id='announcement.announcement' defaultMessage='Announcement' />
+          {hasTimeRange && <span> · <FormattedDate value={startsAt} hour12={false} year={(skipYear || startsAt.getFullYear() === now.getFullYear()) ? undefined : 'numeric'} month='short' day='2-digit' hour={skipTime ? undefined : '2-digit'} minute={skipTime ? undefined : '2-digit'} /> - <FormattedDate value={endsAt} hour12={false} year={(skipYear || endsAt.getFullYear() === now.getFullYear()) ? undefined : 'numeric'} month={skipEndDate ? undefined : 'short'} day={skipEndDate ? undefined : '2-digit'} hour={skipTime ? undefined : '2-digit'} minute={skipTime ? undefined : '2-digit'} /></span>}
+        </strong>
+
+        <Content announcement={announcement} />
+
+        <ReactionsBar
+          reactions={announcement.get('reactions')}
+          announcementId={announcement.get('id')}
+          addReaction={this.props.addReaction}
+          removeReaction={this.props.removeReaction}
+          emojiMap={this.props.emojiMap}
+        />
+
+        {unread && <span className='announcements__item__unread' />}
+      </div>
+    );
+  }
+
+}
+
+export default @injectIntl
+class Announcements extends ImmutablePureComponent {
+
+  static propTypes = {
+    announcements: ImmutablePropTypes.list,
+    emojiMap: ImmutablePropTypes.map.isRequired,
+    dismissAnnouncement: PropTypes.func.isRequired,
+    addReaction: PropTypes.func.isRequired,
+    removeReaction: PropTypes.func.isRequired,
+    intl: PropTypes.object.isRequired,
+  };
+
+  state = {
+    index: 0,
+  };
+
+  componentDidMount () {
+    this._markAnnouncementAsRead();
+  }
+
+  componentDidUpdate () {
+    this._markAnnouncementAsRead();
+  }
+
+  _markAnnouncementAsRead () {
+    const { dismissAnnouncement, announcements } = this.props;
+    const { index } = this.state;
+    const announcement = announcements.get(index);
+    if (!announcement.get('read')) dismissAnnouncement(announcement.get('id'));
+  }
+
+  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;
+    const { index } = this.state;
+
+    if (announcements.isEmpty()) {
+      return null;
+    }
+
+    return (
+      <div className='announcements'>
+        <img className='announcements__mastodon' alt='' draggable='false' src={mascot || elephantUIPlane} />
+
+        <div className='announcements__container'>
+          <ReactSwipeableViews animateHeight={!reduceMotion} adjustHeight={reduceMotion} index={index} onChangeIndex={this.handleChangeIndex}>
+            {announcements.map((announcement, idx) => (
+              <Announcement
+                key={announcement.get('id')}
+                announcement={announcement}
+                emojiMap={this.props.emojiMap}
+                addReaction={this.props.addReaction}
+                removeReaction={this.props.removeReaction}
+                intl={intl}
+                selected={index === idx}
+              />
+            ))}
+          </ReactSwipeableViews>
+
+          {announcements.size > 1 && (
+            <div className='announcements__pagination'>
+              <IconButton disabled={announcements.size === 1} title={intl.formatMessage(messages.previous)} icon='chevron-left' onClick={this.handlePrevClick} size={13} />
+              <span>{index + 1} / {announcements.size}</span>
+              <IconButton disabled={announcements.size === 1} title={intl.formatMessage(messages.next)} icon='chevron-right' onClick={this.handleNextClick} size={13} />
+            </div>
+          )}
+        </div>
+      </div>
+    );
+  }
+
+}
diff --git a/app/javascript/flavours/glitch/features/getting_started/containers/announcements_container.js b/app/javascript/flavours/glitch/features/getting_started/containers/announcements_container.js
new file mode 100644
index 000000000..d472323f8
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/getting_started/containers/announcements_container.js
@@ -0,0 +1,20 @@
+import { connect } from 'react-redux';
+import { addReaction, removeReaction, dismissAnnouncement } from 'flavours/glitch/actions/announcements';
+import Announcements from '../components/announcements';
+import { createSelector } from 'reselect';
+import { Map as ImmutableMap } from 'immutable';
+
+const customEmojiMap = createSelector([state => state.get('custom_emojis')], items => items.reduce((map, emoji) => map.set(emoji.get('shortcode'), emoji), ImmutableMap()));
+
+const mapStateToProps = state => ({
+  announcements: state.getIn(['announcements', 'items']),
+  emojiMap: customEmojiMap(state),
+});
+
+const mapDispatchToProps = dispatch => ({
+  dismissAnnouncement: id => dispatch(dismissAnnouncement(id)),
+  addReaction: (id, name) => dispatch(addReaction(id, name)),
+  removeReaction: (id, name) => dispatch(removeReaction(id, name)),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(Announcements);
diff --git a/app/javascript/flavours/glitch/features/getting_started/containers/trends_container.js b/app/javascript/flavours/glitch/features/getting_started/containers/trends_container.js
index 1df3fb4fe..7a5268780 100644
--- a/app/javascript/flavours/glitch/features/getting_started/containers/trends_container.js
+++ b/app/javascript/flavours/glitch/features/getting_started/containers/trends_container.js
@@ -1,5 +1,5 @@
 import { connect } from 'react-redux';
-import { fetchTrends } from '../../../actions/trends';
+import { fetchTrends } from 'mastodon/actions/trends';
 import Trends from '../components/trends';
 
 const mapStateToProps = state => ({
diff --git a/app/javascript/flavours/glitch/features/home_timeline/index.js b/app/javascript/flavours/glitch/features/home_timeline/index.js
index 9b71a4404..cc8e4664c 100644
--- a/app/javascript/flavours/glitch/features/home_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/home_timeline/index.js
@@ -9,14 +9,23 @@ import { addColumn, removeColumn, moveColumn } from 'flavours/glitch/actions/col
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import ColumnSettingsContainer from './containers/column_settings_container';
 import { Link } from 'react-router-dom';
+import { fetchAnnouncements, toggleShowAnnouncements } from 'flavours/glitch/actions/announcements';
+import AnnouncementsContainer from 'flavours/glitch/features/getting_started/containers/announcements_container';
+import classNames from 'classnames';
+import IconWithBadge from 'flavours/glitch/components/icon_with_badge';
 
 const messages = defineMessages({
   title: { id: 'column.home', defaultMessage: 'Home' },
+  show_announcements: { id: 'home.show_announcements', defaultMessage: 'Show announcements' },
+  hide_announcements: { id: 'home.hide_announcements', defaultMessage: 'Hide announcements' },
 });
 
 const mapStateToProps = state => ({
   hasUnread: state.getIn(['timelines', 'home', 'unread']) > 0,
   isPartial: state.getIn(['timelines', 'home', 'isPartial']),
+  hasAnnouncements: !state.getIn(['announcements', 'items']).isEmpty(),
+  unreadAnnouncements: state.getIn(['announcements', 'items']).count(item => !item.get('read')),
+  showAnnouncements: state.getIn(['announcements', 'show']),
 });
 
 export default @connect(mapStateToProps)
@@ -30,6 +39,9 @@ class HomeTimeline extends React.PureComponent {
     isPartial: PropTypes.bool,
     columnId: PropTypes.string,
     multiColumn: PropTypes.bool,
+    hasAnnouncements: PropTypes.bool,
+    unreadAnnouncements: PropTypes.number,
+    showAnnouncements: PropTypes.bool,
   };
 
   handlePin = () => {
@@ -60,6 +72,7 @@ class HomeTimeline extends React.PureComponent {
   }
 
   componentDidMount () {
+    this.props.dispatch(fetchAnnouncements());
     this._checkIfReloadNeeded(false, this.props.isPartial);
   }
 
@@ -92,10 +105,31 @@ class HomeTimeline extends React.PureComponent {
     }
   }
 
+  handleToggleAnnouncementsClick = (e) => {
+    e.stopPropagation();
+    this.props.dispatch(toggleShowAnnouncements());
+  }
+
   render () {
-    const { intl, hasUnread, columnId, multiColumn } = this.props;
+    const { intl, hasUnread, columnId, multiColumn, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props;
     const pinned = !!columnId;
 
+    let announcementsButton = null;
+
+    if (hasAnnouncements) {
+      announcementsButton = (
+        <button
+          className={classNames('column-header__button', { 'active': showAnnouncements })}
+          title={intl.formatMessage(showAnnouncements ? messages.hide_announcements : messages.show_announcements)}
+          aria-label={intl.formatMessage(showAnnouncements ? messages.hide_announcements : messages.show_announcements)}
+          aria-pressed={showAnnouncements ? 'true' : 'false'}
+          onClick={this.handleToggleAnnouncementsClick}
+        >
+          <IconWithBadge id='bullhorn' count={unreadAnnouncements} />
+        </button>
+      );
+    }
+
     return (
       <Column bindToDocument={!multiColumn} ref={this.setRef} name='home' label={intl.formatMessage(messages.title)}>
         <ColumnHeader
@@ -107,6 +141,8 @@ class HomeTimeline extends React.PureComponent {
           onClick={this.handleHeaderClick}
           pinned={pinned}
           multiColumn={multiColumn}
+          extraButton={announcementsButton}
+          appendContent={hasAnnouncements && showAnnouncements && <AnnouncementsContainer />}
         >
           <ColumnSettingsContainer />
         </ColumnHeader>
diff --git a/app/javascript/flavours/glitch/features/local_settings/page/index.js b/app/javascript/flavours/glitch/features/local_settings/page/index.js
index e08c12c76..0b3428027 100644
--- a/app/javascript/flavours/glitch/features/local_settings/page/index.js
+++ b/app/javascript/flavours/glitch/features/local_settings/page/index.js
@@ -168,6 +168,14 @@ class LocalSettingsPage extends React.PureComponent {
         </LocalSettingsPageItem>
         <LocalSettingsPageItem
           settings={settings}
+          item={['prepend_cw_re']}
+          id='mastodon-settings--prepend_cw_re'
+          onChange={onChange}
+        >
+          <FormattedMessage id='settings.prepend_cw_re' defaultMessage='Prepend “re: ” to content warnings when replying' />
+        </LocalSettingsPageItem>
+        <LocalSettingsPageItem
+          settings={settings}
           item={['preselect_on_reply']}
           id='mastodon-settings--preselect_on_reply'
           onChange={onChange}
diff --git a/app/javascript/flavours/glitch/features/notifications/index.js b/app/javascript/flavours/glitch/features/notifications/index.js
index 7f06d70c5..26710feff 100644
--- a/app/javascript/flavours/glitch/features/notifications/index.js
+++ b/app/javascript/flavours/glitch/features/notifications/index.js
@@ -1,5 +1,6 @@
 import React from 'react';
 import { connect } from 'react-redux';
+import classNames from 'classnames';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import Column from 'flavours/glitch/components/column';
@@ -22,9 +23,13 @@ import { List as ImmutableList } from 'immutable';
 import { debounce } from 'lodash';
 import ScrollableList from 'flavours/glitch/components/scrollable_list';
 import LoadGap from 'flavours/glitch/components/load_gap';
+import Icon from 'flavours/glitch/components/icon';
+
+import NotificationPurgeButtonsContainer from 'flavours/glitch/containers/notification_purge_buttons_container';
 
 const messages = defineMessages({
   title: { id: 'column.notifications', defaultMessage: 'Notifications' },
+  enterNotifCleaning : { id: 'notification_purge.start', defaultMessage: 'Enter notification cleaning mode' },
 });
 
 const getNotifications = createSelector([
@@ -94,6 +99,10 @@ class Notifications extends React.PureComponent {
     trackScroll: true,
   };
 
+  state = {
+    animatingNCD: false,
+  };
+
   handleLoadGap = (maxId) => {
     this.props.dispatch(expandNotifications({ maxId }));
   };
@@ -176,8 +185,19 @@ class Notifications extends React.PureComponent {
     }
   }
 
+  handleTransitionEndNCD = () => {
+    this.setState({ animatingNCD: false });
+  }
+
+  onEnterCleaningMode = () => {
+    this.setState({ animatingNCD: true });
+    this.props.onEnterCleaningMode(!this.props.notifCleaningActive);
+  }
+
   render () {
     const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, showFilterBar } = this.props;
+    const { notifCleaning, notifCleaningActive } = this.props;
+    const { animatingNCD } = this.state;
     const pinned = !!columnId;
     const emptyMessage = <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. Interact with others to start the conversation." />;
 
@@ -232,6 +252,36 @@ class Notifications extends React.PureComponent {
       </ScrollableList>
     );
 
+    const notifCleaningButtonClassName = classNames('column-header__button', {
+      'active': notifCleaningActive,
+    });
+
+    const notifCleaningDrawerClassName = classNames('ncd column-header__collapsible', {
+      'collapsed': !notifCleaningActive,
+      'animating': animatingNCD,
+    });
+
+    const msgEnterNotifCleaning = intl.formatMessage(messages.enterNotifCleaning);
+
+    const notifCleaningButton = (
+      <button
+        aria-label={msgEnterNotifCleaning}
+        title={msgEnterNotifCleaning}
+        onClick={this.onEnterCleaningMode}
+        className={notifCleaningButtonClassName}
+      >
+        <Icon id='eraser' />
+      </button>
+    );
+
+    const notifCleaningDrawer = (
+      <div className={notifCleaningDrawerClassName} onTransitionEnd={this.handleTransitionEndNCD}>
+        <div className='column-header__collapsible-inner nopad-drawer'>
+          {(notifCleaningActive || animatingNCD) ? (<NotificationPurgeButtonsContainer />) : null }
+        </div>
+      </div>
+    );
+
     return (
       <Column
         bindToDocument={!multiColumn}
@@ -250,9 +300,8 @@ class Notifications extends React.PureComponent {
           pinned={pinned}
           multiColumn={multiColumn}
           localSettings={this.props.localSettings}
-          notifCleaning
-          notifCleaningActive={this.props.notifCleaningActive} // this is used to toggle the header text
-          onEnterCleaningMode={this.props.onEnterCleaningMode}
+          extraButton={notifCleaningButton}
+          appendContent={notifCleaningDrawer}
         >
           <ColumnSettingsContainer />
         </ColumnHeader>
diff --git a/app/javascript/flavours/glitch/features/status/components/action_bar.js b/app/javascript/flavours/glitch/features/status/components/action_bar.js
index c48bfaccd..d71a3ae08 100644
--- a/app/javascript/flavours/glitch/features/status/components/action_bar.js
+++ b/app/javascript/flavours/glitch/features/status/components/action_bar.js
@@ -18,6 +18,7 @@ const messages = defineMessages({
   cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
   favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
   bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
+  more: { id: 'status.more', defaultMessage: 'More' },
   mute: { id: 'status.mute', defaultMessage: 'Mute @{name}' },
   muteConversation: { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' },
   unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
@@ -207,7 +208,7 @@ class ActionBar extends React.PureComponent {
         <div className='detailed-status__button'><IconButton className='bookmark-icon' active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} /></div>
 
         <div className='detailed-status__action-bar-dropdown'>
-          <DropdownMenuContainer size={18} icon='ellipsis-h' items={menu} direction='left' ariaLabel='More' />
+          <DropdownMenuContainer size={18} icon='ellipsis-h' items={menu} direction='left' title={intl.formatMessage(messages.more)} />
         </div>
       </div>
     );
diff --git a/app/javascript/flavours/glitch/features/status/components/detailed_status.js b/app/javascript/flavours/glitch/features/status/components/detailed_status.js
index 898011c88..c4ac8f0a6 100644
--- a/app/javascript/flavours/glitch/features/status/components/detailed_status.js
+++ b/app/javascript/flavours/glitch/features/status/components/detailed_status.js
@@ -7,7 +7,7 @@ import StatusContent from 'flavours/glitch/components/status_content';
 import MediaGallery from 'flavours/glitch/components/media_gallery';
 import AttachmentList from 'flavours/glitch/components/attachment_list';
 import { Link } from 'react-router-dom';
-import { FormattedDate, FormattedNumber } from 'react-intl';
+import { FormattedDate } from 'react-intl';
 import Card from './card';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import Video from 'flavours/glitch/features/video';
@@ -17,6 +17,7 @@ import scheduleIdleTask from 'flavours/glitch/util/schedule_idle_task';
 import classNames from 'classnames';
 import PollContainer from 'flavours/glitch/containers/poll_container';
 import Icon from 'flavours/glitch/components/icon';
+import AnimatedNumber from 'flavours/glitch/components/animated_number';
 
 export default class DetailedStatus extends ImmutablePureComponent {
 
@@ -204,7 +205,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
         <Link to={`/statuses/${status.get('id')}/reblogs`} className='detailed-status__link'>
           <Icon id={reblogIcon} />
           <span className='detailed-status__reblogs'>
-            <FormattedNumber value={status.get('reblogs_count')} />
+            <AnimatedNumber value={status.get('reblogs_count')} />
           </span>
         </Link>
       );
@@ -213,7 +214,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
         <a href={`/interact/${status.get('id')}?type=reblog`} className='detailed-status__link' onClick={this.handleModalLink}>
           <Icon id={reblogIcon} />
           <span className='detailed-status__reblogs'>
-            <FormattedNumber value={status.get('reblogs_count')} />
+            <AnimatedNumber value={status.get('reblogs_count')} />
           </span>
         </a>
       );
@@ -224,7 +225,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
         <Link to={`/statuses/${status.get('id')}/favourites`} className='detailed-status__link'>
           <Icon id='star' />
           <span className='detailed-status__favorites'>
-            <FormattedNumber value={status.get('favourites_count')} />
+            <AnimatedNumber value={status.get('favourites_count')} />
           </span>
         </Link>
       );
@@ -233,7 +234,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
         <a href={`/interact/${status.get('id')}?type=favourite`} className='detailed-status__link' onClick={this.handleModalLink}>
           <Icon id='star' />
           <span className='detailed-status__favorites'>
-            <FormattedNumber value={status.get('favourites_count')} />
+            <AnimatedNumber value={status.get('favourites_count')} />
           </span>
         </a>
       );
diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js
index 322f92477..411d2a88d 100644
--- a/app/javascript/flavours/glitch/features/status/index.js
+++ b/app/javascript/flavours/glitch/features/status/index.js
@@ -568,6 +568,7 @@ class Status extends ImmutablePureComponent {
             <HotKeys handlers={handlers}>
               <div className='focusable' tabIndex='0' aria-label={textForScreenReader(intl, status, false, !status.get('hidden'))}>
                 <DetailedStatus
+                  key={`details-${status.get('id')}`}
                   status={status}
                   settings={settings}
                   onOpenVideo={this.handleOpenVideo}
@@ -580,6 +581,7 @@ class Status extends ImmutablePureComponent {
                 />
 
                 <ActionBar
+                  key={`action-bar-${status.get('id')}`}
                   status={status}
                   onReply={this.handleReplyClick}
                   onFavourite={this.handleFavouriteClick}
diff --git a/app/javascript/flavours/glitch/features/ui/components/media_modal.js b/app/javascript/flavours/glitch/features/ui/components/media_modal.js
index c7d6c374c..23e8dac7e 100644
--- a/app/javascript/flavours/glitch/features/ui/components/media_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/media_modal.js
@@ -191,7 +191,6 @@ class MediaModal extends ImmutablePureComponent {
             style={swipeableViewsStyle}
             containerStyle={containerStyle}
             onChangeIndex={this.handleSwipe}
-            onSwitching={this.handleSwitching}
             index={index}
           >
             {content}
diff --git a/app/javascript/flavours/glitch/features/ui/index.js b/app/javascript/flavours/glitch/features/ui/index.js
index 5c861fdee..9f9ef561a 100644
--- a/app/javascript/flavours/glitch/features/ui/index.js
+++ b/app/javascript/flavours/glitch/features/ui/index.js
@@ -12,7 +12,7 @@ import { expandHomeTimeline } from 'flavours/glitch/actions/timelines';
 import { expandNotifications, notificationsSetVisibility } from 'flavours/glitch/actions/notifications';
 import { fetchFilters } from 'flavours/glitch/actions/filters';
 import { clearHeight } from 'flavours/glitch/actions/height_cache';
-import { submitMarkers } from 'flavours/glitch/actions/markers';
+import { submitMarkers, fetchMarkers } from 'flavours/glitch/actions/markers';
 import { WrappedSwitch, WrappedRoute } from 'flavours/glitch/util/react_router_helpers';
 import UploadArea from './components/upload_area';
 import PermaLink from 'flavours/glitch/components/permalink';
@@ -388,6 +388,7 @@ class UI extends React.Component {
 
     this.favicon = new Favico({ animation:"none" });
 
+    this.props.dispatch(fetchMarkers());
     this.props.dispatch(expandHomeTimeline());
     this.props.dispatch(expandNotifications());
     setTimeout(() => this.props.dispatch(fetchFilters()), 500);
diff --git a/app/javascript/flavours/glitch/locales/ast.js b/app/javascript/flavours/glitch/locales/ast.js
new file mode 100644
index 000000000..41355c24c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ast.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/ast.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/bn.js b/app/javascript/flavours/glitch/locales/bn.js
new file mode 100644
index 000000000..a453498b3
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/bn.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/bn.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/br.js b/app/javascript/flavours/glitch/locales/br.js
new file mode 100644
index 000000000..966bd1b2f
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/br.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/br.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/co.js b/app/javascript/flavours/glitch/locales/co.js
new file mode 100644
index 000000000..6e9e46797
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/co.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/co.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/cs.js b/app/javascript/flavours/glitch/locales/cs.js
new file mode 100644
index 000000000..ac7db0327
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/cs.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/cs.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/cy.js b/app/javascript/flavours/glitch/locales/cy.js
new file mode 100644
index 000000000..09412bd72
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/cy.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/cy.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/da.js b/app/javascript/flavours/glitch/locales/da.js
new file mode 100644
index 000000000..2b08806be
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/da.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/da.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/el.js b/app/javascript/flavours/glitch/locales/el.js
new file mode 100644
index 000000000..2d9bb829f
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/el.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/el.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/es-AR.js b/app/javascript/flavours/glitch/locales/es-AR.js
new file mode 100644
index 000000000..0dffabcd4
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/es-AR.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/es-AR.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/et.js b/app/javascript/flavours/glitch/locales/et.js
new file mode 100644
index 000000000..e3ea6b2a9
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/et.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/et.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/eu.js b/app/javascript/flavours/glitch/locales/eu.js
new file mode 100644
index 000000000..946410b67
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/eu.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/eu.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ga.js b/app/javascript/flavours/glitch/locales/ga.js
new file mode 100644
index 000000000..af2846ff8
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ga.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/ga.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/gl.js b/app/javascript/flavours/glitch/locales/gl.js
new file mode 100644
index 000000000..6a9140b1a
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/gl.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/gl.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/hi.js b/app/javascript/flavours/glitch/locales/hi.js
new file mode 100644
index 000000000..1a569495f
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/hi.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/hi.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/hy.js b/app/javascript/flavours/glitch/locales/hy.js
new file mode 100644
index 000000000..96f6a4d19
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/hy.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/hy.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/is.js b/app/javascript/flavours/glitch/locales/is.js
new file mode 100644
index 000000000..b05a08ad0
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/is.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/is.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ka.js b/app/javascript/flavours/glitch/locales/ka.js
new file mode 100644
index 000000000..3e06f4282
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ka.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/ka.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/kab.js b/app/javascript/flavours/glitch/locales/kab.js
new file mode 100644
index 000000000..5ed1156ef
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/kab.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/kab.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/kk.js b/app/javascript/flavours/glitch/locales/kk.js
new file mode 100644
index 000000000..8d00fb035
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/kk.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/kk.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/kn.js b/app/javascript/flavours/glitch/locales/kn.js
new file mode 100644
index 000000000..1c50e3628
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/kn.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/kn.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/lt.js b/app/javascript/flavours/glitch/locales/lt.js
new file mode 100644
index 000000000..47453aeeb
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/lt.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/lt.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/lv.js b/app/javascript/flavours/glitch/locales/lv.js
new file mode 100644
index 000000000..cdbcdf799
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/lv.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/lv.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/mk.js b/app/javascript/flavours/glitch/locales/mk.js
new file mode 100644
index 000000000..55e510b59
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/mk.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/mk.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ml.js b/app/javascript/flavours/glitch/locales/ml.js
new file mode 100644
index 000000000..d00331a1a
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ml.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/ml.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/mr.js b/app/javascript/flavours/glitch/locales/mr.js
new file mode 100644
index 000000000..fb3cde92a
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/mr.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/mr.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ms.js b/app/javascript/flavours/glitch/locales/ms.js
new file mode 100644
index 000000000..61033c521
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ms.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/ms.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/nn.js b/app/javascript/flavours/glitch/locales/nn.js
new file mode 100644
index 000000000..4c42368cb
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/nn.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/nn.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ro.js b/app/javascript/flavours/glitch/locales/ro.js
new file mode 100644
index 000000000..a16446c6a
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ro.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/ro.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/sk.js b/app/javascript/flavours/glitch/locales/sk.js
new file mode 100644
index 000000000..5fba6ab97
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/sk.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/sk.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/sl.js b/app/javascript/flavours/glitch/locales/sl.js
new file mode 100644
index 000000000..c53c1bae8
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/sl.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/sl.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/sq.js b/app/javascript/flavours/glitch/locales/sq.js
new file mode 100644
index 000000000..2fb7a2973
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/sq.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/sq.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/sr-Latn.js b/app/javascript/flavours/glitch/locales/sr-Latn.js
new file mode 100644
index 000000000..b42d5eaaf
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/sr-Latn.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/sr-Latn.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/sr.js b/app/javascript/flavours/glitch/locales/sr.js
new file mode 100644
index 000000000..8793d8d1e
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/sr.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/sr.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ta.js b/app/javascript/flavours/glitch/locales/ta.js
new file mode 100644
index 000000000..d6ecdcb1b
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ta.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/ta.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/te.js b/app/javascript/flavours/glitch/locales/te.js
new file mode 100644
index 000000000..afd6e4f7b
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/te.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/te.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ur.js b/app/javascript/flavours/glitch/locales/ur.js
new file mode 100644
index 000000000..97ba291b0
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ur.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/ur.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/vi.js b/app/javascript/flavours/glitch/locales/vi.js
new file mode 100644
index 000000000..499a96727
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/vi.js
@@ -0,0 +1,7 @@
+import inherited from 'mastodon/locales/vi.json';
+
+const messages = {
+  //  No translations available.
+};
+
+export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/packs/public.js b/app/javascript/flavours/glitch/packs/public.js
index 973d6ee46..d1adfb17a 100644
--- a/app/javascript/flavours/glitch/packs/public.js
+++ b/app/javascript/flavours/glitch/packs/public.js
@@ -97,15 +97,6 @@ function main() {
 
     delegate(document, '.custom-emoji', 'mouseover', getEmojiAnimationHandler('data-original'));
     delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static'));
-
-    delegate(document, '.blocks-table button.icon-button', 'click', function(e) {
-      e.preventDefault();
-
-      const classList = this.firstElementChild.classList;
-      classList.toggle('fa-chevron-down');
-      classList.toggle('fa-chevron-up');
-      this.parentElement.parentElement.nextElementSibling.classList.toggle('hidden');
-    });
   });
 
   delegate(document, '.sidebar__toggle__icon', 'click', () => {
diff --git a/app/javascript/flavours/glitch/reducers/announcements.js b/app/javascript/flavours/glitch/reducers/announcements.js
new file mode 100644
index 000000000..34e08eac8
--- /dev/null
+++ b/app/javascript/flavours/glitch/reducers/announcements.js
@@ -0,0 +1,102 @@
+import {
+  ANNOUNCEMENTS_FETCH_REQUEST,
+  ANNOUNCEMENTS_FETCH_SUCCESS,
+  ANNOUNCEMENTS_FETCH_FAIL,
+  ANNOUNCEMENTS_UPDATE,
+  ANNOUNCEMENTS_REACTION_UPDATE,
+  ANNOUNCEMENTS_REACTION_ADD_REQUEST,
+  ANNOUNCEMENTS_REACTION_ADD_FAIL,
+  ANNOUNCEMENTS_REACTION_REMOVE_REQUEST,
+  ANNOUNCEMENTS_REACTION_REMOVE_FAIL,
+  ANNOUNCEMENTS_TOGGLE_SHOW,
+  ANNOUNCEMENTS_DELETE,
+  ANNOUNCEMENTS_DISMISS_SUCCESS,
+} from '../actions/announcements';
+import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
+
+const initialState = ImmutableMap({
+  items: ImmutableList(),
+  isLoading: false,
+  show: false,
+});
+
+const updateReaction = (state, id, name, updater) => state.update('items', list => list.map(announcement => {
+  if (announcement.get('id') === id) {
+    return announcement.update('reactions', reactions => {
+      const idx = reactions.findIndex(reaction => reaction.get('name') === name);
+
+      if (idx > -1) {
+        return reactions.update(idx, reaction => updater(reaction));
+      }
+
+      return reactions.push(updater(fromJS({ name, count: 0 })));
+    });
+  }
+
+  return announcement;
+}));
+
+const updateReactionCount = (state, reaction) => updateReaction(state, reaction.announcement_id, reaction.name, x => x.set('count', reaction.count));
+
+const addReaction = (state, id, name) => updateReaction(state, id, name, x => x.set('me', true).update('count', y => y + 1));
+
+const removeReaction = (state, id, name) => updateReaction(state, id, name, x => x.set('me', false).update('count', y => y - 1));
+
+const sortAnnouncements = list => list.sortBy(x => x.get('starts_at') || x.get('published_at'));
+
+const updateAnnouncement = (state, announcement) => {
+  const idx = state.get('items').findIndex(x => x.get('id') === announcement.get('id'));
+
+  if (idx > -1) {
+    // Deep merge is used because announcements from the streaming API do not contain
+    // personalized data about which reactions have been selected by the given user,
+    // and that is information we want to preserve
+    return state.update('items', list => sortAnnouncements(list.update(idx, x => x.mergeDeep(announcement))));
+  }
+
+  return state.update('items', list => sortAnnouncements(list.unshift(announcement)));
+};
+
+export default function announcementsReducer(state = initialState, action) {
+  switch(action.type) {
+  case ANNOUNCEMENTS_TOGGLE_SHOW:
+    return state.withMutations(map => {
+      map.set('show', !map.get('show'));
+    });
+  case ANNOUNCEMENTS_FETCH_REQUEST:
+    return state.set('isLoading', true);
+  case ANNOUNCEMENTS_FETCH_SUCCESS:
+    return state.withMutations(map => {
+      const items = fromJS(action.announcements);
+
+      map.set('items', items);
+      map.set('isLoading', false);
+    });
+  case ANNOUNCEMENTS_FETCH_FAIL:
+    return state.set('isLoading', false);
+  case ANNOUNCEMENTS_UPDATE:
+    return updateAnnouncement(state, fromJS(action.announcement));
+  case ANNOUNCEMENTS_REACTION_UPDATE:
+    return updateReactionCount(state, action.reaction);
+  case ANNOUNCEMENTS_REACTION_ADD_REQUEST:
+  case ANNOUNCEMENTS_REACTION_REMOVE_FAIL:
+    return addReaction(state, action.id, action.name);
+  case ANNOUNCEMENTS_REACTION_REMOVE_REQUEST:
+  case ANNOUNCEMENTS_REACTION_ADD_FAIL:
+    return removeReaction(state, action.id, action.name);
+  case ANNOUNCEMENTS_DISMISS_SUCCESS:
+    return updateAnnouncement(state, fromJS({ 'id': action.id, 'read': true }));
+  case ANNOUNCEMENTS_DELETE:
+    return state.update('items', list => {
+      const idx = list.findIndex(x => x.get('id') === action.id);
+
+      if (idx > -1) {
+        return list.delete(idx);
+      }
+
+      return list;
+    });
+  default:
+    return state;
+  }
+};
diff --git a/app/javascript/flavours/glitch/reducers/compose.js b/app/javascript/flavours/glitch/reducers/compose.js
index 0f807790b..92a9859d9 100644
--- a/app/javascript/flavours/glitch/reducers/compose.js
+++ b/app/javascript/flavours/glitch/reducers/compose.js
@@ -387,7 +387,7 @@ export default function compose(state = initialState, action) {
 
       if (action.status.get('spoiler_text').length > 0) {
         let spoiler_text = action.status.get('spoiler_text');
-        if (!spoiler_text.match(/^re[: ]/i)) {
+        if (action.prependCWRe && !spoiler_text.match(/^re[: ]/i)) {
           spoiler_text = 're: '.concat(spoiler_text);
         }
         map.set('spoiler', true);
diff --git a/app/javascript/flavours/glitch/reducers/index.js b/app/javascript/flavours/glitch/reducers/index.js
index 7dbca3a29..586b84749 100644
--- a/app/javascript/flavours/glitch/reducers/index.js
+++ b/app/javascript/flavours/glitch/reducers/index.js
@@ -35,8 +35,10 @@ import pinnedAccountsEditor from './pinned_accounts_editor';
 import polls from './polls';
 import identity_proofs from './identity_proofs';
 import trends from './trends';
+import announcements from './announcements';
 
 const reducers = {
+  announcements,
   dropdown_menu,
   timelines,
   meta,
diff --git a/app/javascript/flavours/glitch/reducers/local_settings.js b/app/javascript/flavours/glitch/reducers/local_settings.js
index ad94ea243..3d94d665c 100644
--- a/app/javascript/flavours/glitch/reducers/local_settings.js
+++ b/app/javascript/flavours/glitch/reducers/local_settings.js
@@ -17,6 +17,7 @@ const initialState = ImmutableMap({
   confirm_missing_media_description: false,
   confirm_boost_missing_media_description: false,
   confirm_before_clearing_draft: true,
+  prepend_cw_re: true,
   preselect_on_reply: true,
   inline_preview_cards: true,
   hicolor_privacy_icons: false,
diff --git a/app/javascript/flavours/glitch/reducers/notifications.js b/app/javascript/flavours/glitch/reducers/notifications.js
index 3623e90da..d0eb7bb5a 100644
--- a/app/javascript/flavours/glitch/reducers/notifications.js
+++ b/app/javascript/flavours/glitch/reducers/notifications.js
@@ -23,6 +23,9 @@ import {
   FOLLOW_REQUEST_AUTHORIZE_SUCCESS,
   FOLLOW_REQUEST_REJECT_SUCCESS,
 } from 'flavours/glitch/actions/accounts';
+import {
+  MARKERS_FETCH_SUCCESS,
+} from 'flavours/glitch/actions/markers';
 import { DOMAIN_BLOCK_SUCCESS } from 'flavours/glitch/actions/domain_blocks';
 import { TIMELINE_DELETE, TIMELINE_DISCONNECT } from 'flavours/glitch/actions/timelines';
 import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
@@ -104,7 +107,7 @@ const expandNormalizedNotifications = (state, notifications, next, isLoadingRece
         mutable.update('lastReadId', id => compareId(id, items.first().get('id')) > 0 ? id : items.first().get('id'));
       }
     } else {
-      mutable.update('unread', unread => unread + items.filter(item => compareId(item.get('id'), lastReadId) > 0).size);
+      mutable.update('unread', unread => unread + items.count(item => compareId(item.get('id'), lastReadId) > 0));
     }
 
     if (!next) {
@@ -197,10 +200,24 @@ const shouldCountUnreadNotifications = (state) => {
   return !(state.get('isTabVisible') && state.get('top') && state.get('mounted') > 0);
 };
 
+const recountUnread = (state, last_read_id) => {
+  return state.withMutations(mutable => {
+    if (compareId(last_read_id, mutable.get('lastReadId')) > 0) {
+      mutable.set('lastReadId', last_read_id);
+    }
+
+    if (state.get('unread') > 0 || shouldCountUnreadNotifications(state)) {
+      mutable.set('unread', mutable.get('pendingItems').count(item => item !== null) + mutable.get('items').count(item => item && compareId(item.get('id'), last_read_id) > 0));
+    }
+  });
+}
+
 export default function notifications(state = initialState, action) {
   let st;
 
   switch(action.type) {
+  case MARKERS_FETCH_SUCCESS:
+    return action.markers.notifications ? recountUnread(state, action.markers.notifications.last_read_id) : state;
   case NOTIFICATIONS_MOUNT:
     return updateMounted(state);
   case NOTIFICATIONS_UNMOUNT:
diff --git a/app/javascript/flavours/glitch/reducers/statuses.js b/app/javascript/flavours/glitch/reducers/statuses.js
index ee8ac929d..8926e49f1 100644
--- a/app/javascript/flavours/glitch/reducers/statuses.js
+++ b/app/javascript/flavours/glitch/reducers/statuses.js
@@ -41,8 +41,7 @@ export default function statuses(state = initialState, action) {
   case FAVOURITE_REQUEST:
     return state.setIn([action.status.get('id'), 'favourited'], true);
   case UNFAVOURITE_SUCCESS:
-    const favouritesCount = action.status.get('favourites_count');
-    return state.setIn([action.status.get('id'), 'favourites_count'], favouritesCount - 1);
+    return state.updateIn([action.status.get('id'), 'favourites_count'], x => Math.max(0, x - 1));
   case FAVOURITE_FAIL:
     return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'favourited'], false);
   case BOOKMARK_REQUEST:
diff --git a/app/javascript/flavours/glitch/selectors/index.js b/app/javascript/flavours/glitch/selectors/index.js
index 8ceb71d03..ab7dac66a 100644
--- a/app/javascript/flavours/glitch/selectors/index.js
+++ b/app/javascript/flavours/glitch/selectors/index.js
@@ -27,6 +27,7 @@ export const toServerSideType = columnType => {
   case 'notifications':
   case 'public':
   case 'thread':
+  case 'account':
     return columnType;
   default:
     if (columnType.indexOf('list:') > -1) {
diff --git a/app/javascript/flavours/glitch/styles/admin.scss b/app/javascript/flavours/glitch/styles/admin.scss
index d2f477d19..26a98c66f 100644
--- a/app/javascript/flavours/glitch/styles/admin.scss
+++ b/app/javascript/flavours/glitch/styles/admin.scss
@@ -186,17 +186,22 @@ $content-width: 840px;
 
       padding-bottom: 40px;
       border-bottom: 1px solid lighten($ui-base-color, 8%);
-      margin-bottom: 40px;
+
+      margin: -15px -15px 40px 0;
 
       flex-wrap: wrap;
       align-items: center;
-
       justify-content: space-between;
 
+      & > * {
+        margin-top: 15px;
+        margin-right: 15px;
+      }
+
       &-actions {
         display: inline-flex;
 
-        & > * {
+        & > :not(:first-child) {
           margin-left: 5px;
         }
       }
@@ -894,3 +899,50 @@ a.name-tag,
     color: $primary-text-color;
   }
 }
+
+.center-text {
+  text-align: center;
+}
+
+.announcements-list {
+  border: 1px solid lighten($ui-base-color, 4%);
+  border-radius: 4px;
+
+  &__item {
+    padding: 15px 0;
+    background: $ui-base-color;
+    border-bottom: 1px solid lighten($ui-base-color, 4%);
+
+    &__title {
+      padding: 0 15px;
+      display: block;
+      font-weight: 500;
+      font-size: 18px;
+      line-height: 1.5;
+      color: $secondary-text-color;
+      text-decoration: none;
+      margin-bottom: 10px;
+
+      &:hover,
+      &:focus,
+      &:active {
+        color: $primary-text-color;
+      }
+    }
+
+    &__meta {
+      padding: 0 15px;
+      color: $dark-text-color;
+    }
+
+    &__action-bar {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+    }
+
+    &:last-child {
+      border-bottom: 0;
+    }
+  }
+}
diff --git a/app/javascript/flavours/glitch/styles/components/accounts.scss b/app/javascript/flavours/glitch/styles/components/accounts.scss
index 5be4da48a..6305e2a4d 100644
--- a/app/javascript/flavours/glitch/styles/components/accounts.scss
+++ b/app/javascript/flavours/glitch/styles/components/accounts.scss
@@ -681,13 +681,13 @@
     &__links {
       font-size: 14px;
       color: $darker-text-color;
+      padding: 10px 0;
 
       a {
         display: inline-block;
         color: $darker-text-color;
         text-decoration: none;
-        padding: 10px;
-        padding-top: 20px;
+        padding: 5px 10px;
         font-weight: 500;
 
         strong {
diff --git a/app/javascript/flavours/glitch/styles/components/announcements.scss b/app/javascript/flavours/glitch/styles/components/announcements.scss
new file mode 100644
index 000000000..ac4c199cd
--- /dev/null
+++ b/app/javascript/flavours/glitch/styles/components/announcements.scss
@@ -0,0 +1,225 @@
+.announcements__item__content {
+  word-wrap: break-word;
+
+  .emojione {
+    width: 20px;
+    height: 20px;
+    margin: -3px 0 0;
+  }
+
+  p {
+    margin-bottom: 10px;
+    white-space: pre-wrap;
+
+    &:last-child {
+      margin-bottom: 0;
+    }
+  }
+
+  a {
+    color: $secondary-text-color;
+    text-decoration: none;
+
+    &:hover {
+      text-decoration: underline;
+    }
+
+    &.mention {
+      &:hover {
+        text-decoration: none;
+
+        span {
+          text-decoration: underline;
+        }
+      }
+    }
+
+    &.unhandled-link {
+      color: lighten($ui-highlight-color, 8%);
+    }
+  }
+}
+
+.announcements {
+  background: lighten($ui-base-color, 8%);
+  font-size: 13px;
+  display: flex;
+  align-items: flex-end;
+
+  &__mastodon {
+    width: 124px;
+    flex: 0 0 auto;
+
+    @media screen and (max-width: 124px + 300px) {
+      display: none;
+    }
+  }
+
+  &__container {
+    width: calc(100% - 124px);
+    flex: 0 0 auto;
+    position: relative;
+
+    @media screen and (max-width: 124px + 300px) {
+      width: 100%;
+    }
+  }
+
+  &__item {
+    box-sizing: border-box;
+    width: 100%;
+    padding: 15px;
+    padding-right: 15px + 18px;
+    position: relative;
+    font-size: 15px;
+    line-height: 20px;
+    word-wrap: break-word;
+    font-weight: 400;
+
+    &__range {
+      display: block;
+      font-weight: 500;
+      margin-bottom: 10px;
+    }
+
+    &__unread {
+      position: absolute;
+      top: 15px;
+      right: 15px;
+      display: inline-block;
+      background: $highlight-text-color;
+      border-radius: 50%;
+      width: 0.625rem;
+      height: 0.625rem;
+      margin: 0 .15em;
+    }
+  }
+
+  &__pagination {
+    padding: 15px;
+    color: $darker-text-color;
+    position: absolute;
+    bottom: 3px;
+    right: 0;
+  }
+}
+
+.layout-multiple-columns .announcements__mastodon {
+  display: none;
+}
+
+.layout-multiple-columns .announcements__container {
+  width: 100%;
+}
+
+.reactions-bar {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  margin-top: 15px;
+  margin-left: -2px;
+  width: calc(100% - (90px - 33px));
+
+  &__item {
+    flex-shrink: 0;
+    background: lighten($ui-base-color, 12%);
+    border: 0;
+    border-radius: 3px;
+    margin: 2px;
+    cursor: pointer;
+    user-select: none;
+    padding: 0 6px;
+    display: flex;
+    align-items: center;
+    transition: all 100ms ease-in;
+    transition-property: background-color, color;
+
+    &__emoji {
+      display: block;
+      margin: 3px 0;
+      width: 16px;
+      height: 16px;
+
+      img {
+        display: block;
+        margin: 0;
+        width: 100%;
+        height: 100%;
+        min-width: auto;
+        min-height: auto;
+        vertical-align: bottom;
+        object-fit: contain;
+      }
+    }
+
+    &__count {
+      display: block;
+      min-width: 9px;
+      font-size: 13px;
+      font-weight: 500;
+      text-align: center;
+      margin-left: 6px;
+      color: $darker-text-color;
+    }
+
+    &:hover,
+    &:focus,
+    &:active {
+      background: lighten($ui-base-color, 16%);
+      transition: all 200ms ease-out;
+      transition-property: background-color, color;
+
+      &__count {
+        color: lighten($darker-text-color, 4%);
+      }
+    }
+
+    &.active {
+      transition: all 100ms ease-in;
+      transition-property: background-color, color;
+      background-color: mix(lighten($ui-base-color, 12%), $ui-highlight-color, 80%);
+
+      .reactions-bar__item__count {
+        color: lighten($highlight-text-color, 8%);
+      }
+    }
+  }
+
+  .emoji-picker-dropdown {
+    margin: 2px;
+  }
+
+  &:hover .emoji-button {
+    opacity: 0.85;
+  }
+
+  .emoji-button {
+    color: $darker-text-color;
+    margin: 0;
+    font-size: 16px;
+    width: auto;
+    flex-shrink: 0;
+    padding: 0 6px;
+    height: 22px;
+    display: flex;
+    align-items: center;
+    opacity: 0.5;
+    transition: all 100ms ease-in;
+    transition-property: background-color, color;
+
+    &:hover,
+    &:active,
+    &:focus {
+      opacity: 1;
+      color: lighten($darker-text-color, 4%);
+      transition: all 200ms ease-out;
+      transition-property: background-color, color;
+    }
+  }
+
+  &--empty {
+    .emoji-button {
+      padding: 0;
+    }
+  }
+}
diff --git a/app/javascript/flavours/glitch/styles/components/columns.scss b/app/javascript/flavours/glitch/styles/components/columns.scss
index 6ba9698c5..525dcaf90 100644
--- a/app/javascript/flavours/glitch/styles/components/columns.scss
+++ b/app/javascript/flavours/glitch/styles/components/columns.scss
@@ -224,13 +224,16 @@
 .column-header__wrapper {
   position: relative;
   flex: 0 0 auto;
+  z-index: 1;
 
   &.active {
+    box-shadow: 0 1px 0 rgba($highlight-text-color, 0.3);
+
     &::before {
       display: block;
       content: "";
       position: absolute;
-      top: 35px;
+      bottom: -13px;
       left: 0;
       right: 0;
       margin: 0 auto;
@@ -241,6 +244,11 @@
       background: radial-gradient(ellipse, rgba($ui-highlight-color, 0.23) 0%, rgba($ui-highlight-color, 0) 60%);
     }
   }
+
+  .announcements {
+    z-index: 1;
+    position: relative;
+  }
 }
 
 .column-header {
@@ -273,8 +281,6 @@
   }
 
   &.active {
-    box-shadow: 0 1px 0 rgba($ui-highlight-color, 0.3);
-
     .column-header__icon {
       color: $highlight-text-color;
       text-shadow: 0 0 10px rgba($ui-highlight-color, 0.4);
@@ -378,6 +384,8 @@
   color: $darker-text-color;
   transition: max-height 150ms ease-in-out, opacity 300ms linear;
   opacity: 1;
+  z-index: 1;
+  position: relative;
 
   &.collapsed {
     max-height: 0;
diff --git a/app/javascript/flavours/glitch/styles/components/composer.scss b/app/javascript/flavours/glitch/styles/components/composer.scss
index 51287f62e..943776010 100644
--- a/app/javascript/flavours/glitch/styles/components/composer.scss
+++ b/app/javascript/flavours/glitch/styles/components/composer.scss
@@ -1,5 +1,16 @@
 .composer {
   padding: 10px;
+
+  .emoji-picker-dropdown {
+    position: absolute;
+    right: 5px;
+    top: 5px;
+
+    ::-webkit-scrollbar-track:hover,
+    ::-webkit-scrollbar-track:active {
+      background-color: rgba($base-overlay-background, 0.3);
+    }
+  }
 }
 
 .character-counter {
@@ -235,17 +246,6 @@
   }
 }
 
-.emoji-picker-dropdown {
-  position: absolute;
-  right: 5px;
-  top: 5px;
-
-  ::-webkit-scrollbar-track:hover,
-  ::-webkit-scrollbar-track:active {
-    background-color: rgba($base-overlay-background, 0.3);
-  }
-}
-
 .compose-form__autosuggest-wrapper,
 .autosuggest-input {
   position: relative;
diff --git a/app/javascript/flavours/glitch/styles/components/drawer.scss b/app/javascript/flavours/glitch/styles/components/drawer.scss
index 93a3f62ed..d5463e406 100644
--- a/app/javascript/flavours/glitch/styles/components/drawer.scss
+++ b/app/javascript/flavours/glitch/styles/components/drawer.scss
@@ -210,7 +210,7 @@
     display: block;
     object-fit: contain;
     object-position: bottom left;
-    width: 100%;
+    width: 85%;
     height: 100%;
     pointer-events: none;
     user-drag: none;
diff --git a/app/javascript/flavours/glitch/styles/components/index.scss b/app/javascript/flavours/glitch/styles/components/index.scss
index 8e576fd86..d97ab436d 100644
--- a/app/javascript/flavours/glitch/styles/components/index.scss
+++ b/app/javascript/flavours/glitch/styles/components/index.scss
@@ -3,6 +3,14 @@
   -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;
@@ -1649,3 +1657,4 @@ noscript {
 @import 'local_settings';
 @import 'error_boundary';
 @import 'single_column';
+@import 'announcements';
diff --git a/app/javascript/flavours/glitch/styles/components/status.scss b/app/javascript/flavours/glitch/styles/components/status.scss
index 00f947cdc..fa26c4706 100644
--- a/app/javascript/flavours/glitch/styles/components/status.scss
+++ b/app/javascript/flavours/glitch/styles/components/status.scss
@@ -510,6 +510,7 @@
   .status-check-box__status {
     margin: 10px 0 10px 10px;
     flex: 1;
+    overflow: hidden;
 
     .media-gallery {
       max-width: 250px;
diff --git a/app/javascript/flavours/glitch/styles/forms.scss b/app/javascript/flavours/glitch/styles/forms.scss
index 1920c33ea..396e87c6c 100644
--- a/app/javascript/flavours/glitch/styles/forms.scss
+++ b/app/javascript/flavours/glitch/styles/forms.scss
@@ -213,6 +213,12 @@ code {
     }
   }
 
+  .input.datetime .label_input select {
+    display: inline-block;
+    width: auto;
+    flex: 0;
+  }
+
   .required abbr {
     text-decoration: none;
     color: lighten($error-value-color, 12%);
diff --git a/app/javascript/flavours/glitch/styles/polls.scss b/app/javascript/flavours/glitch/styles/polls.scss
index 49d0e7f71..9c86cca58 100644
--- a/app/javascript/flavours/glitch/styles/polls.scss
+++ b/app/javascript/flavours/glitch/styles/polls.scss
@@ -170,6 +170,7 @@
 
 .compose-form__poll-wrapper {
   border-top: 1px solid darken($simple-background-color, 8%);
+  overflow-x: hidden;
 
   ul {
     padding: 10px;
diff --git a/app/javascript/flavours/glitch/styles/statuses.scss b/app/javascript/flavours/glitch/styles/statuses.scss
index 611d5185b..22fa7b3fd 100644
--- a/app/javascript/flavours/glitch/styles/statuses.scss
+++ b/app/javascript/flavours/glitch/styles/statuses.scss
@@ -222,3 +222,20 @@
     }
   }
 }
+
+.status__content__read-more-button {
+  display: block;
+  font-size: 15px;
+  line-height: 20px;
+  color: lighten($ui-highlight-color, 8%);
+  border: 0;
+  background: transparent;
+  padding: 0;
+  padding-top: 8px;
+  text-decoration: none;
+
+  &:hover,
+  &:active {
+    text-decoration: underline;
+  }
+}
diff --git a/app/javascript/flavours/glitch/util/numbers.js b/app/javascript/flavours/glitch/util/numbers.js
index f7e4ceb93..af18dcfdd 100644
--- a/app/javascript/flavours/glitch/util/numbers.js
+++ b/app/javascript/flavours/glitch/util/numbers.js
@@ -4,9 +4,13 @@ import { FormattedNumber } from 'react-intl';
 export const shortNumberFormat = number => {
   if (number < 1000) {
     return <FormattedNumber value={number} />;
-  } else if (number < 1000000) {
+  } else if (number < 10000) {
     return <Fragment><FormattedNumber value={number / 1000} maximumFractionDigits={1} />K</Fragment>;
-  } else {
+  } else if (number < 1000000) {
+    return <Fragment><FormattedNumber value={number / 1000} maximumFractionDigits={0} />K</Fragment>;
+  } else if (number < 10000000) {
     return <Fragment><FormattedNumber value={number / 1000000} maximumFractionDigits={1} />M</Fragment>;
+  } else {
+    return <Fragment><FormattedNumber value={number / 1000000} maximumFractionDigits={0} />M</Fragment>;
   }
 };
diff --git a/app/javascript/flavours/glitch/util/stream.js b/app/javascript/flavours/glitch/util/stream.js
index 50f90d44c..0cb2b228f 100644
--- a/app/javascript/flavours/glitch/util/stream.js
+++ b/app/javascript/flavours/glitch/util/stream.js
@@ -2,6 +2,14 @@ import WebSocketClient from '@gamestdio/websocket';
 
 const randomIntUpTo = max => Math.floor(Math.random() * Math.floor(max));
 
+const knownEventTypes = [
+  'update',
+  'delete',
+  'notification',
+  'conversation',
+  'filters_changed',
+];
+
 export function connectStream(path, pollingRefresh = null, callbacks = () => ({ onConnect() {}, onDisconnect() {}, onReceive() {} })) {
   return (dispatch, getState) => {
     const streamingAPIBaseURL = getState().getIn(['meta', 'streaming_api_base_url']);
@@ -69,14 +77,43 @@ export function connectStream(path, pollingRefresh = null, callbacks = () => ({
 
 
 export default function getStream(streamingAPIBaseURL, accessToken, stream, { connected, received, disconnected, reconnected }) {
-  const params = [ `stream=${stream}` ];
-
-  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;
-  ws.onreconnect = reconnected;
+  const params = stream.split('&');
+  stream = params.shift();
+
+  if (streamingAPIBaseURL.startsWith('ws')) {
+    params.unshift(`stream=${stream}`);
+    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;
+    ws.onreconnect = reconnected;
+
+    return ws;
+  }
+
+  stream = stream.replace(/:/g, '/');
+  params.push(`access_token=${accessToken}`);
+  const es = new EventSource(`${streamingAPIBaseURL}/api/v1/streaming/${stream}?${params.join('&')}`);
+
+  let firstConnect = true;
+  es.onopen = () => {
+    if (firstConnect) {
+      firstConnect = false;
+      connected();
+    } else {
+      reconnected();
+    }
+  };
+  for (let type of knownEventTypes) {
+    es.addEventListener(type, (e) => {
+      received({
+        event: e.type,
+        payload: e.data,
+      });
+    });
+  }
+  es.onerror = disconnected;
 
-  return ws;
+  return es;
 };
diff --git a/app/javascript/images/elephant_ui_plane.svg b/app/javascript/images/elephant_ui_plane.svg
index a2624d170..ca675c9eb 100644
--- a/app/javascript/images/elephant_ui_plane.svg
+++ b/app/javascript/images/elephant_ui_plane.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 292.85862 204.49997" width="292.85861" height="204.49997"><g transform="translate(-395.89999 -820.4)"><defs><path id="a" d="M395.89999 745.09998H690.5v279.79999H395.89999z"/></defs><clipPath id="b"><use xlink:href="#a" width="100%" height="100%" overflow="visible"/></clipPath><path class="st53" d="M339.3 1028.6c1.5-3.2 14.4-31.3 27.4-58.8-6-9.3-2-17 1.5-23.7 1.9-3.7 3.8-7.1 3.6-10.4-.8-22 8.1-38.3 22.8-41.6 2.8-.6 5.2-.9 7.5-.9 3 0 5.6.5 8.1 1.6 1.4-1.3 2.8-2.6 4.2-3.8-2.8-2.6-4.3-5.5-4.5-8.8-.3-4.5 2.2-9.5 6.8-13.7 5.3-4.8 16.5-12.9 31.7-12.9.9 0 1.7 0 2.6.1-.4-.9-1-2-2.1-2.9-2.1-1.6-1.9-3.2-1.6-4 .7-2.1 3.6-3.2 8.1-3.2 3.9 0 9.7 1.2 14 4.4.3-.7.7-1.3 1.3-1.7.5-.3 1.3-.5 2.2-.5 3.4 0 10.6 2.7 15.5 9.9 3.6 5.3 3.6 10.8.1 16 18.3 4.7 30.1 15.6 39.5 24.4 2.5 2.4 5 4.6 7.3 6.5 10.7 8.9 21.4 13.2 32.7 13.2.9 0 1.8 0 2.7-.1 2-13.5-4.1-25.5-10-35.7-6.2-10.7-6.4-12.1-4.9-13.9l.1-.1c.6-.7 1.3-1 2.1-1.1h.3c1.7 0 4.5 1 13 8.7 9.9 9 16.9 22.2 19.2 36.5 8.9-4.9 15.2-12.5 17.1-20.3 2-8.6.5-16.8-4.2-22.7l-13.1 6.1-7-16.9-15.6 3.2 7.2-19.5h.1l-.2-.5 4.6-11.1 65.7 11.9c3.1.6 3.9 2.7 3.6 4.5l-.2 1-.4-.1c-.3.4-.6.7-1 .9-.7.3-7.5 3.6-21 9.9 2.1 2.9 2.2 6.1.4 9.4-1.1 1.9-2.5 6.2.4 13.3 3.7 8.9 3.5 29.2-8.3 46.2-8.1 11.7-18.3 23-37.6 26.6-3 4.6-6.5 9-10.2 12.6-7.8 7.6-24 15.3-42.3 15.3-5.3 0-10.7-.6-15.9-1.9-7.6 12.9-10.5 26.2-10.6 32.1-.2 11 .9 16.1.9 16.2l.4 1.8-164.9.8.9-2.3z" clip-path="url(#b)"/><path class="st53" d="M339.8 1028.8c.1-.3 13.9-30.1 27.5-59.1-6.1-9.2-2.1-16.8 1.4-23.5 2-3.7 3.8-7.3 3.7-10.6-.8-21.7 8-37.9 22.4-41 2.7-.6 5.1-.9 7.4-.9 3 0 5.7.5 8.2 1.7 1.6-1.6 3.2-3 4.9-4.4-3-2.6-4.6-5.5-4.8-8.8-.3-4.4 2.1-9.2 6.6-13.3 5.3-4.8 16.4-12.8 31.4-12.8 1.1 0 2.2 0 3.3.1-.3-1.1-1-2.7-2.5-3.8-1.8-1.4-1.7-2.8-1.5-3.4.6-1.8 3.4-2.9 7.6-2.9 4.4 0 10.3 1.5 14.3 4.8.1-.9.6-1.7 1.3-2.1.4-.3 1.1-.4 1.9-.4 3.3 0 10.3 2.6 15.1 9.7 3.6 5.3 3.5 10.9-.3 16.1 18.6 4.6 30.5 15.6 40 24.4 2.6 2.4 5 4.6 7.3 6.5 10.8 8.9 21.6 13.3 33.1 13.3 1 0 2.1 0 3.1-.1 2.2-13.9-4-26.1-10-36.4-6.3-10.9-6.2-11.8-5-13.3l.1-.1c.2-.2.7-.9 1.8-.9h.2c1.5 0 4.2 1 12.7 8.6 10 9.1 17 22.5 19.2 36.9 9.3-5 16-12.8 17.9-20.9 2.1-8.9.4-17.4-4.5-23.4l-13 6.1-6.9-16.8-15.1 3.1 6.8-18.4h.6l-.5-1 4.4-10.6 65.3 11.8c3.5.7 3.3 3.2 3.2 3.9l-.1.5h-.2c-.2.4-.6.8-1 1-.7.3-7.9 3.7-21.6 10.2.1.1.2.2.3.4 2 2.7 2.2 5.8.4 8.9-1.1 2-2.6 6.4.4 13.7 3.6 8.8 3.4 28.8-8.2 45.7-8.1 11.7-18.2 22.9-37.5 26.4-3 4.7-6.5 9.1-10.3 12.7-7.8 7.5-23.7 15.1-42 15.1-5.4 0-10.9-.7-16.1-2-7.7 13.1-10.7 26.7-10.9 32.7-.2 11.1.9 16.2.9 16.3l.3 1.2-163.5.8.5-1.7z" clip-path="url(#b)"/><path d="M577.5 843.7l-1-2.1 3.9-9.4 64.5 11.6c2.4.5 2.5 2 2.4 2.8" clip-path="url(#b)" fill="#fff" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10"/><path d="M584.2 856.7l6.9 16.6s53.7-25.1 55.5-26c.9-.5 1.5-2.2-1-2.3-2.5-.1-69.6-1.4-69.6-1.4l-5.9 16 14.1-2.9z" clip-path="url(#b)" fill="#fff" stroke="#000" stroke-width="1.70000005" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10"/><path d="M585.1 857l58.9-11.2 1.4-.5h.4c.8 0 .7.3.8.6.1.3-.1.6-.4.8L587 861.5l-1.9-4.5z" clip-path="url(#b)" fill="#d1d3d4"/><path class="st57" clip-path="url(#b)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M584.20001 856.70001l61.79999-11.5"/><path class="st58" d="M388.5 927.9c20-40 49.9-56.3 83.4-54.9 33.6 1.4 48.8 21.7 62.4 32.9 13.6 11.2 27.5 15.6 43.4 12.5 15.9-3.1 28.5-14.6 31.2-26.1 2.7-11.5-1-20.7-6.1-25.8-5.1-5.1-1.4-8.1 4.1-6.8 5.4 1.4 8.1 3.4 8.1 3.4s7.8-6.4 9.8-3.7c2 2.7 1.7 5.4.3 7.8-1.4 2.4-2.7 7.1.3 14.6 3.1 7.5 4.1 27.1-8.1 44.8-12.2 17.6-26.5 30.1-62.1 26.5-38.5-3.9-54.6 44.2-54.9 59-.2 11.5.9 16.5.9 16.5l-160.7.8c.2-.1 33.4-72.4 48-101.5z" clip-path="url(#b)" fill="#53606c" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10"/><path class="st59" d="M399.4 989.3c-6.1 0-9.9-4.3-10-4.3l-.2-.2-.3.1c-3.1 1-6.1 1.5-9 1.5-6.1 0-11.4-2.2-15.6-6.5 1.4-3 2.9-6.1 4.3-9.1.1.1.1.2.2.3 6.1 8 12.7 9.7 17 9.7 3.2 0 5.6-.9 6.6-1.3.7 1.3 3 4.7 7.5 4.7 1.2 0 2.5-.3 3.9-.8 6.7-2.5 15.7-13.1 18.3-22.1 3.2 5.7-2.8 18.3-13.8 25.3-3 1.7-6 2.7-8.9 2.7zm24.5-78.1c-5.7-11.1-10.8-14.2-12.4-14.9 15.7-15.3 34.7-23 56.6-23 1.3 0 2.5 0 3.8.1 2.1.1 4.2.3 6.4.5 1.5 2.7-.4 5.6-.8 6.3-3 .8-11.2 6.5-18.5 11.6-1.9 1.3-3.7 2.6-5.3 3.7-5.3 3.7-13.6 4.2-18 4.2-1.8 0-2.9-.1-2.9-.1h-.3l-8.6 11.6zm95.3 49c4.2-7.1 12.6-15.2 28.3-15.2 3.5 0 7.2.4 11 1.2 3.4.7 7 1.1 10.6 1.1 15.9 0 29.8-7.9 34.8-12.5 6.5-6 10.5-9.8 12-12.4 1.4-2.4 3.2-2.8 4.7-2 .1.1-.7 1.1-.6 1.1-.9 1.7-1.9 3.3-3 4.8-11.5 16.6-23.9 26.9-50 26.9-3.6 0-7.5-.2-11.6-.6-1.5-.2-3-.2-4.4-.2-9.4 0-18.1 3.2-25.9 9.4l-5.9-1.6z" clip-path="url(#b)" fill="#38434f"/><path class="st58" d="M427.4 920.5c-9.1-24.2-19-27.9-32.4-25-13.4 3-22.4 18-21.6 40 .4 10.2-15.5 20.5-4.3 35s23.5 8 23.5 8 3 7.1 11 4.1 16.7-13.5 18.5-21.6" clip-path="url(#b)" fill="#53606c" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10"/><path class="st59" d="M417.2 934.2c-7.3.5-8.6-4.8-7.3-7 1.2-2.1 4.6-3.2 4.6-3.2s-5.3-.2-6.3-4c-1-3.7 4-5.3 4-5.3s-2.9-8.6-13.3-6.1-14.4 11.7-14.2 19c.6 15.3-10.2 22.8 0 29.7 10.1 6.9 23.7-2.8 28.5-11.3 4.8-8.5 4-11.8 4-11.8z" clip-path="url(#b)" fill="#38434f"/><path class="st57" d="M392.6 978.6c6.3-3.9 9.1-7.5 9.1-7.5m15.5-36.9c-7.3.5-8.6-4.8-7.3-7 1.2-2.1 4.6-3.2 4.6-3.2s-5.3-.2-6.3-4c-1-3.7 4-5.3 4-5.3s-2.9-8.6-13.3-6.1-14.6 11.7-14.2 19" clip-path="url(#b)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10"/><path d="M417.5 891.4c-9.8-6.9-6.2-16 .3-21.8 6.5-5.8 19-14 35.2-12.2 0 0-.2-3.5-3.1-5.8s-.5-4.5 5.5-4.5 12.9 2.6 15.8 6.9c0 0-1.4-3.2.4-4.4 1.8-1.2 10.2 1 15.6 9s1.1 14.9-5.2 19.9c-6.3 5-36.2 11.6-48.8 10.9" clip-path="url(#b)" fill="#53606c"/><g class="st53" clip-path="url(#b)"><path class="st36" d="M459.5 878.7c-11.1.6-22.1 3.3-32.9 6.1-4.1 1.1-8.2 2.2-12.4 2-.4 0-.8-.1-1.3-.1 1 1.6 2.5 3.2 4.6 4.7l15.6-2.1c11.3.7 37-4.7 46.4-9.4-6.5-1.1-13.3-1.5-20-1.2z" fill="#38434f"/></g><path class="st59" d="M417.5 890.8c-3.5-2.5-5.3-5.5-5.5-8.8-.2-3.2 1.2-6.7 4-9.9-.4 1.6-.2 3.2.5 4.7 1.2 2.5 3.6 4 6.3 4 2.1 0 4.3-.9 6.3-2.6 7.2-6 19.4-10.4 29.1-10.4 1.9 0 3.7.2 5.3.5 7.7 1.6 12.3 3.7 13.8 6.7.8 1.6.8 3.3 0 5.4-10.1 4.3-31.2 8.6-42.1 8.6-.7 0-1.4 0-2.1-.1-1.4-.1-2.8-.1-4-.1-7.5-.1-10.6 1.4-11.6 2z" clip-path="url(#b)" fill="#38434f"/><path class="st57" d="M471.2 854c2.2 3.4 1.1 6.7 1.1 6.7" clip-path="url(#b)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10"/><path d="M489.6 967.9c34.6 23.5 68.7 8.7 80.2-2.4 11.4-11.1 20.8-28.5 20-45.9-.8-17.4-8.5-32.9-19.5-42.8-10.7-9.6-12.4-8.7-13.2-7.7-.8 1-1.4 1.1 5 12.2s13.4 24.6 9.5 40.2c-3.9 15.6-15.6 29.9-29.6 34-14 4-21.6-1.4-26.1-3.2 0 0 1 4-3.4 4.3-4.3.3-11.1-2.9-11.1-2.9s2.7 3 .1 5-5.9-.9-5.9-.9 2.5 2.2 1.5 3.3c-1 1.1-2.3.8-4.2-.6 0 0 3.2 3.6 1 5.5s-4.3 1.9-4.3 1.9z" clip-path="url(#b)" fill="#b3becd" stroke="#000" stroke-width="1.60000002" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10"/><path d="M528.6 979.9c-13.1 0-25.8-3.9-37.6-11.7.9-.2 2.1-.7 3.3-1.8 1.4-1.3 1-3 .3-4.3.4.1.8.2 1.1.2.7 0 1.3-.3 1.8-.8.3-.3.4-.6.4-1 0-.3-.1-.6-.2-.9.6.2 1.2.4 1.9.4.9 0 1.7-.3 2.4-.8.3-.2.5-.5.7-.8 12.6 6.2 22.6 9.1 31.5 9.1 7.8 0 14.7-2.3 21.1-6.9 16.8-12.3 21.3-21.3 24.6-27.9l.3-.6c2.3-4.5 4.2-6.5 6.4-6.5.9 0 1.7.3 2.7.9-1.4 13.6-8.7 28-19.6 38.6-7.9 7.4-23.4 14.8-41.1 14.8z" clip-path="url(#b)" fill="#92a1b5"/><path class="st57" d="M489.6 967.9c-3.9.1-6.7-.9-6.7-.9" clip-path="url(#b)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10"/><path d="M484 908.3c-2.2.9-2.1 3.2.1 6.7 2.1 3.5 4.5 7.9 5.9 10.3 1.4 2.4 2.6 3.5 4 2.8 1.4-.6 1.8-2 .4-4.9-1.4-2.9-5.8-11-7.2-12.7-1.6-1.7-2.4-2.5-3.2-2.2z" clip-path="url(#b)" fill="#38434f" stroke="#000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10"/><path class="st57" d="M525.9 907.8c2.5-3.7 4.8-5.2 4.8-5.2m3.8 11.9c1.5-3.1 3.7-5.6 3.7-5.6m11.9 13.3c1-3.9 2.7-5.6 2.7-5.6m10.7 9c.6-4.6.9-6.6.9-6.6" clip-path="url(#b)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10"/><g class="st53" clip-path="url(#b)"><path class="st49" d="M504.9 862.2c.8-.4 1.5-.8 2.2-1.1" fill="none" stroke="#e3e5e5" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M519.8 856.2c13.6-3.5 22.1.5 28.2 3.3 5.3 2.4 11.6 4.7 17.6 5.3" fill="none" stroke="#e3e5e5" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="4.5286,13.5857"/><path class="st49" d="M572.4 864.5c.8-.2 1.6-.4 2.4-.7" fill="none" stroke="#e3e5e5" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g class="st53" clip-path="url(#b)"><path d="M491.2 946.7c-4.1 1.1-6.1 1.8-10 3.5-2.1.9-4.6.1-5.6-1.9s0-4.6 2.3-5.6c4.3-1.9 6.6-2.7 11.2-3.9 2.4-.6 4.8.8 5.3 2.9.4 2.2-1 4.4-3.2 5z" fill="#505762"/></g><path class="st57" d="M515.8 952.3c-.2-2.3-1.7-3.3-1.7-3.3" clip-path="url(#b)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10"/><g class="st53" clip-path="url(#b)"><path d="M454.5 887.5c-3.5.7-7.1 1.3-10.6 1.7-3.6.4-7.2.8-10.8.6-.3 0-.5-.2-.5-.5s.2-.5.5-.5c3.5.2 7.1-.1 10.6-.4 3.5-.4 7.1-.9 10.6-1.6.2 0 .4.1.4.3.1.2 0 .4-.2.4z"/></g><g class="st53" clip-path="url(#b)"><path d="M417.2 891.8c-2.2-1.6-4.3-3.6-5.4-6.2l-.4-1-.2-1-.1-.5v-.5c0-.4-.1-.7-.1-1.1 0-1.4.2-2.8.7-4.1.9-2.6 2.6-4.9 4.4-6.9 1.9-2 4-3.6 6.2-5.2s4.6-2.9 7-4.1c2.4-1.2 5-2.2 7.6-2.9 5.2-1.5 10.7-1.9 16.1-1.4l-.6.5c-.2-1.3-.7-2.7-1.4-3.8-.4-.6-.8-1.1-1.4-1.5-.5-.5-1.2-1-1.5-1.9-.1-.2-.1-.5-.1-.7.1-.2.1-.5.2-.7.2-.4.6-.7.9-.9.7-.5 1.4-.7 2.2-.9 1.5-.3 3-.4 4.4-.4 2.9.1 5.9.7 8.6 1.8 2.7 1.1 5.3 2.8 7.1 5.3l-.9.5c-.3-.7-.5-1.3-.5-2.1-.1-.7 0-1.5.4-2.3.3-.3.5-.7 1-.8.2-.1.4-.1.6-.2.2 0 .4-.1.6-.1.7 0 1.4.1 2.1.2 1.4.3 2.6.8 3.9 1.4 1.2.6 2.4 1.3 3.5 2.1 2.2 1.6 4.1 3.6 5.6 5.9.7 1.2 1.4 2.4 1.8 3.7.3.6.4 1.3.5 2 .1.3.1.7.1 1v1c-.1 2.8-1.3 5.5-3 7.6-1.7 2.2-3.6 4-5.9 5.6-2.4 1.4-4.9 2.3-7.4 3.2-2.5.8-5.1 1.5-7.8 1.9 1.2-.5 2.5-.9 3.7-1.4l3.7-1.3c2.5-.9 5-1.8 7.2-3.2 2.1-1.5 4-3.3 5.6-5.4 1.5-2.1 2.6-4.5 2.7-7 .1-2.5-.8-5-2.2-7.1-1.4-2.2-3.2-4.1-5.3-5.6-2.1-1.5-4.4-2.8-6.9-3.4-.6-.1-1.2-.2-1.8-.2-.6 0-1 .1-1.3.5-.5.8-.2 2.3.2 3.4.1.3 0 .5-.3.6-.2.1-.4 0-.6-.2-1.6-2.3-4-3.8-6.6-4.9-2.6-1.1-5.4-1.7-8.2-1.7-1.4 0-2.8.1-4.2.4-.7.2-1.3.4-1.8.7-.5.4-.8.8-.7 1.3.1.5.6.9 1.2 1.4.6.5 1.1 1.1 1.5 1.7.9 1.3 1.4 2.7 1.6 4.3 0 .3-.2.5-.4.5h-.1c-5.3-.5-10.7-.1-15.8 1.4-2.5.7-5 1.7-7.4 2.8-2.4 1.2-4.7 2.5-6.8 4.1-2.2 1.5-4.3 3.2-6.1 5.1-1.8 1.9-3.4 4.1-4.2 6.5-.4 1.2-.6 2.5-.7 3.8 0 .3.1.6.1 1v.5l.1.5.2.9.4.9c1 2.4 2.9 4.3 5 5.8.2.2.3.5.1.7-.2.2-.4.3-.7.1z"/></g><g class="st53" clip-path="url(#b)"><path class="st36" d="M615.1 863.2c2 1.2 2.3 1.8 2.3 1.8" fill="#38434f"/><path d="M615.3 862.9l1.2.9c.2.2.4.3.6.5.2.2.3.4.5.6 0 .1 0 .2-.1.2h-.2l-.5-.5c-.2-.1-.4-.3-.6-.4-.4-.3-.8-.5-1.3-.7-.2-.1-.2-.3-.1-.5s.2-.2.5-.1c-.1 0-.1 0 0 0z"/></g></g></svg>
\ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 252.85863 194.49997" width="252.85863" height="194.49997"><g transform="translate(-395.89999 -830.4)"><defs><path id="a" d="M395.89999 745.09998H690.5V1024.9H395.89999z"/></defs><clipPath id="b"><use xlink:href="#a" width="100%" height="100%" overflow="visible"/></clipPath><path class="st53" d="M339.3 1028.6c1.5-3.2 14.4-31.3 27.4-58.8-6-9.3-2-17 1.5-23.7 1.9-3.7 3.8-7.1 3.6-10.4-.8-22 8.1-38.3 22.8-41.6 2.8-.6 5.2-.9 7.5-.9 3 0 5.6.5 8.1 1.6 1.4-1.3 2.8-2.6 4.2-3.8-2.8-2.6-4.3-5.5-4.5-8.8-.3-4.5 2.2-9.5 6.8-13.7 5.3-4.8 16.5-12.9 31.7-12.9.9 0 1.7 0 2.6.1-.4-.9-1-2-2.1-2.9-2.1-1.6-1.9-3.2-1.6-4 .7-2.1 3.6-3.2 8.1-3.2 3.9 0 9.7 1.2 14 4.4.3-.7.7-1.3 1.3-1.7.5-.3 1.3-.5 2.2-.5 3.4 0 10.6 2.7 15.5 9.9 3.6 5.3 3.6 10.8.1 16 18.3 4.7 30.1 15.6 39.5 24.4 2.5 2.4 5 4.6 7.3 6.5 10.7 8.9 21.4 13.2 32.7 13.2.9 0 1.8 0 2.7-.1 2-13.5-4.1-25.5-10-35.7-6.2-10.7-6.4-12.1-4.9-13.9l.1-.1c.6-.7 1.3-1 2.1-1.1h.3c1.7 0 4.5 1 13 8.7 9.9 9 16.9 22.2 19.2 36.5 8.9-4.9 15.2-12.5 17.1-20.3 2-8.6.5-16.8-4.2-22.7l-13.1 6.1-7-16.9-15.6 3.2 7.2-19.5h.1l-.2-.5 4.6-11.1 65.7 11.9c3.1.6 3.9 2.7 3.6 4.5l-.2 1-.4-.1c-.3.4-.6.7-1 .9-.7.3-7.5 3.6-21 9.9 2.1 2.9 2.2 6.1.4 9.4-1.1 1.9-2.5 6.2.4 13.3 3.7 8.9 3.5 29.2-8.3 46.2-8.1 11.7-18.3 23-37.6 26.6-3 4.6-6.5 9-10.2 12.6-7.8 7.6-24 15.3-42.3 15.3-5.3 0-10.7-.6-15.9-1.9-7.6 12.9-10.5 26.2-10.6 32.1-.2 11 .9 16.1.9 16.2l.4 1.8-164.9.8.9-2.3z" clip-path="url(#b)"/><path class="st53" d="M339.8 1028.8c.1-.3 13.9-30.1 27.5-59.1-6.1-9.2-2.1-16.8 1.4-23.5 2-3.7 3.8-7.3 3.7-10.6-.8-21.7 8-37.9 22.4-41 2.7-.6 5.1-.9 7.4-.9 3 0 5.7.5 8.2 1.7 1.6-1.6 3.2-3 4.9-4.4-3-2.6-4.6-5.5-4.8-8.8-.3-4.4 2.1-9.2 6.6-13.3 5.3-4.8 16.4-12.8 31.4-12.8 1.1 0 2.2 0 3.3.1-.3-1.1-1-2.7-2.5-3.8-1.8-1.4-1.7-2.8-1.5-3.4.6-1.8 3.4-2.9 7.6-2.9 4.4 0 10.3 1.5 14.3 4.8.1-.9.6-1.7 1.3-2.1.4-.3 1.1-.4 1.9-.4 3.3 0 10.3 2.6 15.1 9.7 3.6 5.3 3.5 10.9-.3 16.1 18.6 4.6 30.5 15.6 40 24.4 2.6 2.4 5 4.6 7.3 6.5 10.8 8.9 21.6 13.3 33.1 13.3 1 0 2.1 0 3.1-.1 2.2-13.9-4-26.1-10-36.4-6.3-10.9-6.2-11.8-5-13.3l.1-.1c.2-.2.7-.9 1.8-.9h.2c1.5 0 4.2 1 12.7 8.6 10 9.1 17 22.5 19.2 36.9 9.3-5 16-12.8 17.9-20.9 2.1-8.9.4-17.4-4.5-23.4l-13 6.1-6.9-16.8-15.1 3.1 6.8-18.4h.6l-.5-1 4.4-10.6 65.3 11.8c3.5.7 3.3 3.2 3.2 3.9l-.1.5h-.2c-.2.4-.6.8-1 1-.7.3-7.9 3.7-21.6 10.2.1.1.2.2.3.4 2 2.7 2.2 5.8.4 8.9-1.1 2-2.6 6.4.4 13.7 3.6 8.8 3.4 28.8-8.2 45.7-8.1 11.7-18.2 22.9-37.5 26.4-3 4.7-6.5 9.1-10.3 12.7-7.8 7.5-23.7 15.1-42 15.1-5.4 0-10.9-.7-16.1-2-7.7 13.1-10.7 26.7-10.9 32.7-.2 11.1.9 16.2.9 16.3l.3 1.2-163.5.8.5-1.7z" clip-path="url(#b)"/><path d="M577.5 843.7l-1-2.1 3.9-9.4 64.5 11.6c2.4.5 2.5 2 2.4 2.8" clip-path="url(#b)" stroke-miterlimit="10" fill="#fff" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><path d="M584.2 856.7l6.9 16.6s53.7-25.1 55.5-26c.9-.5 1.5-2.2-1-2.3-2.5-.1-69.6-1.4-69.6-1.4l-5.9 16 14.1-2.9z" clip-path="url(#b)" stroke-miterlimit="10" fill="#fff" stroke="#000" stroke-width="1.70000005" stroke-linecap="round" stroke-linejoin="round"/><path d="M585.1 857l58.9-11.2 1.4-.5h.4c.8 0 .7.3.8.6.1.3-.1.6-.4.8L587 861.5l-1.9-4.5z" clip-path="url(#b)" fill="#d1d3d4"/><path class="st57" clip-path="url(#b)" stroke-miterlimit="10" d="M584.20001 856.70001l61.79999-11.5" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><path class="st58" d="M388.5 927.9c20-40 49.9-56.3 83.4-54.9 33.6 1.4 48.8 21.7 62.4 32.9 13.6 11.2 27.5 15.6 43.4 12.5 15.9-3.1 28.5-14.6 31.2-26.1 2.7-11.5-1-20.7-6.1-25.8-5.1-5.1-1.4-8.1 4.1-6.8 5.4 1.4 8.1 3.4 8.1 3.4s7.8-6.4 9.8-3.7c2 2.7 1.7 5.4.3 7.8-1.4 2.4-2.7 7.1.3 14.6 3.1 7.5 4.1 27.1-8.1 44.8-12.2 17.6-26.5 30.1-62.1 26.5-38.5-3.9-54.6 44.2-54.9 59-.2 11.5.9 16.5.9 16.5l-160.7.8c.2-.1 33.4-72.4 48-101.5z" clip-path="url(#b)" stroke-miterlimit="10" fill="#53606c" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><path class="st59" d="M399.4 989.3c-6.1 0-9.9-4.3-10-4.3l-.2-.2-.3.1c-3.1 1-6.1 1.5-9 1.5-6.1 0-11.4-2.2-15.6-6.5 1.4-3 2.9-6.1 4.3-9.1.1.1.1.2.2.3 6.1 8 12.7 9.7 17 9.7 3.2 0 5.6-.9 6.6-1.3.7 1.3 3 4.7 7.5 4.7 1.2 0 2.5-.3 3.9-.8 6.7-2.5 15.7-13.1 18.3-22.1 3.2 5.7-2.8 18.3-13.8 25.3-3 1.7-6 2.7-8.9 2.7zm24.5-78.1c-5.7-11.1-10.8-14.2-12.4-14.9 15.7-15.3 34.7-23 56.6-23 1.3 0 2.5 0 3.8.1 2.1.1 4.2.3 6.4.5 1.5 2.7-.4 5.6-.8 6.3-3 .8-11.2 6.5-18.5 11.6-1.9 1.3-3.7 2.6-5.3 3.7-5.3 3.7-13.6 4.2-18 4.2-1.8 0-2.9-.1-2.9-.1h-.3l-8.6 11.6zm95.3 49c4.2-7.1 12.6-15.2 28.3-15.2 3.5 0 7.2.4 11 1.2 3.4.7 7 1.1 10.6 1.1 15.9 0 29.8-7.9 34.8-12.5 6.5-6 10.5-9.8 12-12.4 1.4-2.4 3.2-2.8 4.7-2 .1.1-.7 1.1-.6 1.1-.9 1.7-1.9 3.3-3 4.8-11.5 16.6-23.9 26.9-50 26.9-3.6 0-7.5-.2-11.6-.6-1.5-.2-3-.2-4.4-.2-9.4 0-18.1 3.2-25.9 9.4l-5.9-1.6z" clip-path="url(#b)" fill="#38434f"/><path class="st58" d="M427.4 920.5c-9.1-24.2-19-27.9-32.4-25-13.4 3-22.4 18-21.6 40 .4 10.2-15.5 20.5-4.3 35s23.5 8 23.5 8 3 7.1 11 4.1 16.7-13.5 18.5-21.6" clip-path="url(#b)" stroke-miterlimit="10" fill="#53606c" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><path class="st59" d="M417.2 934.2c-7.3.5-8.6-4.8-7.3-7 1.2-2.1 4.6-3.2 4.6-3.2s-5.3-.2-6.3-4c-1-3.7 4-5.3 4-5.3s-2.9-8.6-13.3-6.1-14.4 11.7-14.2 19c.6 15.3-10.2 22.8 0 29.7 10.1 6.9 23.7-2.8 28.5-11.3 4.8-8.5 4-11.8 4-11.8z" clip-path="url(#b)" fill="#38434f"/><path class="st57" d="M392.6 978.6c6.3-3.9 9.1-7.5 9.1-7.5m15.5-36.9c-7.3.5-8.6-4.8-7.3-7 1.2-2.1 4.6-3.2 4.6-3.2s-5.3-.2-6.3-4c-1-3.7 4-5.3 4-5.3s-2.9-8.6-13.3-6.1-14.6 11.7-14.2 19" clip-path="url(#b)" stroke-miterlimit="10" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><path d="M417.5 891.4c-9.8-6.9-6.2-16 .3-21.8 6.5-5.8 19-14 35.2-12.2 0 0-.2-3.5-3.1-5.8s-.5-4.5 5.5-4.5 12.9 2.6 15.8 6.9c0 0-1.4-3.2.4-4.4 1.8-1.2 10.2 1 15.6 9s1.1 14.9-5.2 19.9c-6.3 5-36.2 11.6-48.8 10.9" clip-path="url(#b)" fill="#53606c"/><g class="st53" clip-path="url(#b)"><path class="st36" d="M459.5 878.7c-11.1.6-22.1 3.3-32.9 6.1-4.1 1.1-8.2 2.2-12.4 2-.4 0-.8-.1-1.3-.1 1 1.6 2.5 3.2 4.6 4.7l15.6-2.1c11.3.7 37-4.7 46.4-9.4-6.5-1.1-13.3-1.5-20-1.2z" fill="#38434f"/></g><path class="st59" d="M417.5 890.8c-3.5-2.5-5.3-5.5-5.5-8.8-.2-3.2 1.2-6.7 4-9.9-.4 1.6-.2 3.2.5 4.7 1.2 2.5 3.6 4 6.3 4 2.1 0 4.3-.9 6.3-2.6 7.2-6 19.4-10.4 29.1-10.4 1.9 0 3.7.2 5.3.5 7.7 1.6 12.3 3.7 13.8 6.7.8 1.6.8 3.3 0 5.4-10.1 4.3-31.2 8.6-42.1 8.6-.7 0-1.4 0-2.1-.1-1.4-.1-2.8-.1-4-.1-7.5-.1-10.6 1.4-11.6 2z" clip-path="url(#b)" fill="#38434f"/><path class="st57" d="M471.2 854c2.2 3.4 1.1 6.7 1.1 6.7" clip-path="url(#b)" stroke-miterlimit="10" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><path d="M489.6 967.9c34.6 23.5 68.7 8.7 80.2-2.4 11.4-11.1 20.8-28.5 20-45.9-.8-17.4-8.5-32.9-19.5-42.8-10.7-9.6-12.4-8.7-13.2-7.7-.8 1-1.4 1.1 5 12.2s13.4 24.6 9.5 40.2c-3.9 15.6-15.6 29.9-29.6 34-14 4-21.6-1.4-26.1-3.2 0 0 1 4-3.4 4.3-4.3.3-11.1-2.9-11.1-2.9s2.7 3 .1 5-5.9-.9-5.9-.9 2.5 2.2 1.5 3.3c-1 1.1-2.3.8-4.2-.6 0 0 3.2 3.6 1 5.5s-4.3 1.9-4.3 1.9z" clip-path="url(#b)" stroke-miterlimit="10" fill="#b3becd" stroke="#000" stroke-width="1.60000002" stroke-linecap="round" stroke-linejoin="round"/><path d="M528.6 979.9c-13.1 0-25.8-3.9-37.6-11.7.9-.2 2.1-.7 3.3-1.8 1.4-1.3 1-3 .3-4.3.4.1.8.2 1.1.2.7 0 1.3-.3 1.8-.8.3-.3.4-.6.4-1 0-.3-.1-.6-.2-.9.6.2 1.2.4 1.9.4.9 0 1.7-.3 2.4-.8.3-.2.5-.5.7-.8 12.6 6.2 22.6 9.1 31.5 9.1 7.8 0 14.7-2.3 21.1-6.9 16.8-12.3 21.3-21.3 24.6-27.9l.3-.6c2.3-4.5 4.2-6.5 6.4-6.5.9 0 1.7.3 2.7.9-1.4 13.6-8.7 28-19.6 38.6-7.9 7.4-23.4 14.8-41.1 14.8z" clip-path="url(#b)" fill="#92a1b5"/><path class="st57" d="M489.6 967.9c-3.9.1-6.7-.9-6.7-.9" clip-path="url(#b)" stroke-miterlimit="10" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><path d="M484 908.3c-2.2.9-2.1 3.2.1 6.7 2.1 3.5 4.5 7.9 5.9 10.3 1.4 2.4 2.6 3.5 4 2.8 1.4-.6 1.8-2 .4-4.9-1.4-2.9-5.8-11-7.2-12.7-1.6-1.7-2.4-2.5-3.2-2.2z" clip-path="url(#b)" stroke-miterlimit="10" fill="#38434f" stroke="#000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path class="st57" d="M525.9 907.8c2.5-3.7 4.8-5.2 4.8-5.2m3.8 11.9c1.5-3.1 3.7-5.6 3.7-5.6m11.9 13.3c1-3.9 2.7-5.6 2.7-5.6m10.7 9c.6-4.6.9-6.6.9-6.6" clip-path="url(#b)" stroke-miterlimit="10" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><g class="st53" clip-path="url(#b)"><path class="st49" d="M504.9 862.2c.8-.4 1.5-.8 2.2-1.1" fill="none" stroke="#e3e5e5" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M519.8 856.2c13.6-3.5 22.1.5 28.2 3.3 5.3 2.4 11.6 4.7 17.6 5.3" fill="none" stroke="#e3e5e5" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="4.5286,13.5857"/><path class="st49" d="M572.4 864.5c.8-.2 1.6-.4 2.4-.7" fill="none" stroke="#e3e5e5" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g class="st53" clip-path="url(#b)"><path d="M491.2 946.7c-4.1 1.1-6.1 1.8-10 3.5-2.1.9-4.6.1-5.6-1.9s0-4.6 2.3-5.6c4.3-1.9 6.6-2.7 11.2-3.9 2.4-.6 4.8.8 5.3 2.9.4 2.2-1 4.4-3.2 5z" fill="#505762"/></g><path class="st57" d="M515.8 952.3c-.2-2.3-1.7-3.3-1.7-3.3" clip-path="url(#b)" stroke-miterlimit="10" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><g class="st53" clip-path="url(#b)"><path d="M454.5 887.5c-3.5.7-7.1 1.3-10.6 1.7-3.6.4-7.2.8-10.8.6-.3 0-.5-.2-.5-.5s.2-.5.5-.5c3.5.2 7.1-.1 10.6-.4 3.5-.4 7.1-.9 10.6-1.6.2 0 .4.1.4.3.1.2 0 .4-.2.4z"/></g><g class="st53" clip-path="url(#b)"><path d="M417.2 891.8c-2.2-1.6-4.3-3.6-5.4-6.2l-.4-1-.2-1-.1-.5v-.5c0-.4-.1-.7-.1-1.1 0-1.4.2-2.8.7-4.1.9-2.6 2.6-4.9 4.4-6.9 1.9-2 4-3.6 6.2-5.2s4.6-2.9 7-4.1c2.4-1.2 5-2.2 7.6-2.9 5.2-1.5 10.7-1.9 16.1-1.4l-.6.5c-.2-1.3-.7-2.7-1.4-3.8-.4-.6-.8-1.1-1.4-1.5-.5-.5-1.2-1-1.5-1.9-.1-.2-.1-.5-.1-.7.1-.2.1-.5.2-.7.2-.4.6-.7.9-.9.7-.5 1.4-.7 2.2-.9 1.5-.3 3-.4 4.4-.4 2.9.1 5.9.7 8.6 1.8 2.7 1.1 5.3 2.8 7.1 5.3l-.9.5c-.3-.7-.5-1.3-.5-2.1-.1-.7 0-1.5.4-2.3.3-.3.5-.7 1-.8.2-.1.4-.1.6-.2.2 0 .4-.1.6-.1.7 0 1.4.1 2.1.2 1.4.3 2.6.8 3.9 1.4 1.2.6 2.4 1.3 3.5 2.1 2.2 1.6 4.1 3.6 5.6 5.9.7 1.2 1.4 2.4 1.8 3.7.3.6.4 1.3.5 2 .1.3.1.7.1 1v1c-.1 2.8-1.3 5.5-3 7.6-1.7 2.2-3.6 4-5.9 5.6-2.4 1.4-4.9 2.3-7.4 3.2-2.5.8-5.1 1.5-7.8 1.9 1.2-.5 2.5-.9 3.7-1.4l3.7-1.3c2.5-.9 5-1.8 7.2-3.2 2.1-1.5 4-3.3 5.6-5.4 1.5-2.1 2.6-4.5 2.7-7 .1-2.5-.8-5-2.2-7.1-1.4-2.2-3.2-4.1-5.3-5.6-2.1-1.5-4.4-2.8-6.9-3.4-.6-.1-1.2-.2-1.8-.2-.6 0-1 .1-1.3.5-.5.8-.2 2.3.2 3.4.1.3 0 .5-.3.6-.2.1-.4 0-.6-.2-1.6-2.3-4-3.8-6.6-4.9-2.6-1.1-5.4-1.7-8.2-1.7-1.4 0-2.8.1-4.2.4-.7.2-1.3.4-1.8.7-.5.4-.8.8-.7 1.3.1.5.6.9 1.2 1.4.6.5 1.1 1.1 1.5 1.7.9 1.3 1.4 2.7 1.6 4.3 0 .3-.2.5-.4.5h-.1c-5.3-.5-10.7-.1-15.8 1.4-2.5.7-5 1.7-7.4 2.8-2.4 1.2-4.7 2.5-6.8 4.1-2.2 1.5-4.3 3.2-6.1 5.1-1.8 1.9-3.4 4.1-4.2 6.5-.4 1.2-.6 2.5-.7 3.8 0 .3.1.6.1 1v.5l.1.5.2.9.4.9c1 2.4 2.9 4.3 5 5.8.2.2.3.5.1.7-.2.2-.4.3-.7.1z"/></g><g class="st53" clip-path="url(#b)"><path class="st36" d="M615.1 863.2c2 1.2 2.3 1.8 2.3 1.8" fill="#38434f"/><path d="M615.3 862.9l1.2.9c.2.2.4.3.6.5.2.2.3.4.5.6 0 .1 0 .2-.1.2h-.2l-.5-.5c-.2-.1-.4-.3-.6-.4-.4-.3-.8-.5-1.3-.7-.2-.1-.2-.3-.1-.5s.2-.2.5-.1c-.1 0-.1 0 0 0z"/></g></g></svg>
diff --git a/app/javascript/mastodon/actions/announcements.js b/app/javascript/mastodon/actions/announcements.js
new file mode 100644
index 000000000..1bdea909f
--- /dev/null
+++ b/app/javascript/mastodon/actions/announcements.js
@@ -0,0 +1,180 @@
+import api from '../api';
+import { normalizeAnnouncement } from './importer/normalizer';
+
+export const ANNOUNCEMENTS_FETCH_REQUEST = 'ANNOUNCEMENTS_FETCH_REQUEST';
+export const ANNOUNCEMENTS_FETCH_SUCCESS = 'ANNOUNCEMENTS_FETCH_SUCCESS';
+export const ANNOUNCEMENTS_FETCH_FAIL    = 'ANNOUNCEMENTS_FETCH_FAIL';
+export const ANNOUNCEMENTS_UPDATE        = 'ANNOUNCEMENTS_UPDATE';
+export const ANNOUNCEMENTS_DELETE        = 'ANNOUNCEMENTS_DELETE';
+
+export const ANNOUNCEMENTS_DISMISS_REQUEST = 'ANNOUNCEMENTS_DISMISS_REQUEST';
+export const ANNOUNCEMENTS_DISMISS_SUCCESS = 'ANNOUNCEMENTS_DISMISS_SUCCESS';
+export const ANNOUNCEMENTS_DISMISS_FAIL    = 'ANNOUNCEMENTS_DISMISS_FAIL';
+
+export const ANNOUNCEMENTS_REACTION_ADD_REQUEST = 'ANNOUNCEMENTS_REACTION_ADD_REQUEST';
+export const ANNOUNCEMENTS_REACTION_ADD_SUCCESS = 'ANNOUNCEMENTS_REACTION_ADD_SUCCESS';
+export const ANNOUNCEMENTS_REACTION_ADD_FAIL    = 'ANNOUNCEMENTS_REACTION_ADD_FAIL';
+
+export const ANNOUNCEMENTS_REACTION_REMOVE_REQUEST = 'ANNOUNCEMENTS_REACTION_REMOVE_REQUEST';
+export const ANNOUNCEMENTS_REACTION_REMOVE_SUCCESS = 'ANNOUNCEMENTS_REACTION_REMOVE_SUCCESS';
+export const ANNOUNCEMENTS_REACTION_REMOVE_FAIL    = 'ANNOUNCEMENTS_REACTION_REMOVE_FAIL';
+
+export const ANNOUNCEMENTS_REACTION_UPDATE = 'ANNOUNCEMENTS_REACTION_UPDATE';
+
+export const ANNOUNCEMENTS_TOGGLE_SHOW = 'ANNOUNCEMENTS_TOGGLE_SHOW';
+
+const noOp = () => {};
+
+export const fetchAnnouncements = (done = noOp) => (dispatch, getState) => {
+  dispatch(fetchAnnouncementsRequest());
+
+  api(getState).get('/api/v1/announcements').then(response => {
+    dispatch(fetchAnnouncementsSuccess(response.data.map(x => normalizeAnnouncement(x))));
+  }).catch(error => {
+    dispatch(fetchAnnouncementsFail(error));
+  }).finally(() => {
+    done();
+  });
+};
+
+export const fetchAnnouncementsRequest = () => ({
+  type: ANNOUNCEMENTS_FETCH_REQUEST,
+  skipLoading: true,
+});
+
+export const fetchAnnouncementsSuccess = announcements => ({
+  type: ANNOUNCEMENTS_FETCH_SUCCESS,
+  announcements,
+  skipLoading: true,
+});
+
+export const fetchAnnouncementsFail= error => ({
+  type: ANNOUNCEMENTS_FETCH_FAIL,
+  error,
+  skipLoading: true,
+  skipAlert: true,
+});
+
+export const updateAnnouncements = announcement => ({
+  type: ANNOUNCEMENTS_UPDATE,
+  announcement: normalizeAnnouncement(announcement),
+});
+
+export const dismissAnnouncement = announcementId => (dispatch, getState) => {
+  dispatch(dismissAnnouncementRequest(announcementId));
+
+  api(getState).post(`/api/v1/announcements/${announcementId}/dismiss`).then(() => {
+    dispatch(dismissAnnouncementSuccess(announcementId));
+  }).catch(error => {
+    dispatch(dismissAnnouncementFail(announcementId, error));
+  });
+};
+
+export const dismissAnnouncementRequest = announcementId => ({
+  type: ANNOUNCEMENTS_DISMISS_REQUEST,
+  id: announcementId,
+});
+
+export const dismissAnnouncementSuccess = announcementId => ({
+  type: ANNOUNCEMENTS_DISMISS_SUCCESS,
+  id: announcementId,
+});
+
+export const dismissAnnouncementFail = (announcementId, error) => ({
+  type: ANNOUNCEMENTS_DISMISS_FAIL,
+  id: announcementId,
+  error,
+});
+
+export const addReaction = (announcementId, name) => (dispatch, getState) => {
+  const announcement = getState().getIn(['announcements', 'items']).find(x => x.get('id') === announcementId);
+
+  let alreadyAdded = false;
+
+  if (announcement) {
+    const reaction = announcement.get('reactions').find(x => x.get('name') === name);
+    if (reaction && reaction.get('me')) {
+      alreadyAdded = true;
+    }
+  }
+
+  if (!alreadyAdded) {
+    dispatch(addReactionRequest(announcementId, name, alreadyAdded));
+  }
+
+  api(getState).put(`/api/v1/announcements/${announcementId}/reactions/${name}`).then(() => {
+    dispatch(addReactionSuccess(announcementId, name, alreadyAdded));
+  }).catch(err => {
+    if (!alreadyAdded) {
+      dispatch(addReactionFail(announcementId, name, err));
+    }
+  });
+};
+
+export const addReactionRequest = (announcementId, name) => ({
+  type: ANNOUNCEMENTS_REACTION_ADD_REQUEST,
+  id: announcementId,
+  name,
+  skipLoading: true,
+});
+
+export const addReactionSuccess = (announcementId, name) => ({
+  type: ANNOUNCEMENTS_REACTION_ADD_SUCCESS,
+  id: announcementId,
+  name,
+  skipLoading: true,
+});
+
+export const addReactionFail = (announcementId, name, error) => ({
+  type: ANNOUNCEMENTS_REACTION_ADD_FAIL,
+  id: announcementId,
+  name,
+  error,
+  skipLoading: true,
+});
+
+export const removeReaction = (announcementId, name) => (dispatch, getState) => {
+  dispatch(removeReactionRequest(announcementId, name));
+
+  api(getState).delete(`/api/v1/announcements/${announcementId}/reactions/${name}`).then(() => {
+    dispatch(removeReactionSuccess(announcementId, name));
+  }).catch(err => {
+    dispatch(removeReactionFail(announcementId, name, err));
+  });
+};
+
+export const removeReactionRequest = (announcementId, name) => ({
+  type: ANNOUNCEMENTS_REACTION_REMOVE_REQUEST,
+  id: announcementId,
+  name,
+  skipLoading: true,
+});
+
+export const removeReactionSuccess = (announcementId, name) => ({
+  type: ANNOUNCEMENTS_REACTION_REMOVE_SUCCESS,
+  id: announcementId,
+  name,
+  skipLoading: true,
+});
+
+export const removeReactionFail = (announcementId, name, error) => ({
+  type: ANNOUNCEMENTS_REACTION_REMOVE_FAIL,
+  id: announcementId,
+  name,
+  error,
+  skipLoading: true,
+});
+
+export const updateReaction = reaction => ({
+  type: ANNOUNCEMENTS_REACTION_UPDATE,
+  reaction,
+});
+
+export const toggleShowAnnouncements = () => ({
+  type: ANNOUNCEMENTS_TOGGLE_SHOW,
+});
+
+export const deleteAnnouncement = id => ({
+  type: ANNOUNCEMENTS_DELETE,
+  id,
+});
diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js
index 78f321da4..f7cbe4c1c 100644
--- a/app/javascript/mastodon/actions/importer/normalizer.js
+++ b/app/javascript/mastodon/actions/importer/normalizer.js
@@ -76,7 +76,6 @@ export function normalizeStatus(status, normalOldStatus) {
 
 export function normalizePoll(poll) {
   const normalPoll = { ...poll };
-
   const emojiMap = makeEmojiMap(normalPoll);
 
   normalPoll.options = poll.options.map((option, index) => ({
@@ -87,3 +86,12 @@ export function normalizePoll(poll) {
 
   return normalPoll;
 }
+
+export function normalizeAnnouncement(announcement) {
+  const normalAnnouncement = { ...announcement };
+  const emojiMap = makeEmojiMap(normalAnnouncement);
+
+  normalAnnouncement.contentHtml = emojify(normalAnnouncement.content, emojiMap);
+
+  return normalAnnouncement;
+}
diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js
index 798f9b37e..8a066b896 100644
--- a/app/javascript/mastodon/actions/notifications.js
+++ b/app/javascript/mastodon/actions/notifications.js
@@ -157,9 +157,9 @@ export function expandNotifications({ maxId } = {}, done = noOp) {
 
       dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null, isLoadingMore, isLoadingRecent, isLoadingRecent && preferPendingItems));
       fetchRelatedRelationships(dispatch, response.data);
-      done();
     }).catch(error => {
       dispatch(expandNotificationsFail(error, isLoadingMore));
+    }).finally(() => {
       done();
     });
   };
@@ -188,6 +188,7 @@ export function expandNotificationsFail(error, isLoadingMore) {
     type: NOTIFICATIONS_EXPAND_FAIL,
     error,
     skipLoading: !isLoadingMore,
+    skipAlert: !isLoadingMore,
   };
 };
 
diff --git a/app/javascript/mastodon/actions/statuses.js b/app/javascript/mastodon/actions/statuses.js
index 06a19afc3..5640201c6 100644
--- a/app/javascript/mastodon/actions/statuses.js
+++ b/app/javascript/mastodon/actions/statuses.js
@@ -26,8 +26,9 @@ export const STATUS_UNMUTE_REQUEST = 'STATUS_UNMUTE_REQUEST';
 export const STATUS_UNMUTE_SUCCESS = 'STATUS_UNMUTE_SUCCESS';
 export const STATUS_UNMUTE_FAIL    = 'STATUS_UNMUTE_FAIL';
 
-export const STATUS_REVEAL = 'STATUS_REVEAL';
-export const STATUS_HIDE   = 'STATUS_HIDE';
+export const STATUS_REVEAL   = 'STATUS_REVEAL';
+export const STATUS_HIDE     = 'STATUS_HIDE';
+export const STATUS_COLLAPSE = 'STATUS_COLLAPSE';
 
 export const REDRAFT = 'REDRAFT';
 
@@ -320,3 +321,11 @@ export function revealStatus(ids) {
     ids,
   };
 };
+
+export function toggleStatusCollapse(id, isCollapsed) {
+  return {
+    type: STATUS_COLLAPSE,
+    id,
+    isCollapsed,
+  };
+}
diff --git a/app/javascript/mastodon/actions/streaming.js b/app/javascript/mastodon/actions/streaming.js
index c678e9393..79b08bdda 100644
--- a/app/javascript/mastodon/actions/streaming.js
+++ b/app/javascript/mastodon/actions/streaming.js
@@ -8,6 +8,12 @@ import {
 } from './timelines';
 import { updateNotifications, expandNotifications } from './notifications';
 import { updateConversations } from './conversations';
+import {
+  fetchAnnouncements,
+  updateAnnouncements,
+  updateReaction as updateAnnouncementsReaction,
+  deleteAnnouncement,
+} from './announcements';
 import { fetchFilters } from './filters';
 import { getLocale } from '../locales';
 
@@ -44,6 +50,15 @@ export function connectTimelineStream (timelineId, path, pollingRefresh = null,
         case 'filters_changed':
           dispatch(fetchFilters());
           break;
+        case 'announcement':
+          dispatch(updateAnnouncements(JSON.parse(data.payload)));
+          break;
+        case 'announcement.reaction':
+          dispatch(updateAnnouncementsReaction(JSON.parse(data.payload)));
+          break;
+        case 'announcement.delete':
+          dispatch(deleteAnnouncement(data.payload));
+          break;
         }
       },
     };
@@ -51,7 +66,9 @@ export function connectTimelineStream (timelineId, path, pollingRefresh = null,
 }
 
 const refreshHomeTimelineAndNotification = (dispatch, done) => {
-  dispatch(expandHomeTimeline({}, () => dispatch(expandNotifications({}, done))));
+  dispatch(expandHomeTimeline({}, () =>
+    dispatch(expandNotifications({}, () =>
+      dispatch(fetchAnnouncements(done))))));
 };
 
 export const connectUserStream      = () => connectTimelineStream('home', 'user', refreshHomeTimelineAndNotification);
diff --git a/app/javascript/mastodon/actions/timelines.js b/app/javascript/mastodon/actions/timelines.js
index bc2ac5e82..054668655 100644
--- a/app/javascript/mastodon/actions/timelines.js
+++ b/app/javascript/mastodon/actions/timelines.js
@@ -98,9 +98,9 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
       const next = getLinks(response).refs.find(link => link.rel === 'next');
       dispatch(importFetchedStatuses(response.data));
       dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.status === 206, isLoadingRecent, isLoadingMore, isLoadingRecent && preferPendingItems));
-      done();
     }).catch(error => {
       dispatch(expandTimelineFail(timelineId, error, isLoadingMore));
+    }).finally(() => {
       done();
     });
   };
diff --git a/app/javascript/mastodon/components/animated_number.js b/app/javascript/mastodon/components/animated_number.js
new file mode 100644
index 000000000..f3127c88e
--- /dev/null
+++ b/app/javascript/mastodon/components/animated_number.js
@@ -0,0 +1,65 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { FormattedNumber } from 'react-intl';
+import TransitionMotion from 'react-motion/lib/TransitionMotion';
+import spring from 'react-motion/lib/spring';
+import { reduceMotion } from 'mastodon/initial_state';
+
+export default class AnimatedNumber extends React.PureComponent {
+
+  static propTypes = {
+    value: PropTypes.number.isRequired,
+  };
+
+  state = {
+    direction: 1,
+  };
+
+  componentWillReceiveProps (nextProps) {
+    if (nextProps.value > this.props.value) {
+      this.setState({ direction: 1 });
+    } else if (nextProps.value < this.props.value) {
+      this.setState({ direction: -1 });
+    }
+  }
+
+  willEnter = () => {
+    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 } = this.props;
+    const { direction } = this.state;
+
+    if (reduceMotion) {
+      return <FormattedNumber value={value} />;
+    }
+
+    const styles = [{
+      key: `${value}`,
+      data: value,
+      style: { y: spring(0, { damping: 35, stiffness: 400 }) },
+    }];
+
+    return (
+      <TransitionMotion styles={styles} willEnter={this.willEnter} willLeave={this.willLeave}>
+        {items => (
+          <span className='animated-number'>
+            {items.map(({ key, data, style }) => (
+              <span key={key} style={{ position: (direction * style.y) > 0 ? 'absolute' : 'static', transform: `translateY(${style.y * 100}%)` }}><FormattedNumber value={data} /></span>
+            ))}
+          </span>
+        )}
+      </TransitionMotion>
+    );
+  }
+
+}
diff --git a/app/javascript/mastodon/components/column_header.js b/app/javascript/mastodon/components/column_header.js
index 0038995c8..ea82f9ef9 100644
--- a/app/javascript/mastodon/components/column_header.js
+++ b/app/javascript/mastodon/components/column_header.js
@@ -33,6 +33,7 @@ class ColumnHeader extends React.PureComponent {
     onPin: PropTypes.func,
     onMove: PropTypes.func,
     onClick: PropTypes.func,
+    appendContent: PropTypes.node,
   };
 
   state = {
@@ -81,7 +82,7 @@ class ColumnHeader extends React.PureComponent {
   }
 
   render () {
-    const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder } = this.props;
+    const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder, appendContent } = this.props;
     const { collapsed, animating } = this.state;
 
     const wrapperClassName = classNames('column-header__wrapper', {
@@ -172,6 +173,8 @@ class ColumnHeader extends React.PureComponent {
             {(!collapsed || animating) && collapsedContent}
           </div>
         </div>
+
+        {appendContent}
       </div>
     );
 
diff --git a/app/javascript/mastodon/components/error_boundary.js b/app/javascript/mastodon/components/error_boundary.js
index 800b1c270..4e1c882e2 100644
--- a/app/javascript/mastodon/components/error_boundary.js
+++ b/app/javascript/mastodon/components/error_boundary.js
@@ -58,7 +58,7 @@ export default class ErrorBoundary extends React.PureComponent {
         <div>
           <p className='error-boundary__error'><FormattedMessage id='error.unexpected_crash.explanation' defaultMessage='Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.' /></p>
           <p><FormattedMessage id='error.unexpected_crash.next_steps' defaultMessage='Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.' /></p>
-          <p className='error-boundary__footer'>Mastodon v{version} · <a href={source_url} rel='noopener noreferrer' target='_blank'><FormattedMessage id='errors.unexpected_crash.report_issue' defaultMessage='Report issue' /></a> · <button onClick={this.handleCopyStackTrace} className={copied && 'copied'}><FormattedMessage id='errors.unexpected_crash.copy_stacktrace' defaultMessage='Copy stacktrace to clipboard' /></button></p>
+          <p className='error-boundary__footer'>Mastodon v{version} · <a href={source_url} rel='noopener noreferrer' target='_blank'><FormattedMessage id='errors.unexpected_crash.report_issue' defaultMessage='Report issue' /></a> · <button onClick={this.handleCopyStackTrace} className={copied ? 'copied' : ''}><FormattedMessage id='errors.unexpected_crash.copy_stacktrace' defaultMessage='Copy stacktrace to clipboard' /></button></p>
         </div>
       </div>
     );
diff --git a/app/javascript/mastodon/components/media_gallery.js b/app/javascript/mastodon/components/media_gallery.js
index 12b7e5b66..cfe164a50 100644
--- a/app/javascript/mastodon/components/media_gallery.js
+++ b/app/javascript/mastodon/components/media_gallery.js
@@ -23,6 +23,7 @@ class Item extends React.PureComponent {
     onClick: PropTypes.func.isRequired,
     displayWidth: PropTypes.number,
     visible: PropTypes.bool.isRequired,
+    autoplay: PropTypes.bool,
   };
 
   static defaultProps = {
@@ -48,9 +49,13 @@ class Item extends React.PureComponent {
     }
   }
 
+  getAutoPlay() {
+    return this.props.autoplay || autoPlayGif;
+  }
+
   hoverToPlay () {
     const { attachment } = this.props;
-    return !autoPlayGif && attachment.get('type') === 'gifv';
+    return !this.getAutoPlay() && attachment.get('type') === 'gifv';
   }
 
   handleClick = (e) => {
@@ -201,7 +206,7 @@ class Item extends React.PureComponent {
         </a>
       );
     } else if (attachment.get('type') === 'gifv') {
-      const autoPlay = !isIOS() && autoPlayGif;
+      const autoPlay = !isIOS() && this.getAutoPlay();
 
       thumbnail = (
         <div className={classNames('media-gallery__gifv', { autoplay: autoPlay })}>
@@ -248,6 +253,7 @@ class MediaGallery extends React.PureComponent {
     defaultWidth: PropTypes.number,
     cacheWidth: PropTypes.func,
     visible: PropTypes.bool,
+    autoplay: PropTypes.bool,
     onToggleVisibility: PropTypes.func,
   };
 
@@ -297,7 +303,7 @@ class MediaGallery extends React.PureComponent {
   }
 
   render () {
-    const { media, intl, sensitive, height, defaultWidth, standalone } = this.props;
+    const { media, intl, sensitive, height, defaultWidth, standalone, autoplay } = this.props;
     const { visible } = this.state;
 
     const width = this.state.width || defaultWidth;
@@ -320,9 +326,9 @@ class MediaGallery extends React.PureComponent {
     const uncached = media.every(attachment => attachment.get('type') === 'unknown');
 
     if (standalone && this.isFullSizeEligible()) {
-      children = <Item standalone onClick={this.handleClick} attachment={media.get(0)} displayWidth={width} visible={visible} />;
+      children = <Item standalone autoplay={autoplay} onClick={this.handleClick} attachment={media.get(0)} displayWidth={width} visible={visible} />;
     } else {
-      children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} 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} size={size} displayWidth={width} visible={visible || uncached} />);
     }
 
     if (uncached) {
diff --git a/app/javascript/mastodon/components/relative_timestamp.js b/app/javascript/mastodon/components/relative_timestamp.js
index aa4b73cfe..711181dcd 100644
--- a/app/javascript/mastodon/components/relative_timestamp.js
+++ b/app/javascript/mastodon/components/relative_timestamp.js
@@ -3,6 +3,7 @@ import { injectIntl, defineMessages } from 'react-intl';
 import PropTypes from 'prop-types';
 
 const messages = defineMessages({
+  today: { id: 'relative_time.today', defaultMessage: 'today' },
   just_now: { id: 'relative_time.just_now', defaultMessage: 'now' },
   seconds: { id: 'relative_time.seconds', defaultMessage: '{number}s' },
   minutes: { id: 'relative_time.minutes', defaultMessage: '{number}m' },
@@ -65,12 +66,14 @@ const getUnitDelay = units => {
   }
 };
 
-export const timeAgoString = (intl, date, now, year) => {
+export const timeAgoString = (intl, date, now, year, timeGiven = true) => {
   const delta = now - date.getTime();
 
   let relativeTime;
 
-  if (delta < 10 * SECOND) {
+  if (delta < DAY && !timeGiven) {
+    relativeTime = intl.formatMessage(messages.today);
+  } else if (delta < 10 * SECOND) {
     relativeTime = intl.formatMessage(messages.just_now);
   } else if (delta < 7 * DAY) {
     if (delta < MINUTE) {
@@ -91,12 +94,14 @@ export const timeAgoString = (intl, date, now, year) => {
   return relativeTime;
 };
 
-const timeRemainingString = (intl, date, now) => {
+const timeRemainingString = (intl, date, now, timeGiven = true) => {
   const delta = date.getTime() - now;
 
   let relativeTime;
 
-  if (delta < 10 * SECOND) {
+  if (delta < DAY && !timeGiven) {
+    relativeTime = intl.formatMessage(messages.today);
+  } else if (delta < 10 * SECOND) {
     relativeTime = intl.formatMessage(messages.moments_remaining);
   } else if (delta < MINUTE) {
     relativeTime = intl.formatMessage(messages.seconds_remaining, { number: Math.floor(delta / SECOND) });
@@ -173,8 +178,9 @@ class RelativeTimestamp extends React.Component {
   render () {
     const { timestamp, intl, year, futureDate } = this.props;
 
+    const timeGiven    = timestamp.includes('T');
     const date         = new Date(timestamp);
-    const relativeTime = futureDate ? timeRemainingString(intl, date, this.state.now) : timeAgoString(intl, date, this.state.now, year);
+    const relativeTime = futureDate ? timeRemainingString(intl, date, this.state.now, timeGiven) : timeAgoString(intl, date, this.state.now, year, timeGiven);
 
     return (
       <time dateTime={timestamp} title={intl.formatDate(date, dateFormatOptions)}>
diff --git a/app/javascript/mastodon/components/scrollable_list.js b/app/javascript/mastodon/components/scrollable_list.js
index 421756803..3a490e78e 100644
--- a/app/javascript/mastodon/components/scrollable_list.js
+++ b/app/javascript/mastodon/components/scrollable_list.js
@@ -208,10 +208,13 @@ export default class ScrollableList extends PureComponent {
   }
 
   attachIntersectionObserver () {
-    this.intersectionObserverWrapper.connect({
+    let nodeOptions = {
       root: this.node,
       rootMargin: '300% 0px',
-    });
+    };
+
+    this.intersectionObserverWrapper
+      .connect(this.props.bindToDocument ? {} : nodeOptions);
   }
 
   detachIntersectionObserver () {
@@ -293,7 +296,7 @@ export default class ScrollableList extends PureComponent {
           </div>
         </div>
       );
-    } else if (isLoading || childrenCount > 0 || hasMore || !emptyMessage) {
+    } else if (isLoading || childrenCount > 0 || numPending > 0 || hasMore || !emptyMessage) {
       scrollableArea = (
         <div className={classNames('scrollable', { fullscreen })} ref={this.setRef} onMouseMove={this.handleMouseMove}>
           <div role='feed' className='item-list'>
diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js
index e120278a0..0dc00cb98 100644
--- a/app/javascript/mastodon/components/status.js
+++ b/app/javascript/mastodon/components/status.js
@@ -76,6 +76,7 @@ class Status extends ImmutablePureComponent {
     onEmbed: PropTypes.func,
     onHeightChange: PropTypes.func,
     onToggleHidden: PropTypes.func,
+    onToggleCollapsed: PropTypes.func,
     muted: PropTypes.bool,
     hidden: PropTypes.bool,
     unread: PropTypes.bool,
@@ -102,19 +103,6 @@ class Status extends ImmutablePureComponent {
     statusId: undefined,
   };
 
-  // Track height changes we know about to compensate scrolling
-  componentDidMount () {
-    this.didShowCard = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card');
-  }
-
-  getSnapshotBeforeUpdate () {
-    if (this.props.getScrollPosition) {
-      return this.props.getScrollPosition();
-    } else {
-      return null;
-    }
-  }
-
   static getDerivedStateFromProps(nextProps, prevState) {
     if (nextProps.status && nextProps.status.get('id') !== prevState.statusId) {
       return {
@@ -126,32 +114,6 @@ class Status extends ImmutablePureComponent {
     }
   }
 
-  // Compensate height changes
-  componentDidUpdate (prevProps, prevState, snapshot) {
-    const doShowCard  = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card');
-
-    if (doShowCard && !this.didShowCard) {
-      this.didShowCard = true;
-
-      if (snapshot !== null && this.props.updateScrollBottom) {
-        if (this.node && this.node.offsetTop < snapshot.top) {
-          this.props.updateScrollBottom(snapshot.height - snapshot.top);
-        }
-      }
-    }
-  }
-
-  componentWillUnmount() {
-    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);
-        });
-      }
-    }
-  }
-
   handleToggleMediaVisibility = () => {
     this.setState({ showMedia: !this.state.showMedia });
   }
@@ -196,7 +158,11 @@ class Status extends ImmutablePureComponent {
 
   handleExpandedToggle = () => {
     this.props.onToggleHidden(this._properStatus());
-  };
+  }
+
+  handleCollapsedToggle = isCollapsed => {
+    this.props.onToggleCollapsed(this._properStatus(), isCollapsed);
+  }
 
   renderLoadingMediaGallery () {
     return <div className='media-gallery' style={{ height: '110px' }} />;
@@ -466,7 +432,7 @@ class Status extends ImmutablePureComponent {
               </a>
             </div>
 
-            <StatusContent status={status} onClick={this.handleClick} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} collapsable />
+            <StatusContent status={status} onClick={this.handleClick} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} collapsable onCollapsedToggle={this.handleCollapsedToggle} />
 
             {media}
 
diff --git a/app/javascript/mastodon/components/status_content.js b/app/javascript/mastodon/components/status_content.js
index d13091325..5d921fd41 100644
--- a/app/javascript/mastodon/components/status_content.js
+++ b/app/javascript/mastodon/components/status_content.js
@@ -23,11 +23,11 @@ export default class StatusContent extends React.PureComponent {
     onExpandedToggle: PropTypes.func,
     onClick: PropTypes.func,
     collapsable: PropTypes.bool,
+    onCollapsedToggle: PropTypes.func,
   };
 
   state = {
     hidden: true,
-    collapsed: null, //  `collapsed: null` indicates that an element doesn't need collapsing, while `true` or `false` indicates that it does (and is/isn't).
   };
 
   _updateStatusLinks () {
@@ -62,14 +62,16 @@ export default class StatusContent extends React.PureComponent {
       link.setAttribute('rel', 'noopener noreferrer');
     }
 
-    if (
-      this.props.collapsable
-      && this.props.onClick
-      && this.state.collapsed === null
-      && node.clientHeight > MAX_HEIGHT
-      && this.props.status.get('spoiler_text').length === 0
-    ) {
-      this.setState({ collapsed: true });
+    if (this.props.status.get('collapsed', null) === null) {
+      let collapsed =
+          this.props.collapsable
+          && this.props.onClick
+          && node.clientHeight > MAX_HEIGHT
+          && this.props.status.get('spoiler_text').length === 0;
+
+      if(this.props.onCollapsedToggle) this.props.onCollapsedToggle(collapsed);
+
+      this.props.status.set('collapsed', collapsed);
     }
   }
 
@@ -178,6 +180,7 @@ export default class StatusContent extends React.PureComponent {
     }
 
     const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden;
+    const renderReadMore = this.props.onClick && status.get('collapsed');
 
     const content = { __html: status.get('contentHtml') };
     const spoilerContent = { __html: status.get('spoilerHtml') };
@@ -185,7 +188,7 @@ export default class StatusContent extends React.PureComponent {
     const classNames = classnames('status__content', {
       'status__content--with-action': this.props.onClick && this.context.router,
       'status__content--with-spoiler': status.get('spoiler_text').length > 0,
-      'status__content--collapsed': this.state.collapsed === true,
+      'status__content--collapsed': renderReadMore,
     });
 
     if (isRtl(status.get('search_index'))) {
@@ -237,7 +240,7 @@ export default class StatusContent extends React.PureComponent {
         </div>,
       ];
 
-      if (this.state.collapsed) {
+      if (renderReadMore) {
         output.push(readMoreButton);
       }
 
diff --git a/app/javascript/mastodon/containers/status_container.js b/app/javascript/mastodon/containers/status_container.js
index 35c16a20c..2ba3a3123 100644
--- a/app/javascript/mastodon/containers/status_container.js
+++ b/app/javascript/mastodon/containers/status_container.js
@@ -23,6 +23,7 @@ import {
   deleteStatus,
   hideStatus,
   revealStatus,
+  toggleStatusCollapse,
 } from '../actions/statuses';
 import {
   unmuteAccount,
@@ -190,6 +191,10 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
     }
   },
 
+  onToggleCollapsed (status, isCollapsed) {
+    dispatch(toggleStatusCollapse(status.get('id'), isCollapsed));
+  },
+
   onBlockDomain (domain) {
     dispatch(openModal('CONFIRM', {
       message: <FormattedMessage id='confirmations.domain_block.message' defaultMessage='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.' values={{ domain: <strong>{domain}</strong> }} />,
diff --git a/app/javascript/mastodon/features/account_timeline/index.js b/app/javascript/mastodon/features/account_timeline/index.js
index 8d0cbe5a1..37622d4c0 100644
--- a/app/javascript/mastodon/features/account_timeline/index.js
+++ b/app/javascript/mastodon/features/account_timeline/index.js
@@ -115,6 +115,7 @@ class AccountTimeline extends ImmutablePureComponent {
           shouldUpdateScroll={shouldUpdateScroll}
           emptyMessage={emptyMessage}
           bindToDocument={!multiColumn}
+          timelineId='account'
         />
       </Column>
     );
diff --git a/app/javascript/mastodon/features/audio/index.js b/app/javascript/mastodon/features/audio/index.js
index 1b4cdbb4f..fda5a074f 100644
--- a/app/javascript/mastodon/features/audio/index.js
+++ b/app/javascript/mastodon/features/audio/index.js
@@ -37,15 +37,14 @@ class Audio extends React.PureComponent {
     volume: 0.5,
   };
 
-  // hard coded in components.scss
-  // any way to get ::before values programatically?
-
-  volWidth = 50;
-
+  // Hard coded in components.scss
+  // Any way to get ::before values programatically?
+  volWidth  = 50;
   volOffset = 70;
 
   volHandleOffset = v => {
     const offset = v * this.volWidth + this.volOffset;
+
     return (offset > 110) ? 110 : offset;
   }
 
@@ -61,6 +60,8 @@ class Audio extends React.PureComponent {
     if (this.waveform) {
       this._updateWaveform();
     }
+
+    window.addEventListener('scroll', this.handleScroll);
   }
 
   componentDidUpdate (prevProps) {
@@ -70,6 +71,8 @@ class Audio extends React.PureComponent {
   }
 
   componentWillUnmount () {
+    window.removeEventListener('scroll', this.handleScroll);
+
     if (this.wavesurfer) {
       this.wavesurfer.destroy();
       this.wavesurfer = null;
@@ -128,16 +131,15 @@ class Audio extends React.PureComponent {
         this.loaded = true;
       }
 
-      this.wavesurfer.play();
-      this.setState({ paused: false });
+      this.setState({ paused: false }, () => this.wavesurfer.play());
     } else {
-      this.wavesurfer.pause();
-      this.setState({ paused: true });
+      this.setState({ paused: true }, () => this.wavesurfer.pause());
     }
   }
 
   toggleMute = () => {
-    this.wavesurfer.setMute(!this.state.muted);
+    const muted = !this.state.muted;
+    this.setState({ muted }, () => this.wavesurfer.setMute(muted));
   }
 
   handleVolumeMouseDown = e => {
@@ -176,6 +178,19 @@ class Audio extends React.PureComponent {
     }
   }, 60);
 
+  handleScroll = throttle(() => {
+    if (!this.waveform || !this.wavesurfer) {
+      return;
+    }
+
+    const { top, height } = this.waveform.getBoundingClientRect();
+    const inView = (top <= (window.innerHeight || document.documentElement.clientHeight)) && (top + height >= 0);
+
+    if (!this.state.paused && !inView) {
+      this.setState({ paused: true }, () => this.wavesurfer.pause());
+    }
+  }, 150, { trailing: true })
+
   render () {
     const { height, intl, alt, editable } = this.props;
     const { paused, muted, volume, currentTime } = this.state;
diff --git a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js
index e57c3c20c..582bb0d39 100644
--- a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js
+++ b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js
@@ -290,6 +290,7 @@ class EmojiPickerDropdown extends React.PureComponent {
     onPickEmoji: PropTypes.func.isRequired,
     onSkinTone: PropTypes.func.isRequired,
     skinTone: PropTypes.number.isRequired,
+    button: PropTypes.node,
   };
 
   state = {
@@ -350,18 +351,18 @@ class EmojiPickerDropdown extends React.PureComponent {
   }
 
   render () {
-    const { intl, onPickEmoji, onSkinTone, skinTone, frequentlyUsedEmojis } = this.props;
+    const { intl, onPickEmoji, onSkinTone, skinTone, frequentlyUsedEmojis, button } = this.props;
     const title = intl.formatMessage(messages.emoji);
     const { active, loading, placement } = this.state;
 
     return (
       <div className='emoji-picker-dropdown' onKeyDown={this.handleKeyDown}>
         <div ref={this.setTargetRef} className='emoji-button' title={title} aria-label={title} aria-expanded={active} role='button' onClick={this.onToggle} onKeyDown={this.onToggle} tabIndex={0}>
-          <img
+          {button || <img
             className={classNames('emojione', { 'pulse-loading': active && loading })}
             alt='🙂'
             src={`${assetHost}/emoji/1f602.svg`}
-          />
+          />}
         </div>
 
         <Overlay show={active} placement={placement} target={this.findTarget}>
diff --git a/app/javascript/mastodon/features/directory/components/account_card.js b/app/javascript/mastodon/features/directory/components/account_card.js
index 50ad74450..cb47d9db4 100644
--- a/app/javascript/mastodon/features/directory/components/account_card.js
+++ b/app/javascript/mastodon/features/directory/components/account_card.js
@@ -22,6 +22,7 @@ const messages = defineMessages({
   requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' },
   unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
   unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
+  unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' },
 });
 
 const makeMapStateToProps = () => {
diff --git a/app/javascript/mastodon/features/getting_started/components/announcements.js b/app/javascript/mastodon/features/getting_started/components/announcements.js
new file mode 100644
index 000000000..91cf6215e
--- /dev/null
+++ b/app/javascript/mastodon/features/getting_started/components/announcements.js
@@ -0,0 +1,436 @@
+import React from 'react';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import ReactSwipeableViews from 'react-swipeable-views';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+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 } 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';
+import AnimatedNumber from 'mastodon/components/animated_number';
+import TransitionMotion from 'react-motion/lib/TransitionMotion';
+import spring from 'react-motion/lib/spring';
+
+const messages = defineMessages({
+  close: { id: 'lightbox.close', defaultMessage: 'Close' },
+  previous: { id: 'lightbox.previous', defaultMessage: 'Previous' },
+  next: { id: 'lightbox.next', defaultMessage: 'Next' },
+});
+
+class Content extends ImmutablePureComponent {
+
+  static contextTypes = {
+    router: PropTypes.object,
+  };
+
+  static propTypes = {
+    announcement: ImmutablePropTypes.map.isRequired,
+  };
+
+  setRef = c => {
+    this.node = c;
+  }
+
+  componentDidMount () {
+    this._updateLinks();
+    this._updateEmojis();
+  }
+
+  componentDidUpdate () {
+    this._updateLinks();
+    this._updateEmojis();
+  }
+
+  _updateEmojis () {
+    const node = this.node;
+
+    if (!node || autoPlayGif) {
+      return;
+    }
+
+    const emojis = node.querySelectorAll('.custom-emoji');
+
+    for (var i = 0; i < emojis.length; i++) {
+      let emoji = emojis[i];
+
+      if (emoji.classList.contains('status-emoji')) {
+        continue;
+      }
+
+      emoji.classList.add('status-emoji');
+
+      emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false);
+      emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false);
+    }
+  }
+
+  _updateLinks () {
+    const node = this.node;
+
+    if (!node) {
+      return;
+    }
+
+    const links = node.querySelectorAll('a');
+
+    for (var i = 0; i < links.length; ++i) {
+      let link = links[i];
+
+      if (link.classList.contains('status-link')) {
+        continue;
+      }
+
+      link.classList.add('status-link');
+
+      let mention = this.props.announcement.get('mentions').find(item => link.href === item.get('url'));
+
+      if (mention) {
+        link.addEventListener('click', this.onMentionClick.bind(this, mention), false);
+        link.setAttribute('title', mention.get('acct'));
+      } else if (link.textContent[0] === '#' || (link.previousSibling && link.previousSibling.textContent && link.previousSibling.textContent[link.previousSibling.textContent.length - 1] === '#')) {
+        link.addEventListener('click', this.onHashtagClick.bind(this, link.text), false);
+      } else {
+        link.setAttribute('title', link.href);
+        link.classList.add('unhandled-link');
+      }
+
+      link.setAttribute('target', '_blank');
+      link.setAttribute('rel', 'noopener noreferrer');
+    }
+  }
+
+  onMentionClick = (mention, e) => {
+    if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
+      e.preventDefault();
+      this.context.router.history.push(`/accounts/${mention.get('id')}`);
+    }
+  }
+
+  onHashtagClick = (hashtag, e) => {
+    hashtag = hashtag.replace(/^#/, '');
+
+    if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
+      e.preventDefault();
+      this.context.router.history.push(`/timelines/tag/${hashtag}`);
+    }
+  }
+
+  handleEmojiMouseEnter = ({ target }) => {
+    target.src = target.getAttribute('data-original');
+  }
+
+  handleEmojiMouseLeave = ({ target }) => {
+    target.src = target.getAttribute('data-static');
+  }
+
+  render () {
+    const { announcement } = this.props;
+
+    return (
+      <div
+        className='announcements__item__content'
+        ref={this.setRef}
+        dangerouslySetInnerHTML={{ __html: announcement.get('contentHtml') }}
+      />
+    );
+  }
+
+}
+
+const assetHost = process.env.CDN_HOST || '';
+
+class Emoji extends React.PureComponent {
+
+  static propTypes = {
+    emoji: PropTypes.string.isRequired,
+    emojiMap: ImmutablePropTypes.map.isRequired,
+    hovered: PropTypes.bool.isRequired,
+  };
+
+  render () {
+    const { emoji, emojiMap, hovered } = this.props;
+
+    if (unicodeMapping[emoji]) {
+      const { filename, shortCode } = unicodeMapping[this.props.emoji];
+      const title = shortCode ? `:${shortCode}:` : '';
+
+      return (
+        <img
+          draggable='false'
+          className='emojione'
+          alt={emoji}
+          title={title}
+          src={`${assetHost}/emoji/${filename}.svg`}
+        />
+      );
+    } else if (emojiMap.get(emoji)) {
+      const filename  = (autoPlayGif || hovered) ? emojiMap.getIn([emoji, 'url']) : emojiMap.getIn([emoji, 'static_url']);
+      const shortCode = `:${emoji}:`;
+
+      return (
+        <img
+          draggable='false'
+          className='emojione custom-emoji'
+          alt={shortCode}
+          title={shortCode}
+          src={filename}
+        />
+      );
+    } else {
+      return null;
+    }
+  }
+
+}
+
+class Reaction extends ImmutablePureComponent {
+
+  static propTypes = {
+    announcementId: PropTypes.string.isRequired,
+    reaction: ImmutablePropTypes.map.isRequired,
+    addReaction: PropTypes.func.isRequired,
+    removeReaction: PropTypes.func.isRequired,
+    emojiMap: ImmutablePropTypes.map.isRequired,
+    style: PropTypes.object,
+  };
+
+  state = {
+    hovered: false,
+  };
+
+  handleClick = () => {
+    const { reaction, announcementId, addReaction, removeReaction } = this.props;
+
+    if (reaction.get('me')) {
+      removeReaction(announcementId, reaction.get('name'));
+    } else {
+      addReaction(announcementId, reaction.get('name'));
+    }
+  }
+
+  handleMouseEnter = () => this.setState({ hovered: true })
+
+  handleMouseLeave = () => this.setState({ hovered: false })
+
+  render () {
+    const { reaction } = this.props;
+
+    let shortCode = reaction.get('name');
+
+    if (unicodeMapping[shortCode]) {
+      shortCode = unicodeMapping[shortCode].shortCode;
+    }
+
+    return (
+      <button className={classNames('reactions-bar__item', { active: reaction.get('me') })} onClick={this.handleClick} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} title={`:${shortCode}:`} style={this.props.style}>
+        <span className='reactions-bar__item__emoji'><Emoji hovered={this.state.hovered} emoji={reaction.get('name')} emojiMap={this.props.emojiMap} /></span>
+        <span className='reactions-bar__item__count'><AnimatedNumber value={reaction.get('count')} /></span>
+      </button>
+    );
+  }
+
+}
+
+class ReactionsBar extends ImmutablePureComponent {
+
+  static propTypes = {
+    announcementId: PropTypes.string.isRequired,
+    reactions: ImmutablePropTypes.list.isRequired,
+    addReaction: PropTypes.func.isRequired,
+    removeReaction: PropTypes.func.isRequired,
+    emojiMap: ImmutablePropTypes.map.isRequired,
+  };
+
+  handleEmojiPick = data => {
+    const { addReaction, announcementId } = this.props;
+    addReaction(announcementId, data.native.replace(/:/g, ''));
+  }
+
+  willEnter () {
+    return { scale: reduceMotion ? 1 : 0 };
+  }
+
+  willLeave () {
+    return { scale: reduceMotion ? 0 : spring(0, { stiffness: 170, damping: 26 }) };
+  }
+
+  render () {
+    const { reactions } = this.props;
+    const visibleReactions = reactions.filter(x => x.get('count') > 0);
+
+    const styles = visibleReactions.map(reaction => ({
+      key: reaction.get('name'),
+      data: reaction,
+      style: { scale: reduceMotion ? 1 : spring(1, { stiffness: 150, damping: 13 }) },
+    })).toArray();
+
+    return (
+      <TransitionMotion styles={styles} willEnter={this.willEnter} willLeave={this.willLeave}>
+        {items => (
+          <div className={classNames('reactions-bar', { 'reactions-bar--empty': visibleReactions.isEmpty() })}>
+            {items.map(({ key, data, style }) => (
+              <Reaction
+                key={key}
+                reaction={data}
+                style={{ transform: `scale(${style.scale})`, position: style.scale < 0.5 ? 'absolute' : 'static' }}
+                announcementId={this.props.announcementId}
+                addReaction={this.props.addReaction}
+                removeReaction={this.props.removeReaction}
+                emojiMap={this.props.emojiMap}
+              />
+            ))}
+
+            {visibleReactions.size < 8 && <EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} button={<Icon id='plus' />} />}
+          </div>
+        )}
+      </TransitionMotion>
+    );
+  }
+
+}
+
+class Announcement extends ImmutablePureComponent {
+
+  static propTypes = {
+    announcement: ImmutablePropTypes.map.isRequired,
+    emojiMap: ImmutablePropTypes.map.isRequired,
+    addReaction: PropTypes.func.isRequired,
+    removeReaction: PropTypes.func.isRequired,
+    intl: PropTypes.object.isRequired,
+    selected: PropTypes.bool,
+  };
+
+  state = {
+    unread: !this.props.announcement.get('read'),
+  };
+
+  componentDidUpdate () {
+    const { selected, announcement } = this.props;
+    if (!selected && this.state.unread !== !announcement.get('read')) {
+      this.setState({ unread: !announcement.get('read') });
+    }
+  }
+
+  render () {
+    const { announcement } = this.props;
+    const { unread } = this.state;
+    const startsAt = announcement.get('starts_at') && new Date(announcement.get('starts_at'));
+    const endsAt = announcement.get('ends_at') && new Date(announcement.get('ends_at'));
+    const now = new Date();
+    const hasTimeRange = startsAt && endsAt;
+    const skipYear = hasTimeRange && startsAt.getFullYear() === endsAt.getFullYear() && endsAt.getFullYear() === now.getFullYear();
+    const skipEndDate = hasTimeRange && startsAt.getDate() === endsAt.getDate() && startsAt.getMonth() === endsAt.getMonth() && startsAt.getFullYear() === endsAt.getFullYear();
+    const skipTime = announcement.get('all_day');
+
+    return (
+      <div className='announcements__item'>
+        <strong className='announcements__item__range'>
+          <FormattedMessage id='announcement.announcement' defaultMessage='Announcement' />
+          {hasTimeRange && <span> · <FormattedDate value={startsAt} hour12={false} year={(skipYear || startsAt.getFullYear() === now.getFullYear()) ? undefined : 'numeric'} month='short' day='2-digit' hour={skipTime ? undefined : '2-digit'} minute={skipTime ? undefined : '2-digit'} /> - <FormattedDate value={endsAt} hour12={false} year={(skipYear || endsAt.getFullYear() === now.getFullYear()) ? undefined : 'numeric'} month={skipEndDate ? undefined : 'short'} day={skipEndDate ? undefined : '2-digit'} hour={skipTime ? undefined : '2-digit'} minute={skipTime ? undefined : '2-digit'} /></span>}
+        </strong>
+
+        <Content announcement={announcement} />
+
+        <ReactionsBar
+          reactions={announcement.get('reactions')}
+          announcementId={announcement.get('id')}
+          addReaction={this.props.addReaction}
+          removeReaction={this.props.removeReaction}
+          emojiMap={this.props.emojiMap}
+        />
+
+        {unread && <span className='announcements__item__unread' />}
+      </div>
+    );
+  }
+
+}
+
+export default @injectIntl
+class Announcements extends ImmutablePureComponent {
+
+  static propTypes = {
+    announcements: ImmutablePropTypes.list,
+    emojiMap: ImmutablePropTypes.map.isRequired,
+    dismissAnnouncement: PropTypes.func.isRequired,
+    addReaction: PropTypes.func.isRequired,
+    removeReaction: PropTypes.func.isRequired,
+    intl: PropTypes.object.isRequired,
+  };
+
+  state = {
+    index: 0,
+  };
+
+  componentDidMount () {
+    this._markAnnouncementAsRead();
+  }
+
+  componentDidUpdate () {
+    this._markAnnouncementAsRead();
+  }
+
+  _markAnnouncementAsRead () {
+    const { dismissAnnouncement, announcements } = this.props;
+    const { index } = this.state;
+    const announcement = announcements.get(index);
+    if (!announcement.get('read')) dismissAnnouncement(announcement.get('id'));
+  }
+
+  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;
+    const { index } = this.state;
+
+    if (announcements.isEmpty()) {
+      return null;
+    }
+
+    return (
+      <div className='announcements'>
+        <img className='announcements__mastodon' alt='' draggable='false' src={mascot || elephantUIPlane} />
+
+        <div className='announcements__container'>
+          <ReactSwipeableViews animateHeight={!reduceMotion} adjustHeight={reduceMotion} index={index} onChangeIndex={this.handleChangeIndex}>
+            {announcements.map((announcement, idx) => (
+              <Announcement
+                key={announcement.get('id')}
+                announcement={announcement}
+                emojiMap={this.props.emojiMap}
+                addReaction={this.props.addReaction}
+                removeReaction={this.props.removeReaction}
+                intl={intl}
+                selected={index === idx}
+              />
+            ))}
+          </ReactSwipeableViews>
+
+          {announcements.size > 1 && (
+            <div className='announcements__pagination'>
+              <IconButton disabled={announcements.size === 1} title={intl.formatMessage(messages.previous)} icon='chevron-left' onClick={this.handlePrevClick} size={13} />
+              <span>{index + 1} / {announcements.size}</span>
+              <IconButton disabled={announcements.size === 1} title={intl.formatMessage(messages.next)} icon='chevron-right' onClick={this.handleNextClick} size={13} />
+            </div>
+          )}
+        </div>
+      </div>
+    );
+  }
+
+}
diff --git a/app/javascript/mastodon/features/getting_started/containers/announcements_container.js b/app/javascript/mastodon/features/getting_started/containers/announcements_container.js
new file mode 100644
index 000000000..9d03ad6f7
--- /dev/null
+++ b/app/javascript/mastodon/features/getting_started/containers/announcements_container.js
@@ -0,0 +1,20 @@
+import { connect } from 'react-redux';
+import { addReaction, removeReaction, dismissAnnouncement } from 'mastodon/actions/announcements';
+import Announcements from '../components/announcements';
+import { createSelector } from 'reselect';
+import { Map as ImmutableMap } from 'immutable';
+
+const customEmojiMap = createSelector([state => state.get('custom_emojis')], items => items.reduce((map, emoji) => map.set(emoji.get('shortcode'), emoji), ImmutableMap()));
+
+const mapStateToProps = state => ({
+  announcements: state.getIn(['announcements', 'items']),
+  emojiMap: customEmojiMap(state),
+});
+
+const mapDispatchToProps = dispatch => ({
+  dismissAnnouncement: id => dispatch(dismissAnnouncement(id)),
+  addReaction: (id, name) => dispatch(addReaction(id, name)),
+  removeReaction: (id, name) => dispatch(removeReaction(id, name)),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(Announcements);
diff --git a/app/javascript/mastodon/features/getting_started/containers/trends_container.js b/app/javascript/mastodon/features/getting_started/containers/trends_container.js
index 1df3fb4fe..7a5268780 100644
--- a/app/javascript/mastodon/features/getting_started/containers/trends_container.js
+++ b/app/javascript/mastodon/features/getting_started/containers/trends_container.js
@@ -1,5 +1,5 @@
 import { connect } from 'react-redux';
-import { fetchTrends } from '../../../actions/trends';
+import { fetchTrends } from 'mastodon/actions/trends';
 import Trends from '../components/trends';
 
 const mapStateToProps = state => ({
diff --git a/app/javascript/mastodon/features/home_timeline/index.js b/app/javascript/mastodon/features/home_timeline/index.js
index 1cafb88ed..577ff33bb 100644
--- a/app/javascript/mastodon/features/home_timeline/index.js
+++ b/app/javascript/mastodon/features/home_timeline/index.js
@@ -9,14 +9,23 @@ import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import ColumnSettingsContainer from './containers/column_settings_container';
 import { Link } from 'react-router-dom';
+import { fetchAnnouncements, toggleShowAnnouncements } from 'mastodon/actions/announcements';
+import AnnouncementsContainer from 'mastodon/features/getting_started/containers/announcements_container';
+import classNames from 'classnames';
+import IconWithBadge from 'mastodon/components/icon_with_badge';
 
 const messages = defineMessages({
   title: { id: 'column.home', defaultMessage: 'Home' },
+  show_announcements: { id: 'home.show_announcements', defaultMessage: 'Show announcements' },
+  hide_announcements: { id: 'home.hide_announcements', defaultMessage: 'Hide announcements' },
 });
 
 const mapStateToProps = state => ({
   hasUnread: state.getIn(['timelines', 'home', 'unread']) > 0,
   isPartial: state.getIn(['timelines', 'home', 'isPartial']),
+  hasAnnouncements: !state.getIn(['announcements', 'items']).isEmpty(),
+  unreadAnnouncements: state.getIn(['announcements', 'items']).count(item => !item.get('read')),
+  showAnnouncements: state.getIn(['announcements', 'show']),
 });
 
 export default @connect(mapStateToProps)
@@ -31,6 +40,9 @@ class HomeTimeline extends React.PureComponent {
     isPartial: PropTypes.bool,
     columnId: PropTypes.string,
     multiColumn: PropTypes.bool,
+    hasAnnouncements: PropTypes.bool,
+    unreadAnnouncements: PropTypes.number,
+    showAnnouncements: PropTypes.bool,
   };
 
   handlePin = () => {
@@ -61,6 +73,7 @@ class HomeTimeline extends React.PureComponent {
   }
 
   componentDidMount () {
+    this.props.dispatch(fetchAnnouncements());
     this._checkIfReloadNeeded(false, this.props.isPartial);
   }
 
@@ -93,10 +106,31 @@ class HomeTimeline extends React.PureComponent {
     }
   }
 
+  handleToggleAnnouncementsClick = (e) => {
+    e.stopPropagation();
+    this.props.dispatch(toggleShowAnnouncements());
+  }
+
   render () {
-    const { intl, shouldUpdateScroll, hasUnread, columnId, multiColumn } = this.props;
+    const { intl, shouldUpdateScroll, hasUnread, columnId, multiColumn, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props;
     const pinned = !!columnId;
 
+    let announcementsButton = null;
+
+    if (hasAnnouncements) {
+      announcementsButton = (
+        <button
+          className={classNames('column-header__button', { 'active': showAnnouncements })}
+          title={intl.formatMessage(showAnnouncements ? messages.hide_announcements : messages.show_announcements)}
+          aria-label={intl.formatMessage(showAnnouncements ? messages.hide_announcements : messages.show_announcements)}
+          aria-pressed={showAnnouncements ? 'true' : 'false'}
+          onClick={this.handleToggleAnnouncementsClick}
+        >
+          <IconWithBadge id='bullhorn' count={unreadAnnouncements} />
+        </button>
+      );
+    }
+
     return (
       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
         <ColumnHeader
@@ -108,6 +142,8 @@ class HomeTimeline extends React.PureComponent {
           onClick={this.handleHeaderClick}
           pinned={pinned}
           multiColumn={multiColumn}
+          extraButton={announcementsButton}
+          appendContent={hasAnnouncements && showAnnouncements && <AnnouncementsContainer />}
         >
           <ColumnSettingsContainer />
         </ColumnHeader>
diff --git a/app/javascript/mastodon/features/status/components/action_bar.js b/app/javascript/mastodon/features/status/components/action_bar.js
index bf6469f2f..657d2bb1c 100644
--- a/app/javascript/mastodon/features/status/components/action_bar.js
+++ b/app/javascript/mastodon/features/status/components/action_bar.js
@@ -19,6 +19,7 @@ const messages = defineMessages({
   cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
   favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
   bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
+  more: { id: 'status.more', defaultMessage: 'More' },
   mute: { id: 'status.mute', defaultMessage: 'Mute @{name}' },
   muteConversation: { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' },
   unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
@@ -275,7 +276,7 @@ class ActionBar extends React.PureComponent {
         <div className='detailed-status__button'><IconButton className='bookmark-icon' active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} /></div>
 
         <div className='detailed-status__action-bar-dropdown'>
-          <DropdownMenuContainer size={18} icon='ellipsis-h' status={status} items={menu} direction='left' title='More' />
+          <DropdownMenuContainer size={18} icon='ellipsis-h' status={status} items={menu} direction='left' title={intl.formatMessage(messages.more)} />
         </div>
       </div>
     );
diff --git a/app/javascript/mastodon/features/status/components/detailed_status.js b/app/javascript/mastodon/features/status/components/detailed_status.js
index d5bc82735..2fec247c1 100644
--- a/app/javascript/mastodon/features/status/components/detailed_status.js
+++ b/app/javascript/mastodon/features/status/components/detailed_status.js
@@ -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 { FormattedDate, FormattedNumber } from 'react-intl';
+import { FormattedDate } from 'react-intl';
 import Card from './card';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import Video from '../../video';
@@ -14,6 +14,7 @@ import Audio from '../../audio';
 import scheduleIdleTask from '../../ui/util/schedule_idle_task';
 import classNames from 'classnames';
 import Icon from 'mastodon/components/icon';
+import AnimatedNumber from 'mastodon/components/animated_number';
 
 export default class DetailedStatus extends ImmutablePureComponent {
 
@@ -172,7 +173,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
         <Link to={`/statuses/${status.get('id')}/reblogs`} className='detailed-status__link'>
           <Icon id={reblogIcon} />
           <span className='detailed-status__reblogs'>
-            <FormattedNumber value={status.get('reblogs_count')} />
+            <AnimatedNumber value={status.get('reblogs_count')} />
           </span>
         </Link>
       );
@@ -181,7 +182,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
         <a href={`/interact/${status.get('id')}?type=reblog`} className='detailed-status__link' onClick={this.handleModalLink}>
           <Icon id={reblogIcon} />
           <span className='detailed-status__reblogs'>
-            <FormattedNumber value={status.get('reblogs_count')} />
+            <AnimatedNumber value={status.get('reblogs_count')} />
           </span>
         </a>
       );
@@ -192,7 +193,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
         <Link to={`/statuses/${status.get('id')}/favourites`} className='detailed-status__link'>
           <Icon id='star' />
           <span className='detailed-status__favorites'>
-            <FormattedNumber value={status.get('favourites_count')} />
+            <AnimatedNumber value={status.get('favourites_count')} />
           </span>
         </Link>
       );
@@ -201,7 +202,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
         <a href={`/interact/${status.get('id')}?type=favourite`} className='detailed-status__link' onClick={this.handleModalLink}>
           <Icon id='star' />
           <span className='detailed-status__favorites'>
-            <FormattedNumber value={status.get('favourites_count')} />
+            <AnimatedNumber value={status.get('favourites_count')} />
           </span>
         </a>
       );
diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js
index 6b18f34d1..c058120d6 100644
--- a/app/javascript/mastodon/features/status/index.js
+++ b/app/javascript/mastodon/features/status/index.js
@@ -542,6 +542,7 @@ class Status extends ImmutablePureComponent {
             <HotKeys handlers={handlers}>
               <div className={classNames('focusable', 'detailed-status__wrapper')} tabIndex='0' aria-label={textForScreenReader(intl, status, false)}>
                 <DetailedStatus
+                  key={`details-${status.get('id')}`}
                   status={status}
                   onOpenVideo={this.handleOpenVideo}
                   onOpenMedia={this.handleOpenMedia}
@@ -552,6 +553,7 @@ class Status extends ImmutablePureComponent {
                 />
 
                 <ActionBar
+                  key={`action-bar-${status.get('id')}`}
                   status={status}
                   onReply={this.handleReplyClick}
                   onFavourite={this.handleFavouriteClick}
diff --git a/app/javascript/mastodon/features/ui/components/media_modal.js b/app/javascript/mastodon/features/ui/components/media_modal.js
index a785551c0..d7f97f210 100644
--- a/app/javascript/mastodon/features/ui/components/media_modal.js
+++ b/app/javascript/mastodon/features/ui/components/media_modal.js
@@ -211,7 +211,6 @@ class MediaModal extends ImmutablePureComponent {
             style={swipeableViewsStyle}
             containerStyle={containerStyle}
             onChangeIndex={this.handleSwipe}
-            onSwitching={this.handleSwitching}
             index={index}
           >
             {content}
diff --git a/app/javascript/mastodon/features/video/index.js b/app/javascript/mastodon/features/video/index.js
index f6aeb8c9a..0e2a99416 100644
--- a/app/javascript/mastodon/features/video/index.js
+++ b/app/javascript/mastodon/features/video/index.js
@@ -124,12 +124,14 @@ class Video extends React.PureComponent {
     revealed: this.props.visible !== undefined ? this.props.visible : (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all'),
   };
 
-  // hard coded in components.scss
-  // any way to get ::before values programatically?
-  volWidth = 50;
+  // Hard-coded in components.scss
+  // Any way to get ::before values programatically?
+  volWidth  = 50;
   volOffset = 70;
+
   volHandleOffset = v => {
     const offset = v * this.volWidth + this.volOffset;
+
     return (offset > 110) ? 110 : offset;
   }
 
@@ -138,6 +140,7 @@ class Video extends React.PureComponent {
 
     if (c) {
       if (this.props.cacheWidth) this.props.cacheWidth(this.player.offsetWidth);
+
       this.setState({
         containerWidth: c.offsetWidth,
       });
@@ -205,12 +208,14 @@ class Video extends React.PureComponent {
     const x = (e.clientX - rect.left) / this.volWidth; //x position within the element.
 
     if(!isNaN(x)) {
-      var slideamt = x;
+      let slideamt = x;
+
       if(x > 1) {
         slideamt = 1;
       } else if(x < 0) {
         slideamt = 0;
       }
+
       this.video.volume = slideamt;
       this.setState({ volume: slideamt });
     }
@@ -252,9 +257,9 @@ class Video extends React.PureComponent {
 
   togglePlay = () => {
     if (this.state.paused) {
-      this.video.play();
+      this.setState({ paused: false }, () => this.video.play());
     } else {
-      this.video.pause();
+      this.setState({ paused: true }, () => this.video.pause());
     }
   }
 
@@ -272,12 +277,16 @@ class Video extends React.PureComponent {
     document.addEventListener('mozfullscreenchange', this.handleFullscreenChange, true);
     document.addEventListener('MSFullscreenChange', this.handleFullscreenChange, true);
 
+    window.addEventListener('scroll', this.handleScroll);
+
     if (this.props.blurhash) {
       this._decode();
     }
   }
 
   componentWillUnmount () {
+    window.removeEventListener('scroll', this.handleScroll);
+
     document.removeEventListener('fullscreenchange', this.handleFullscreenChange, true);
     document.removeEventListener('webkitfullscreenchange', this.handleFullscreenChange, true);
     document.removeEventListener('mozfullscreenchange', this.handleFullscreenChange, true);
@@ -294,6 +303,7 @@ class Video extends React.PureComponent {
     if (prevState.revealed && !this.state.revealed && this.video) {
       this.video.pause();
     }
+
     if (prevProps.blurhash !== this.props.blurhash && this.props.blurhash) {
       this._decode();
     }
@@ -313,6 +323,19 @@ class Video extends React.PureComponent {
     }
   }
 
+  handleScroll = throttle(() => {
+    if (!this.video) {
+      return;
+    }
+
+    const { top, height } = this.video.getBoundingClientRect();
+    const inView = (top <= (window.innerHeight || document.documentElement.clientHeight)) && (top + height >= 0);
+
+    if (!this.state.paused && !inView) {
+      this.setState({ paused: true }, () => this.video.pause());
+    }
+  }, 150, { trailing: true })
+
   handleFullscreenChange = () => {
     this.setState({ fullscreen: isFullscreen() });
   }
@@ -326,8 +349,11 @@ class Video extends React.PureComponent {
   }
 
   toggleMute = () => {
-    this.video.muted = !this.video.muted;
-    this.setState({ muted: this.video.muted });
+    const muted = !this.video.muted;
+
+    this.setState({ muted }, () => {
+      this.video.muted = muted;
+    });
   }
 
   toggleReveal = () => {
@@ -430,7 +456,6 @@ class Video extends React.PureComponent {
           src={src}
           poster={preview}
           preload={preload}
-          loop
           role='button'
           tabIndex='0'
           aria-label={alt}
@@ -495,13 +520,8 @@ class Video extends React.PureComponent {
               {(!onCloseVideo && !editable) && <button type='button' aria-label={intl.formatMessage(messages.hide)} onClick={this.toggleReveal}><Icon id='eye-slash' fixedWidth /></button>}
               {(!fullscreen && onOpenVideo) && <button type='button' aria-label={intl.formatMessage(messages.expand)} onClick={this.handleOpenVideo}><Icon id='expand' fixedWidth /></button>}
               {onCloseVideo && <button type='button' aria-label={intl.formatMessage(messages.close)} onClick={this.handleCloseVideo}><Icon id='compress' fixedWidth /></button>}
-              <button type='button' aria-label={intl.formatMessage(messages.download)}>
-                <a className='video-player__download__icon' href={this.props.src} download>
-                  <Icon id={'download'} fixedWidth />
-                </a>
-              </button>
+              <button type='button' aria-label={intl.formatMessage(messages.download)}><a className='video-player__download__icon' href={this.props.src} download><Icon id={'download'} fixedWidth /></a></button>
               <button type='button' aria-label={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)} onClick={this.toggleFullscreen}><Icon id={fullscreen ? 'compress' : 'arrows-alt'} fixedWidth /></button>
-
             </div>
           </div>
         </div>
diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json
index e65c81e64..5ce5eb12e 100644
--- a/app/javascript/mastodon/locales/ar.json
+++ b/app/javascript/mastodon/locales/ar.json
@@ -1,18 +1,19 @@
 {
   "account.add_or_remove_from_list": "أضفه أو أزله من القائمة",
   "account.badges.bot": "روبوت",
+  "account.badges.group": "Group",
   "account.block": "حظر @{name}",
   "account.block_domain": "إخفاء كل شيئ قادم من اسم النطاق {domain}",
   "account.blocked": "محظور",
   "account.cancel_follow_request": "إلغاء طلب المتابَعة",
   "account.direct": "رسالة خاصة إلى @{name}",
   "account.domain_blocked": "النطاق مخفي",
-  "account.edit_profile": "تعديل الملف التعريفي",
+  "account.edit_profile": "تعديل الملف الشخصي",
   "account.endorse": "أوصِ به على صفحتك",
   "account.follow": "تابِع",
   "account.followers": "مُتابِعون",
   "account.followers.empty": "لا أحد يتبع هذا الحساب بعد.",
-  "account.follows": "يتبع",
+  "account.follows": "يتابع",
   "account.follows.empty": "هذا الحساب لا يتبع أحدًا بعد.",
   "account.follows_you": "يتابعك",
   "account.hide_reblogs": "إخفاء ترقيات @{name}",
@@ -39,9 +40,10 @@
   "account.unmute": "إلغاء الكتم عن @{name}",
   "account.unmute_notifications": "إلغاء كتم إخطارات @{name}",
   "alert.rate_limited.message": "يرجى إعادة المحاولة بعد {retry_time, time, medium}.",
-  "alert.rate_limited.title": "Rate limited",
+  "alert.rate_limited.title": "المعدل محدود",
   "alert.unexpected.message": "لقد طرأ هناك خطأ غير متوقّع.",
   "alert.unexpected.title": "المعذرة!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} في الأسبوع",
   "boost_modal.combo": "يمكنك/ي ضغط {combo} لتخطّي هذه في المرّة القادمة",
   "bundle_column_error.body": "لقد وقع هناك خطأ أثناء عملية تحميل هذا العنصر.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "لقد وقع هناك خطأ أثناء عملية تحميل هذا العنصر.",
   "bundle_modal_error.retry": "إعادة المحاولة",
   "column.blocks": "الحسابات المحجوبة",
+  "column.bookmarks": "الفواصل المرجعية",
   "column.community": "الخيط العام المحلي",
   "column.direct": "الرسائل المباشرة",
   "column.directory": "استعراض الملفات التعريفية",
@@ -82,6 +85,8 @@
   "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_loud": "{publish}!",
   "compose_form.sensitive.hide": "تحديد الوسائط كحساسة",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "ليس هناك تبويقات!",
   "empty_column.account_unavailable": "الملف التعريفي غير متوفر",
   "empty_column.blocks": "لم تقم بحظر أي مستخدِم بعد.",
+  "empty_column.bookmarked_statuses": "ليس لديك أية تبويقات في الفواصل المرجعية بعد. عندما ستقوم بإضافة البعض منها، ستظهر هنا.",
   "empty_column.community": "الخط العام المحلي فارغ. أكتب شيئا ما للعامة كبداية!",
   "empty_column.direct": "لم تتلق أية رسالة خاصة مباشِرة بعد. سوف يتم عرض الرسائل المباشرة هنا إن قمت بإرسال واحدة أو تلقيت البعض منها.",
   "empty_column.domain_blocks": "ليس هناك نطاقات مخفية بعد.",
@@ -153,8 +159,8 @@
   "empty_column.notifications": "لم تتلق أي إشعار بعدُ. تفاعل مع المستخدمين الآخرين لإنشاء محادثة.",
   "empty_column.public": "لا يوجد أي شيء هنا! قم بنشر شيء ما للعامة، أو اتبع المستخدمين الآخرين المتواجدين على الخوادم الأخرى لملء خيط المحادثات",
   "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.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.",
-  "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard",
+  "error.unexpected_crash.next_steps": "حاول إعادة إنعاش الصفحة. إن لم تُحلّ المشكلة ، يمكنك دائمًا استخدام ماستدون عبر متصفّح آخر أو تطبيق أصلي.",
+  "errors.unexpected_crash.copy_stacktrace": "انسخ تتبع الارتباطات إلى الحافظة",
   "errors.unexpected_crash.report_issue": "الإبلاغ عن خلل",
   "follow_request.authorize": "ترخيص",
   "follow_request.reject": "رفض",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "لفتح قائمة المستخدِمين المكتومين",
   "keyboard_shortcuts.my_profile": "لفتح ملفك التعريفي",
   "keyboard_shortcuts.notifications": "لفتح عمود الإشعارات",
+  "keyboard_shortcuts.open_media": "لفتح الوسائط",
   "keyboard_shortcuts.pinned": "لفتح قائمة التبويقات المدبسة",
   "keyboard_shortcuts.profile": "لفتح الملف التعريفي للناشر",
   "keyboard_shortcuts.reply": "للردّ",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "هل تود إخفاء الإخطارات القادمة من هذا المستخدم ؟",
   "navigation_bar.apps": "تطبيقات الأجهزة المحمولة",
   "navigation_bar.blocks": "الحسابات المحجوبة",
+  "navigation_bar.bookmarks": "الفواصل المرجعية",
   "navigation_bar.community_timeline": "الخيط العام المحلي",
   "navigation_bar.compose": "تحرير تبويق جديد",
   "navigation_bar.direct": "الرسائل المباشِرة",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "الأمان",
   "notification.favourite": "أُعجِب {name} بمنشورك",
   "notification.follow": "{name} يتابعك",
+  "notification.follow_request": "لقد طلب {name} متابعتك",
   "notification.mention": "{name} ذكرك",
+  "notification.own_poll": "انتهى استطلاعك للرأي",
   "notification.poll": "لقد إنتها تصويت شاركت فيه",
   "notification.reblog": "{name} قام بترقية تبويقك",
   "notifications.clear": "امسح الإخطارات",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "شريط الفلترة السريعة",
   "notifications.column_settings.filter_bar.show": "اظهِره",
   "notifications.column_settings.follow": "متابعُون جُدُد:",
+  "notifications.column_settings.follow_request": "الطلبات الجديد لِمتابَعتك:",
   "notifications.column_settings.mention": "الإشارات:",
   "notifications.column_settings.poll": "نتائج استطلاع الرأي:",
   "notifications.column_settings.push": "الإخطارات المدفوعة",
@@ -344,6 +355,7 @@
   "status.admin_account": "افتح الواجهة الإدارية لـ @{name}",
   "status.admin_status": "افتح هذا المنشور على واجهة الإشراف",
   "status.block": "احجب @{name}",
+  "status.bookmark": "أضفه إلى الفواصل المرجعية",
   "status.cancel_reblog_private": "إلغاء الترقية",
   "status.cannot_reblog": "تعذرت ترقية هذا المنشور",
   "status.copy": "نسخ رابط المنشور",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "رقّاه {name}",
   "status.reblogs.empty": "لم يقم أي أحد بترقية هذا التبويق بعد. عندما يقوم أحدهم بذلك سوف تظهر هنا.",
   "status.redraft": "إزالة و إعادة الصياغة",
+  "status.remove_bookmark": "احذفه مِن الفواصل المرجعية",
   "status.reply": "ردّ",
   "status.replyAll": "رُد على الخيط",
   "status.report": "ابلِغ عن @{name}",
@@ -400,18 +413,21 @@
   "upload_button.label": "إضافة وسائط ({formats})",
   "upload_error.limit": "لقد تم بلوغ الحد الأقصى المسموح به لإرسال الملفات.",
   "upload_error.poll": "لا يمكن إدراج ملفات في استطلاعات الرأي.",
+  "upload_form.audio_description": "وصف للأشخاص ذي قِصر السمع",
   "upload_form.description": "وصف للمعاقين بصريا",
   "upload_form.edit": "تعديل",
   "upload_form.undo": "حذف",
+  "upload_form.video_description": "وصف للمعاقين بصريا أو لِذي قِصر السمع",
   "upload_modal.analyzing_picture": "جارٍ فحص الصورة…",
   "upload_modal.apply": "طبّق",
   "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog",
   "upload_modal.detect_text": "اكتشف النص مِن الصورة",
   "upload_modal.edit_media": "تعديل الوسائط",
-  "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.",
+  "upload_modal.hint": "اضغط أو اسحب الدائرة على خانة المعاينة لاختيار نقطة التركيز التي ستُعرَض دائمًا على كل المصغرات.",
   "upload_modal.preview_label": "معاينة ({ratio})",
   "upload_progress.label": "يرفع...",
   "video.close": "إغلاق الفيديو",
+  "video.download": "تنزيل الملف",
   "video.exit_fullscreen": "الخروج من وضع الشاشة المليئة",
   "video.expand": "توسيع الفيديو",
   "video.fullscreen": "ملء الشاشة",
diff --git a/app/javascript/mastodon/locales/ast.json b/app/javascript/mastodon/locales/ast.json
index 5145a6579..e570fc3b7 100644
--- a/app/javascript/mastodon/locales/ast.json
+++ b/app/javascript/mastodon/locales/ast.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Add or Remove from lists",
   "account.badges.bot": "Robó",
+  "account.badges.group": "Group",
   "account.block": "Bloquiar a @{name}",
   "account.block_domain": "Anubrir tolo de {domain}",
   "account.blocked": "Blocked",
@@ -21,15 +22,15 @@
   "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Media",
   "account.mention": "Mentar a @{name}",
-  "account.moved_to": "{name} has moved to:",
+  "account.moved_to": "{name} mudóse a:",
   "account.mute": "Silenciar a @{name}",
   "account.mute_notifications": "Mute notifications from @{name}",
   "account.muted": "Muted",
-  "account.never_active": "Never",
-  "account.posts": "Toots",
-  "account.posts_with_replies": "Toots y rempuestes",
+  "account.never_active": "Enxamás",
+  "account.posts": "Barritos",
+  "account.posts_with_replies": "Barritos y rempuestes",
   "account.report": "Report @{name}",
-  "account.requested": "Awaiting approval",
+  "account.requested": "Esperando pola aprobación. Calca pa encaboxar la solicitú de siguimientu",
   "account.share": "Share @{name}'s profile",
   "account.show_reblogs": "Show boosts from @{name}",
   "account.unblock": "Desbloquiar a @{name}",
@@ -41,16 +42,18 @@
   "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.",
   "alert.rate_limited.title": "Rate limited",
   "alert.unexpected.message": "Asocedió un fallu inesperáu.",
-  "alert.unexpected.title": "¡Ups!",
-  "autosuggest_hashtag.per_week": "{count} per week",
+  "alert.unexpected.title": "¡Meca!",
+  "announcement.announcement": "Announcement",
+  "autosuggest_hashtag.per_week": "{count} per selmana",
   "boost_modal.combo": "Pues primir {combo} pa saltar esto la próxima vegada",
-  "bundle_column_error.body": "Something went wrong while loading this component.",
+  "bundle_column_error.body": "Asocedió daqué malo mentanto se cargaba esti componente.",
   "bundle_column_error.retry": "Try again",
   "bundle_column_error.title": "Network error",
   "bundle_modal_error.close": "Close",
-  "bundle_modal_error.message": "Something went wrong while loading this component.",
+  "bundle_modal_error.message": "Asocedió daqué malo mentanto se cargaba esti componente.",
   "bundle_modal_error.retry": "Try again",
   "column.blocks": "Usuarios bloquiaos",
+  "column.bookmarks": "Marcadores",
   "column.community": "Llinia temporal llocal",
   "column.direct": "Mensaxes direutos",
   "column.directory": "Browse profiles",
@@ -61,7 +64,7 @@
   "column.lists": "Llistes",
   "column.mutes": "Usuarios silenciaos",
   "column.notifications": "Avisos",
-  "column.pins": "Toots fixaos",
+  "column.pins": "Barritos fixaos",
   "column.public": "Llinia temporal federada",
   "column_back_button.label": "Atrás",
   "column_header.hide_settings": "Hide settings",
@@ -71,25 +74,27 @@
   "column_header.show_settings": "Show settings",
   "column_header.unpin": "Desfixar",
   "column_subheading.settings": "Axustes",
-  "community.column_settings.media_only": "Media only",
-  "compose_form.direct_message_warning": "Esti toot namái va unviase a los usuarios mentaos.",
+  "community.column_settings.media_only": "Namái multimedia",
+  "compose_form.direct_message_warning": "Esti barritu namái va unviase a los usuarios mentaos.",
   "compose_form.direct_message_warning_learn_more": "Learn more",
   "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
   "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
   "compose_form.lock_disclaimer.lock": "locked",
   "compose_form.placeholder": "¿En qué pienses?",
-  "compose_form.poll.add_option": "Add a choice",
+  "compose_form.poll.add_option": "Amestar una escoyeta",
   "compose_form.poll.duration": "Poll duration",
-  "compose_form.poll.option_placeholder": "Choice {number}",
+  "compose_form.poll.option_placeholder": "Escoyeta {number}",
   "compose_form.poll.remove_option": "Remove this choice",
+  "compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices",
+  "compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
   "compose_form.publish": "Toot",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Mark media as sensitive",
   "compose_form.sensitive.marked": "Media is marked as sensitive",
   "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
   "compose_form.spoiler.marked": "El testu nun va anubrise darrera d'una alvertencia",
-  "compose_form.spoiler.unmarked": "El testu va anubrise",
-  "compose_form.spoiler_placeholder": "Escribi equí l'avertencia",
+  "compose_form.spoiler.unmarked": "El testu nun va anubrise",
+  "compose_form.spoiler_placeholder": "Escribi equí l'alvertencia",
   "confirmation_modal.cancel": "Encaboxar",
   "confirmations.block.block_and_report": "Block & Report",
   "confirmations.block.confirm": "Block",
@@ -106,7 +111,7 @@
   "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": "¿De xuru que quies silenciar a {name}?",
   "confirmations.redraft.confirm": "Desaniciar y reeditar",
-  "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.",
+  "confirmations.redraft.message": "¿De xuru que quies desaniciar esti estáu y reeditalu? Van perdese los favoritos y comparticiones, y les rempuestes al toot orixinal van quedar güérfanes.",
   "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",
@@ -115,80 +120,81 @@
   "conversation.mark_as_read": "Mark as read",
   "conversation.open": "View conversation",
   "conversation.with": "With {names}",
-  "directory.federated": "From known fediverse",
-  "directory.local": "From {domain} only",
-  "directory.new_arrivals": "New arrivals",
+  "directory.federated": "Dende'l fediversu",
+  "directory.local": "Dende {domain} namái",
+  "directory.new_arrivals": "Cuentes nueves",
   "directory.recently_active": "Recently active",
   "embed.instructions": "Empotra esti estáu nun sitiu web copiando'l códigu d'embaxo.",
-  "embed.preview": "Asina ye como va vese:",
+  "embed.preview": "Asina ye cómo va vese:",
   "emoji_button.activity": "Actividaes",
   "emoji_button.custom": "Custom",
   "emoji_button.flags": "Banderes",
   "emoji_button.food": "Comida y bébora",
-  "emoji_button.label": "Insert emoji",
+  "emoji_button.label": "Inxertar un fustaxe",
   "emoji_button.nature": "Natura",
   "emoji_button.not_found": "¡Nun hai fustaxes! (╯°□°)╯︵ ┻━┻",
   "emoji_button.objects": "Oxetos",
   "emoji_button.people": "Xente",
   "emoji_button.recent": "Úsase davezu",
-  "emoji_button.search": "Guetar...",
+  "emoji_button.search": "Guetar…",
   "emoji_button.search_results": "Search results",
   "emoji_button.symbols": "Símbolos",
   "emoji_button.travel": "Viaxes y llugares",
-  "empty_column.account_timeline": "No toots here!",
+  "empty_column.account_timeline": "¡Equí nun hai barritos!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "Entá nun bloquiesti a dengún usuariu.",
+  "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": "Entá nun tienes dengún mensaxe direutu. Cuando unvies o recibas dalgún, va apaecer equí.",
   "empty_column.domain_blocks": "Entá nun hai dominios anubríos.",
   "empty_column.favourited_statuses": "You don't have any favourite toots yet. When you favourite one, it will show up here.",
   "empty_column.favourites": "No one has favourited this toot yet. When someone does, they will show up here.",
   "empty_column.follow_requests": "Entá nun tienes denguna solicitú de siguimientu. Cuando recibas una, va amosase equí.",
-  "empty_column.hashtag": "There is nothing in this hashtag yet.",
+  "empty_column.hashtag": "Entá nun hai nada nesta etiqueta.",
   "empty_column.home": "¡Tienes la llinia temporal balera! Visita {public} o usa la gueta pa entamar y conocer a otros usuarios.",
   "empty_column.home.public_timeline": "la llinia temporal pública",
   "empty_column.list": "Entá nun hai nada nesta llista. Cuando los miembros d'esta llista espublicen estaos nuevos, van apaecer equí.",
   "empty_column.lists": "Entá nun tienes denguna llista. Cuando crees una, va amosase equí.",
   "empty_column.mutes": "Entá nun silenciesti a dengún usuariu.",
-  "empty_column.notifications": "Entá nun tienes dengún avisu. Interactua con otros p'aniciar la conversación.",
-  "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up",
-  "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.",
+  "empty_column.notifications": "Entá nun tienes dengún avisu. Interactúa con otros p'aniciar la conversación.",
+  "empty_column.public": "¡Equí nun hai nada! Escribi daqué público o sigui a usuarios d'otros sirvidores pa rellenar esto",
+  "error.unexpected_crash.explanation": "Pola mor d'un fallu nel códigu o un problema de compatibilidá del restolador, esta páxina nun pudo amosase correutamente.",
   "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.",
   "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard",
   "errors.unexpected_crash.report_issue": "Report issue",
   "follow_request.authorize": "Autorizar",
   "follow_request.reject": "Refugar",
   "getting_started.developers": "Desendolcadores",
-  "getting_started.directory": "Profile directory",
+  "getting_started.directory": "Direutoriu de perfiles",
   "getting_started.documentation": "Documentación",
   "getting_started.heading": "Entamu",
-  "getting_started.invite": "Convidar xente",
-  "getting_started.open_source_notice": "Mastodon ye software de códigu abiertu. Pues collaborar o informar de fallos en {github} (GitHub).",
-  "getting_started.security": "Seguranza",
+  "getting_started.invite": "Convidar a persones",
+  "getting_started.open_source_notice": "Mastodon ye software de códigu abiertu. Pues collaborar o informar de fallos en GitHub: {github}.",
+  "getting_started.security": "Axustes de la cuenta",
   "getting_started.terms": "Términos del serviciu",
-  "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_header.tag_mode.all": "y {additional}",
+  "hashtag.column_header.tag_mode.any": "o {additional}",
+  "hashtag.column_header.tag_mode.none": "ensin {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.column_settings.tag_toggle": "Incluyir les etiquetes adicionales d'esta columna",
   "home.column_settings.basic": "Basic",
   "home.column_settings.show_reblogs": "Amosar toots compartíos",
   "home.column_settings.show_replies": "Amosar rempuestes",
-  "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}}",
+  "intervals.full.days": "{number, plural, one {# día} other {# díes}}",
+  "intervals.full.hours": "{number, plural, one {# hora} other {# hores}}",
+  "intervals.full.minutes": "{number, plural, one {# minutu} other {# minutos}}",
   "introduction.federation.action": "Next",
   "introduction.federation.federated.headline": "Federated",
-  "introduction.federation.federated.text": "Public posts from other servers of the fediverse will appear in the federated timeline.",
+  "introduction.federation.federated.text": "Los espublizamientos públicos de los demás sirvidores del fediversu van apaecer na llinia temporal federada.",
   "introduction.federation.home.headline": "Home",
   "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!",
   "introduction.federation.local.headline": "Local",
   "introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
-  "introduction.interactions.action": "Finish toot-orial!",
+  "introduction.interactions.action": "¡Finar el tutorial!",
   "introduction.interactions.favourite.headline": "Favourite",
   "introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
   "introduction.interactions.reblog.headline": "Boost",
@@ -196,18 +202,18 @@
   "introduction.interactions.reply.headline": "Reply",
   "introduction.interactions.reply.text": "You can reply to other people's and your own toots, which will chain them together in a conversation.",
   "introduction.welcome.action": "Let's go!",
-  "introduction.welcome.headline": "First steps",
-  "introduction.welcome.text": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.",
+  "introduction.welcome.headline": "Primeros pasos",
+  "introduction.welcome.text": "¡Afáyate nel fediversu! Nun momentu vas ser a tresmitir mensaxes y falar colos collacios d'una montonera de sirvidores. Pero esti sirvidor, {domain}, ye especial: agospia'l to perfil, asina qu'alcuérdate de cómo se llama.",
   "keyboard_shortcuts.back": "pa dir p'atrás",
   "keyboard_shortcuts.blocked": "p'abrir la llista d'usuarios bloquiaos",
   "keyboard_shortcuts.boost": "pa compartir un toot",
   "keyboard_shortcuts.column": "to focus a status in one of the columns",
-  "keyboard_shortcuts.compose": "to focus the compose textarea",
+  "keyboard_shortcuts.compose": "pa enfocar l'área de composición",
   "keyboard_shortcuts.description": "Descripción",
   "keyboard_shortcuts.direct": "p'abrir la columna de los mensaxes direutos",
   "keyboard_shortcuts.down": "pa baxar na llista",
   "keyboard_shortcuts.enter": "to open status",
-  "keyboard_shortcuts.favourite": "to favourite",
+  "keyboard_shortcuts.favourite": "p'amestar a Favoritos",
   "keyboard_shortcuts.favourites": "p'abrir la llista de favoritos",
   "keyboard_shortcuts.federated": "p'abrir la llinia temporal federada",
   "keyboard_shortcuts.heading": "Atayos del tecláu",
@@ -217,18 +223,19 @@
   "keyboard_shortcuts.local": "p'abrir la llinia temporal llocal",
   "keyboard_shortcuts.mention": "pa mentar al autor",
   "keyboard_shortcuts.muted": "p'abrir la llista d'usuarios silenciaos",
-  "keyboard_shortcuts.my_profile": "to open your profile",
+  "keyboard_shortcuts.my_profile": "p'abrir el to perfil",
   "keyboard_shortcuts.notifications": "p'abrir la columna d'avisos",
-  "keyboard_shortcuts.pinned": "p'abrir la llista de toots fixaos",
+  "keyboard_shortcuts.open_media": "to open media",
+  "keyboard_shortcuts.pinned": "p'abrir la llista de barritos fixaos",
   "keyboard_shortcuts.profile": "p'abrir el perfil del autor",
   "keyboard_shortcuts.reply": "pa responder",
   "keyboard_shortcuts.requests": "p'abrir la llista de solicitúes de siguimientu",
-  "keyboard_shortcuts.search": "to focus search",
+  "keyboard_shortcuts.search": "pa enfocar la gueta",
   "keyboard_shortcuts.start": "p'abrir la columna «entamar»",
   "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW",
   "keyboard_shortcuts.toggle_sensitivity": "to show/hide media",
-  "keyboard_shortcuts.toot": "p'apenzar un toot nuevu",
-  "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
+  "keyboard_shortcuts.toot": "p'apenzar un barritu nuevu",
+  "keyboard_shortcuts.unfocus": "pa desenfocar l'área de composición/gueta",
   "keyboard_shortcuts.up": "pa xubir na llista",
   "lightbox.close": "Close",
   "lightbox.next": "Siguiente",
@@ -243,14 +250,15 @@
   "lists.new.title_placeholder": "Títulu nuevu de la llista",
   "lists.search": "Guetar ente la xente que sigues",
   "lists.subheading": "Les tos llistes",
-  "load_pending": "{count, plural, one {# new item} other {# new items}}",
-  "loading_indicator.label": "Cargando...",
+  "load_pending": "{count, plural, one {# elementu nuevu} other {# elementos nuevos}}",
+  "loading_indicator.label": "Cargando…",
   "media_gallery.toggle_visible": "Toggle visibility",
   "missing_indicator.label": "Nun s'alcontró",
   "missing_indicator.sublabel": "Esti recursu nun pudo alcontrase",
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.apps": "Aplicaciones pa móviles",
   "navigation_bar.blocks": "Usuarios bloquiaos",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Llinia temporal llocal",
   "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Mensaxes direutos",
@@ -267,13 +275,15 @@
   "navigation_bar.logout": "Zarrar sesión",
   "navigation_bar.mutes": "Usuarios silenciaos",
   "navigation_bar.personal": "Personal",
-  "navigation_bar.pins": "Toots fixaos",
+  "navigation_bar.pins": "Barritos fixaos",
   "navigation_bar.preferences": "Preferencies",
   "navigation_bar.public_timeline": "Llinia temporal federada",
   "navigation_bar.security": "Seguranza",
   "notification.favourite": "{name} favourited your status",
   "notification.follow": "{name} siguióte",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} mentóte",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "A poll you have voted in has ended",
   "notification.reblog": "{name} compartió'l to estáu",
   "notifications.clear": "Llimpiar avisos",
@@ -282,19 +292,20 @@
   "notifications.column_settings.favourite": "Favoritos:",
   "notifications.column_settings.filter_bar.advanced": "Display all categories",
   "notifications.column_settings.filter_bar.category": "Quick filter bar",
-  "notifications.column_settings.filter_bar.show": "Show",
+  "notifications.column_settings.filter_bar.show": "Amosar",
   "notifications.column_settings.follow": "Siguidores nuevos:",
+  "notifications.column_settings.follow_request": "Solicitúes de siguimientu nueves:",
   "notifications.column_settings.mention": "Menciones:",
   "notifications.column_settings.poll": "Poll results:",
   "notifications.column_settings.push": "Push notifications",
-  "notifications.column_settings.reblog": "Toots compartíos:",
+  "notifications.column_settings.reblog": "Barritos compartíos:",
   "notifications.column_settings.show": "Amosar en columna",
-  "notifications.column_settings.sound": "Reproducir soníu",
+  "notifications.column_settings.sound": "Reproducir un soníu",
   "notifications.filter.all": "All",
   "notifications.filter.boosts": "Boosts",
   "notifications.filter.favourites": "Favourites",
   "notifications.filter.follows": "Follows",
-  "notifications.filter.mentions": "Mentions",
+  "notifications.filter.mentions": "Menciones",
   "notifications.filter.polls": "Poll results",
   "notifications.group": "{count} avisos",
   "poll.closed": "Closed",
@@ -312,11 +323,11 @@
   "privacy.private.short": "Namái siguidores",
   "privacy.public.long": "Post to public timelines",
   "privacy.public.short": "Public",
-  "privacy.unlisted.long": "Do not show in public timelines",
-  "privacy.unlisted.short": "Unlisted",
+  "privacy.unlisted.long": "Nun apaez nes llinies temporales públiques",
+  "privacy.unlisted.short": "Nun llistar",
   "refresh": "Refresh",
   "regeneration_indicator.label": "Cargando…",
-  "regeneration_indicator.sublabel": "Your home feed is being prepared!",
+  "regeneration_indicator.sublabel": "¡Tamos tresnando'l feed d'Aniciu!",
   "relative_time.days": "{number}d",
   "relative_time.hours": "{number}h",
   "relative_time.just_now": "agora",
@@ -325,7 +336,7 @@
   "reply_indicator.cancel": "Encaboxar",
   "report.forward": "Forward to {target}",
   "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
-  "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
+  "report.hint": "L'informe va unviase a los llendadores del to sirvidor. Embaxo, pues desplicar por qué informes d'esta cuenta:",
   "report.placeholder": "Comentarios adicionales",
   "report.submit": "Submit",
   "report.target": "Report {target}",
@@ -338,12 +349,13 @@
   "search_popout.tips.user": "usuariu",
   "search_results.accounts": "Xente",
   "search_results.hashtags": "Etiquetes",
-  "search_results.statuses": "Toots",
-  "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.",
-  "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
+  "search_results.statuses": "Barritos",
+  "search_results.statuses_fts_disabled": "Esti sirvidor de Mastodon tien activada la gueta de barritos pol so conteníu.",
+  "search_results.total": "{count, number} {count, plural, one {resultáu} other {resultaos}}",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_status": "Open this status in the moderation interface",
   "status.block": "Bloquiar a @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Dexar de compartir",
   "status.cannot_reblog": "Esti artículu nun pue compartise",
   "status.copy": "Copy link to status",
@@ -354,20 +366,21 @@
   "status.favourite": "Favourite",
   "status.filtered": "Filtered",
   "status.load_more": "Cargar más",
-  "status.media_hidden": "Mediu anubríu",
+  "status.media_hidden": "Multimedia anubrida",
   "status.mention": "Mentar a @{name}",
   "status.more": "Más",
   "status.mute": "Silenciar a @{name}",
   "status.mute_conversation": "Silenciar la conversación",
   "status.open": "Espander esti estáu",
   "status.pin": "Fixar nel perfil",
-  "status.pinned": "Toot fixáu",
+  "status.pinned": "Barritu fixáu",
   "status.read_more": "Read more",
   "status.reblog": "Compartir",
   "status.reblog_private": "Compartir cola audiencia orixinal",
   "status.reblogged_by": "{name} compartió",
-  "status.reblogs.empty": "Naide nun compartió esti toot entá. Cuando daquién lo faiga, va amosase equí.",
+  "status.reblogs.empty": "Naide nun compartió esti barritu entá. Cuando daquién lo faiga, va amosase equí.",
   "status.redraft": "Desaniciar y reeditar",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "Responder",
   "status.replyAll": "Reply to thread",
   "status.report": "Report @{name}",
@@ -377,7 +390,7 @@
   "status.show_less_all": "Show less for all",
   "status.show_more": "Amosar más",
   "status.show_more_all": "Show more for all",
-  "status.show_thread": "Show thread",
+  "status.show_thread": "Amosar el filu",
   "status.uncached_media_warning": "Not available",
   "status.unmute_conversation": "Unmute conversation",
   "status.unpin": "Desfixar del perfil",
@@ -388,30 +401,33 @@
   "tabs_bar.local_timeline": "Llocal",
   "tabs_bar.notifications": "Avisos",
   "tabs_bar.search": "Search",
-  "time_remaining.days": "{number, plural, one {# day} other {# days}} left",
+  "time_remaining.days": "{number, plural, one {# día} other {# díes}} que queden",
   "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",
-  "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking",
+  "trends.count_by_accounts": "{count} {rawCount, plural, one {persona} other {persones}} falando",
   "trends.trending_now": "Trending now",
   "ui.beforeunload": "El borrador va perdese si coles de Mastodon.",
   "upload_area.title": "Drag & drop to upload",
-  "upload_button.label": "Add media",
+  "upload_button.label": "Add media ({formats})",
   "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": "Descripción pa discapacitaos visuales",
-  "upload_form.edit": "Edit",
+  "upload_form.edit": "Editar",
   "upload_form.undo": "Desaniciar",
-  "upload_modal.analyzing_picture": "Analyzing picture…",
-  "upload_modal.apply": "Apply",
+  "upload_form.video_description": "Describe for people with hearing loss or visual impairment",
+  "upload_modal.analyzing_picture": "Analizando la semeya…",
+  "upload_modal.apply": "Aplicar",
   "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog",
   "upload_modal.detect_text": "Detect text from picture",
-  "upload_modal.edit_media": "Edit media",
+  "upload_modal.edit_media": "Edición",
   "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.preview_label": "Preview ({ratio})",
-  "upload_progress.label": "Xubiendo...",
+  "upload_modal.preview_label": "Previsualización ({ratio})",
+  "upload_progress.label": "Xubiendo…",
   "video.close": "Zarrar el videu",
+  "video.download": "Download file",
   "video.exit_fullscreen": "Colar de la pantalla completa",
   "video.expand": "Espander el videu",
   "video.fullscreen": "Pantalla completa",
diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json
index 2ec155362..6a6f9a309 100644
--- a/app/javascript/mastodon/locales/bg.json
+++ b/app/javascript/mastodon/locales/bg.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Добави или премахни от списъците",
   "account.badges.bot": "бот",
+  "account.badges.group": "Group",
   "account.block": "Блокирай",
   "account.block_domain": "скрий всичко от (домейн)",
   "account.blocked": "Блокирани",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Rate limited",
   "alert.unexpected.message": "An unexpected error occurred.",
   "alert.unexpected.title": "Oops!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} per week",
   "boost_modal.combo": "You can press {combo} to skip this next time",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Something went wrong while loading this component.",
   "bundle_modal_error.retry": "Try again",
   "column.blocks": "Blocked users",
+  "column.bookmarks": "Bookmarks",
   "column.community": "Local timeline",
   "column.direct": "Direct messages",
   "column.directory": "Browse profiles",
@@ -82,6 +85,8 @@
   "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": "Раздумай",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Mark media as sensitive",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
   "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no hidden domains yet.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "to open muted users list",
   "keyboard_shortcuts.my_profile": "to open your profile",
   "keyboard_shortcuts.notifications": "to open notifications column",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "to open pinned toots list",
   "keyboard_shortcuts.profile": "to open author's profile",
   "keyboard_shortcuts.reply": "to reply",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blocked users",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Security",
   "notification.favourite": "{name} хареса твоята публикация",
   "notification.follow": "{name} те последва",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} те спомена",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "A poll you have voted in has ended",
   "notification.reblog": "{name} сподели твоята публикация",
   "notifications.clear": "Clear notifications",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Quick filter bar",
   "notifications.column_settings.filter_bar.show": "Show",
   "notifications.column_settings.follow": "Нови последователи:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "Споменавания:",
   "notifications.column_settings.poll": "Poll results:",
   "notifications.column_settings.push": "Push notifications",
@@ -344,6 +355,7 @@
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_status": "Open this status in the moderation interface",
   "status.block": "Block @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.copy": "Copy link to status",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} сподели",
   "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
   "status.redraft": "Delete & re-draft",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "Отговор",
   "status.replyAll": "Reply to thread",
   "status.report": "Report @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Добави медия",
   "upload_error.limit": "File upload limit exceeded.",
   "upload_error.poll": "File upload not allowed with polls.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "Describe for the visually impaired",
   "upload_form.edit": "Edit",
   "upload_form.undo": "Отмяна",
+  "upload_form.video_description": "Describe for people with hearing loss or visual impairment",
   "upload_modal.analyzing_picture": "Analyzing picture…",
   "upload_modal.apply": "Apply",
   "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "Uploading…",
   "video.close": "Close video",
+  "video.download": "Download file",
   "video.exit_fullscreen": "Exit full screen",
   "video.expand": "Expand video",
   "video.fullscreen": "Full screen",
diff --git a/app/javascript/mastodon/locales/bn.json b/app/javascript/mastodon/locales/bn.json
index 5cf5f7a76..283812fdf 100644
--- a/app/javascript/mastodon/locales/bn.json
+++ b/app/javascript/mastodon/locales/bn.json
@@ -1,56 +1,59 @@
 {
-  "account.add_or_remove_from_list": "তালিকা হতে মুছুন অথবা যুক্ত করুন",
+  "account.add_or_remove_from_list": "তালিকাতে যুক্ত বা অপসারণ করুন",
   "account.badges.bot": "বট",
+  "account.badges.group": "Group",
   "account.block": "@{name} কে ব্লক করুন",
-  "account.block_domain": "{domain} থেকে সব লুকান",
-  "account.blocked": "ব্লককৃত",
-  "account.cancel_follow_request": "অসুসারণ অনুরোধ বাতিল করুন",
+  "account.block_domain": "{domain} থেকে সব আড়াল করুন",
+  "account.blocked": "অবরুদ্ধ",
+  "account.cancel_follow_request": "অনুসরণ অনুরোধ বাতিল করুন",
   "account.direct": "@{name} কে সরাসরি বার্তা",
-  "account.domain_blocked": "ডোমেন লুকানো আছে",
-  "account.edit_profile": "প্রোফাইল সম্পাদন করুন",
+  "account.domain_blocked": "ডোমেন গোপন করুন",
+  "account.edit_profile": "প্রোফাইল পরিবর্তন করুন",
   "account.endorse": "নিজের পাতায় দেখান",
   "account.follow": "অনুসরণ করুন",
-  "account.followers": "অনুসরণকারক",
-  "account.followers.empty": "এই ব্যবহারকারীকে কেও এখনো অনুসরণ করে না।",
+  "account.followers": "অনুসরণকারী",
+  "account.followers.empty": "এই সদস্যকে এখনো কেউ অনুসরণ করে না।.",
   "account.follows": "যাদেরকে অনুসরণ করেন",
-  "account.follows.empty": "এই ব্যবহারকারী কাওকে এখনো অনুসরণ করেন না।",
+  "account.follows.empty": "এই সদস্য কাওকে এখনো অনুসরণ করেন না.",
   "account.follows_you": "আপনাকে অনুসরণ করে",
   "account.hide_reblogs": "@{name}'র সমর্থনগুলি লুকিয়ে ফেলুন",
   "account.last_status": "শেষ সক্রিয় ছিল",
-  "account.link_verified_on": "এই লিংকের মালিকানা চেক করা হয়েছে {date} তারিকে",
+  "account.link_verified_on": "এই লিংকের মালিকানা চেক করা হয়েছে {date} তারিখে",
   "account.locked_info": "এই নিবন্ধনের গোপনীয়তার ক্ষেত্র তালা দেওয়া আছে। নিবন্ধনকারী অনুসরণ করার অনুমতি যাদেরকে দেবেন, শুধু তারাই অনুসরণ করতে পারবেন।",
   "account.media": "মিডিয়া",
   "account.mention": "@{name} কে উল্লেখ করুন",
   "account.moved_to": "{name} কে এখানে সরানো হয়েছে:",
   "account.mute": "@{name} কে নিঃশব্দ করুন",
-  "account.mute_notifications": "@{name}র প্রজ্ঞাপন আপনার কাছ থেকে সরিয়ে ফেলুন",
-  "account.muted": "সরানো আছে",
+  "account.mute_notifications": "@{name} র প্রজ্ঞাপন আপনার কাছে নিঃশব্দ করুন",
+  "account.muted": "নিঃশব্দ",
   "account.never_active": "কখনও নয়",
   "account.posts": "টুট",
   "account.posts_with_replies": "টুট এবং মতামত",
-  "account.report": "@{name} কে রিপোর্ট করতে",
-  "account.requested": "অনুমতির অপেক্ষায় আছে। অনুসরণ করার অনুরোধ বাতিল করতে এখানে ক্লিক করুন",
-  "account.share": "@{name}র পাতা অন্যদের দেখান",
-  "account.show_reblogs": "@{name}র সমর্থনগুলো দেখুন",
-  "account.unblock": "@{name}র কার্যকলাপ আবার দেখুন",
-  "account.unblock_domain": "{domain} থেকে আবার দেখুন",
-  "account.unendorse": "আপনার নিজের পাতায় এটা না দেখাতে",
+  "account.report": "@{name} কে রিপোর্ট করুন",
+  "account.requested": "অনুমতির অপেক্ষা। অনুসরণ করার অনুরোধ বাতিল করতে এখানে ক্লিক করুন",
+  "account.share": "@{name} র প্রোফাইল অন্যদের দেখান",
+  "account.show_reblogs": "@{name} র সমর্থনগুলো দেখান",
+  "account.unblock": "@{name} র কার্যকলাপ দেখুন",
+  "account.unblock_domain": "{domain} কে আবার দেখুন",
+  "account.unendorse": "আপনার নিজের পাতায় এটা দেখবেন না",
   "account.unfollow": "অনুসরণ না করতে",
-  "account.unmute": "@{name}র কার্যকলাপ আবার দেখুন",
-  "account.unmute_notifications": "@{name}র প্রজ্ঞাপন দেওয়ার অনুমতি দিন",
+  "account.unmute": "@{name} র কার্যকলাপ আবার দেখুন",
+  "account.unmute_notifications": "@{name} র প্রজ্ঞাপন দেখুন",
   "alert.rate_limited.message": "{retry_time, time, medium} -এর পরে আবার প্রচেষ্টা করুন।",
   "alert.rate_limited.title": "হার সীমিত",
-  "alert.unexpected.message": "অপ্রত্যাশিত একটি সমস্যা হয়েছে।",
+  "alert.unexpected.message": "সমস্যা অপ্রত্যাশিত.",
   "alert.unexpected.title": "ওহো!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "প্রতি সপ্তাহে {count}",
-  "boost_modal.combo": "পরেরবার আপনি {combo} চাপ দিলে এটার শেষে চলে যেতে পারবেন",
-  "bundle_column_error.body": "এই অংশটি দেখতে যেয়ে কোনো সমস্যা হয়েছে।",
+  "boost_modal.combo": "পরেরবার আপনি {combo} টিপলে এটি আর আসবে না",
+  "bundle_column_error.body": "এই অংশটি দেখতে যেয়ে কোনো সমস্যা হয়েছে।.",
   "bundle_column_error.retry": "আবার চেষ্টা করুন",
-  "bundle_column_error.title": "নেটওয়ার্কের সমস্যা হচ্ছে",
+  "bundle_column_error.title": "নেটওয়ার্কের সমস্যা",
   "bundle_modal_error.close": "বন্ধ করুন",
-  "bundle_modal_error.message": "এই অংশটি দেখাতে যেয়ে কোনো সমস্যা হয়েছে।",
+  "bundle_modal_error.message": "এই অংশটি দেখাতে যেয়ে কোনো সমস্যা হয়েছে।.",
   "bundle_modal_error.retry": "আবার চেষ্টা করুন",
-  "column.blocks": "যাদের ব্লক করে রাখা হয়েছে",
+  "column.blocks": "যাদের ব্লক করা হয়েছে",
+  "column.bookmarks": "Bookmarks",
   "column.community": "স্থানীয় সময়সারি",
   "column.direct": "সরাসরি লেখা",
   "column.directory": "প্রোফাইল ব্রাউজ করুন",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "ভোটগ্রহনের সময়",
   "compose_form.poll.option_placeholder": "বিকল্প {number}",
   "compose_form.poll.remove_option": "এই বিকল্পটি মুছে ফেলুন",
+  "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": "টুট",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "এই ছবি বা ভিডিওটি সংবেদনশীল হিসেবে চিহ্নিত করতে",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "এখানে কোনো টুট নেই!",
   "empty_column.account_unavailable": "নিজস্ব পাতা নেই",
   "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": "আপনার কাছে সরাসরি পাঠানো কোনো লেখা নেই। যদি কেও পাঠায়, সেটা এখানে দেখা যাবে।",
   "empty_column.domain_blocks": "এখনও কোনও লুকানো ডোমেন নেই।",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "বন্ধ করা ব্যবহারকারীদের তালিকা খুলতে",
   "keyboard_shortcuts.my_profile": "আপনার নিজের পাতা দেখতে",
   "keyboard_shortcuts.notifications": "প্রজ্ঞাপনের কলাম খুলতে",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "পিন দেওয়া টুটের তালিকা খুলতে",
   "keyboard_shortcuts.profile": "লেখকের পাতা দেখতে",
   "keyboard_shortcuts.reply": "মতামত দিতে",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "এই ব্যবহারকারীর প্রজ্ঞাপন বন্ধ করবেন ?",
   "navigation_bar.apps": "মোবাইলের আপ্প",
   "navigation_bar.blocks": "বন্ধ করা ব্যবহারকারী",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "স্থানীয় সময়রেখা",
   "navigation_bar.compose": "নতুন টুট লিখুন",
   "navigation_bar.direct": "সরাসরি লেখাগুলি",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "নিরাপত্তা",
   "notification.favourite": "{name} আপনার কার্যক্রম পছন্দ করেছেন",
   "notification.follow": "{name} আপনাকে অনুসরণ করেছেন",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} আপনাকে উল্লেখ করেছেন",
+  "notification.own_poll": "আপনার পোল শেষ হয়েছে",
   "notification.poll": "আপনি ভোট দিয়েছিলেন এমন এক  নির্বাচনের ভোটের সময় শেষ হয়েছে",
   "notification.reblog": "{name} আপনার কার্যক্রমে সমর্থন দেখিয়েছেন",
   "notifications.clear": "প্রজ্ঞাপনগুলো মুছে ফেলতে",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "সংক্ষিপ্ত ছাঁকনি অংশ",
   "notifications.column_settings.filter_bar.show": "দেখানো",
   "notifications.column_settings.follow": "নতুন অনুসরণকারীরা:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "প্রজ্ঞাপনগুলো:",
   "notifications.column_settings.poll": "নির্বাচনের ফলাফল:",
   "notifications.column_settings.push": "পুশ প্রজ্ঞাপনগুলি",
@@ -344,6 +355,7 @@
   "status.admin_account": "@{name} র জন্য পরিচালনার ইন্টারফেসে ঢুকুন",
   "status.admin_status": "যায় লেখাটি পরিচালনার ইন্টারফেসে খুলুন",
   "status.block": "@{name} কে ব্লক করুন",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "সমর্থন বাতিল করতে",
   "status.cannot_reblog": "এটিতে সমর্থন দেওয়া যাবেনা",
   "status.copy": "লেখাটির লিংক কপি করতে",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} সমর্থন দিয়েছে",
   "status.reblogs.empty": "এখনো কেও এটাতে সমর্থন দেয়নি। যখন কেও দেয়, সেটা তখন এখানে দেখা যাবে।",
   "status.redraft": "মুছে আবার নতুন করে লিখতে",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "মতামত জানাতে",
   "status.replyAll": "লেখাযুক্ত সবার কাছে মতামত জানাতে",
   "status.report": "@{name} কে রিপোর্ট করতে",
@@ -400,9 +413,11 @@
   "upload_button.label": "ছবি বা ভিডিও যুক্ত করতে (এসব ধরণের: JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_error.limit": "যা যুক্ত করতে চাচ্ছেন সেটি বেশি বড়, এখানকার সর্বাধিকের মেমোরির উপরে চলে গেছে।",
   "upload_error.poll": "নির্বাচনক্ষেত্রে কোনো ফাইল যুক্ত করা যাবেনা।",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "যারা দেখতে পায়না তাদের জন্য এটা বর্ণনা করতে",
   "upload_form.edit": "সম্পাদন",
   "upload_form.undo": "মুছে ফেলতে",
+  "upload_form.video_description": "Describe for people with hearing loss or visual impairment",
   "upload_modal.analyzing_picture": "চিত্র বিশ্লেষণ করা হচ্ছে…",
   "upload_modal.apply": "প্রয়োগ করুন",
   "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "পূর্বরূপ({ratio})",
   "upload_progress.label": "যুক্ত করতে পাঠানো হচ্ছে...",
   "video.close": "ভিডিওটি বন্ধ করতে",
+  "video.download": "ফাইলটি ডাউনলোড করুন",
   "video.exit_fullscreen": "পূর্ণ পর্দা থেকে বাইরে বের হতে",
   "video.expand": "ভিডিওটি বড়ো করতে",
   "video.fullscreen": "পূর্ণ পর্দা করতে",
diff --git a/app/javascript/mastodon/locales/br.json b/app/javascript/mastodon/locales/br.json
index db07f1aba..b6bf0eb8a 100644
--- a/app/javascript/mastodon/locales/br.json
+++ b/app/javascript/mastodon/locales/br.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Ouzhpenn pe lemel ag ar listennadoù",
   "account.badges.bot": "Robot",
+  "account.badges.group": "Strollad",
   "account.block": "Stankañ @{name}",
   "account.block_domain": "Kuzh kement tra a {domain}",
   "account.blocked": "Stanket",
@@ -13,7 +14,7 @@
   "account.followers": "Heilour·ezed·ion",
   "account.followers.empty": "Den na heul an implijour-mañ c'hoazh.",
   "account.follows": "Koumanantoù",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "An implijer-mañ na heul ket den ebet.",
   "account.follows_you": "Ho heul",
   "account.hide_reblogs": "Kuzh toudoù skignet gant @{name}",
   "account.last_status": "Oberiantiz zivezhañ",
@@ -29,21 +30,22 @@
   "account.posts": "Toudoù",
   "account.posts_with_replies": "Toudoù ha respontoù",
   "account.report": "Disklêriañ @{name}",
-  "account.requested": "Awaiting approval",
+  "account.requested": "É c'hortoz bout aprouet. Clikit da nullañ ar pedad heuliañ",
   "account.share": "Skignañ profil @{name}",
   "account.show_reblogs": "Diskouez toudoù a @{name}",
   "account.unblock": "Distankañ @{name}",
   "account.unblock_domain": "Diguzh {domain}",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "Paouez da lakaat war-wel war ar profil",
   "account.unfollow": "Diheuliañ",
   "account.unmute": "Diguzhat @{name}",
   "account.unmute_notifications": "Diguzhat kemennoù a @{name}",
-  "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.",
+  "alert.rate_limited.message": "Klaskit en-dro a-benn {retry_time, time, medium}.",
   "alert.rate_limited.title": "Rate limited",
   "alert.unexpected.message": "Ur fazi dic'hortozet zo degouezhet.",
   "alert.unexpected.title": "C'hem !",
+  "announcement.announcement": "Kemenn",
   "autosuggest_hashtag.per_week": "{count} bep sizhun",
-  "boost_modal.combo": "You can press {combo} to skip this next time",
+  "boost_modal.combo": "Ar wezh kentañ e c'halliot gwaskañ war {combo} evit tremen hebiou",
   "bundle_column_error.body": "Something went wrong while loading this component.",
   "bundle_column_error.retry": "Klask endro",
   "bundle_column_error.title": "Fazi rouedad",
@@ -51,65 +53,68 @@
   "bundle_modal_error.message": "Something went wrong while loading this component.",
   "bundle_modal_error.retry": "Klask endro",
   "column.blocks": "Implijour·ezed·ion stanket",
+  "column.bookmarks": "Sinedoù",
   "column.community": "Red-amzer lec'hel",
   "column.direct": "Kemennadoù prevez",
   "column.directory": "Mont a-dreuz ar profiloù",
   "column.domain_blocks": "Domani kuzhet",
-  "column.favourites": "Favourites",
-  "column.follow_requests": "Follow requests",
-  "column.home": "Home",
-  "column.lists": "Lists",
-  "column.mutes": "Muted users",
-  "column.notifications": "Notifications",
-  "column.pins": "Pinned toot",
-  "column.public": "Federated timeline",
-  "column_back_button.label": "Back",
-  "column_header.hide_settings": "Hide settings",
+  "column.favourites": "Ar re vuiañ-karet",
+  "column.follow_requests": "Pedadoù heuliañ",
+  "column.home": "Degemer",
+  "column.lists": "Listennoù",
+  "column.mutes": "Implijer·ion·ezed kuzhet",
+  "column.notifications": "Kemennoù",
+  "column.pins": "Toudoù spilhennet",
+  "column.public": "Red-amzer kevreet",
+  "column_back_button.label": "Distro",
+  "column_header.hide_settings": "Kuzhat an arventennoù",
   "column_header.moveLeft_settings": "Move column to the left",
   "column_header.moveRight_settings": "Move column to the right",
-  "column_header.pin": "Pin",
-  "column_header.show_settings": "Show settings",
-  "column_header.unpin": "Unpin",
-  "column_subheading.settings": "Settings",
-  "community.column_settings.media_only": "Media only",
-  "compose_form.direct_message_warning": "This toot will only be sent to all the mentioned users.",
-  "compose_form.direct_message_warning_learn_more": "Learn more",
+  "column_header.pin": "Spilhennañ",
+  "column_header.show_settings": "Diskouez an arventennoù",
+  "column_header.unpin": "Dispilhennañ",
+  "column_subheading.settings": "Arventennoù",
+  "community.column_settings.media_only": "Nemet Mediaoù",
+  "compose_form.direct_message_warning": "An toud-mañ a vo kaset nemet d'an implijer·ion·ezed meneget.",
+  "compose_form.direct_message_warning_learn_more": "Gouiet hiroc'h",
   "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
   "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
-  "compose_form.lock_disclaimer.lock": "locked",
-  "compose_form.placeholder": "What is on your mind?",
-  "compose_form.poll.add_option": "Add a choice",
+  "compose_form.lock_disclaimer.lock": "prennet",
+  "compose_form.placeholder": "Petra eh oc'h é soñjal a-barzh ?",
+  "compose_form.poll.add_option": "Ouzhpenniñ un dibab",
   "compose_form.poll.duration": "Poll duration",
-  "compose_form.poll.option_placeholder": "Choice {number}",
-  "compose_form.poll.remove_option": "Remove this choice",
-  "compose_form.publish": "Toot",
-  "compose_form.publish_loud": "{publish}!",
+  "compose_form.poll.option_placeholder": "Dibab {number}",
+  "compose_form.poll.remove_option": "Lemel an dibab-mañ",
+  "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": "Toudañ",
+  "compose_form.publish_loud": "{publish} !",
   "compose_form.sensitive.hide": "Mark media as sensitive",
   "compose_form.sensitive.marked": "Media is marked as sensitive",
   "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
   "compose_form.spoiler.marked": "Text is hidden behind warning",
-  "compose_form.spoiler.unmarked": "Text is not hidden",
+  "compose_form.spoiler.unmarked": "N'eo ket kuzhet an destenn",
   "compose_form.spoiler_placeholder": "Write your warning here",
-  "confirmation_modal.cancel": "Cancel",
+  "confirmation_modal.cancel": "Nullañ",
   "confirmations.block.block_and_report": "Block & Report",
   "confirmations.block.confirm": "Block",
   "confirmations.block.message": "Are you sure you want to block {name}?",
-  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.confirm": "Dilemel",
   "confirmations.delete.message": "Are you sure you want to delete this status?",
-  "confirmations.delete_list.confirm": "Delete",
-  "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?",
-  "confirmations.domain_block.confirm": "Hide entire domain",
+  "confirmations.delete_list.confirm": "Dilemel",
+  "confirmations.delete_list.message": "Ha sur eo hoc'h eus c'hoant da zilemel ar roll-mañ da vat ?",
+  "confirmations.domain_block.confirm": "Kuzhat an domani a-bezh",
   "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.confirm": "Digevreañ",
   "confirmations.logout.message": "Are you sure you want to log out?",
-  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.confirm": "Kuzhat",
   "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.message": "Ha sur oc'h e fell deoc'h kuzhaat {name} ?",
+  "confirmations.redraft.confirm": "Diverkañ ha skrivañ en-dro",
   "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.confirm": "Respont",
   "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.confirm": "Diheuliañ",
   "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
   "conversation.delete": "Delete conversation",
   "conversation.mark_as_read": "Mark as read",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
   "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no hidden domains yet.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "to open muted users list",
   "keyboard_shortcuts.my_profile": "to open your profile",
   "keyboard_shortcuts.notifications": "to open notifications column",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "to open pinned toots list",
   "keyboard_shortcuts.profile": "to open author's profile",
   "keyboard_shortcuts.reply": "to reply",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blocked users",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Security",
   "notification.favourite": "{name} favourited your status",
   "notification.follow": "{name} followed you",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} mentioned you",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "A poll you have voted in has ended",
   "notification.reblog": "{name} boosted your status",
   "notifications.clear": "Clear notifications",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Quick filter bar",
   "notifications.column_settings.filter_bar.show": "Show",
   "notifications.column_settings.follow": "New followers:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "Mentions:",
   "notifications.column_settings.poll": "Poll results:",
   "notifications.column_settings.push": "Push notifications",
@@ -344,6 +355,7 @@
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_status": "Open this status in the moderation interface",
   "status.block": "Block @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.copy": "Copy link to status",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} boosted",
   "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
   "status.redraft": "Delete & re-draft",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "Reply",
   "status.replyAll": "Reply to thread",
   "status.report": "Report @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Add media ({formats})",
   "upload_error.limit": "File upload limit exceeded.",
   "upload_error.poll": "File upload not allowed with polls.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "Describe for the visually impaired",
   "upload_form.edit": "Edit",
   "upload_form.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.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "Uploading…",
   "video.close": "Close video",
+  "video.download": "Download file",
   "video.exit_fullscreen": "Exit full screen",
   "video.expand": "Expand video",
   "video.fullscreen": "Full screen",
diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json
index 396e4a325..65ec5750b 100644
--- a/app/javascript/mastodon/locales/ca.json
+++ b/app/javascript/mastodon/locales/ca.json
@@ -1,14 +1,15 @@
 {
   "account.add_or_remove_from_list": "Afegir o Treure de les llistes",
   "account.badges.bot": "Bot",
+  "account.badges.group": "Grup",
   "account.block": "Bloqueja @{name}",
   "account.block_domain": "Amaga-ho tot de {domain}",
   "account.blocked": "Bloquejat",
   "account.cancel_follow_request": "Anul·la la sol·licitud de seguiment",
   "account.direct": "Missatge directe @{name}",
   "account.domain_blocked": "Domini ocult",
-  "account.edit_profile": "Editar el perfil",
-  "account.endorse": "Recomanar en el teu perfil",
+  "account.edit_profile": "Edita el perfil",
+  "account.endorse": "Recomana en el teu perfil",
   "account.follow": "Segueix",
   "account.followers": "Seguidors",
   "account.followers.empty": "Encara ningú no segueix aquest usuari.",
@@ -26,15 +27,15 @@
   "account.mute_notifications": "Notificacions desactivades de @{name}",
   "account.muted": "Silenciat",
   "account.never_active": "Mai",
-  "account.posts": "Toots",
-  "account.posts_with_replies": "Toots i respostes",
-  "account.report": "Informe @{name}",
+  "account.posts": "Tuts",
+  "account.posts_with_replies": "Tuts i respostes",
+  "account.report": "Informar sobre @{name}",
   "account.requested": "Esperant aprovació. Clic per a cancel·lar la petició de seguiment",
   "account.share": "Comparteix el perfil de @{name}",
   "account.show_reblogs": "Mostra els impulsos de @{name}",
-  "account.unblock": "Desbloca @{name}",
+  "account.unblock": "Desbloqueja @{name}",
   "account.unblock_domain": "Mostra {domain}",
-  "account.unendorse": "No es mostren al perfil",
+  "account.unendorse": "No recomanar en el perfil",
   "account.unfollow": "Deixa de seguir",
   "account.unmute": "Treure silenci de @{name}",
   "account.unmute_notifications": "Activar notificacions de @{name}",
@@ -42,8 +43,9 @@
   "alert.rate_limited.title": "Límit de freqüència",
   "alert.unexpected.message": "S'ha produït un error inesperat.",
   "alert.unexpected.title": "Vaja!",
+  "announcement.announcement": "Anunci",
   "autosuggest_hashtag.per_week": "{count} per setmana",
-  "boost_modal.combo": "Pots premer {combo} per saltar-te això el proper cop",
+  "boost_modal.combo": "Pots prémer {combo} per saltar-te això el proper cop",
   "bundle_column_error.body": "S'ha produït un error en carregar aquest component.",
   "bundle_column_error.retry": "Torna-ho a provar",
   "bundle_column_error.title": "Error de connexió",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "S'ha produït un error en carregar aquest component.",
   "bundle_modal_error.retry": "Torna-ho a provar",
   "column.blocks": "Usuaris bloquejats",
+  "column.bookmarks": "Marcadors",
   "column.community": "Línia de temps local",
   "column.direct": "Missatges directes",
   "column.directory": "Navega els perfils",
@@ -61,7 +64,7 @@
   "column.lists": "Llistes",
   "column.mutes": "Usuaris silenciats",
   "column.notifications": "Notificacions",
-  "column.pins": "Toots fixats",
+  "column.pins": "Tuts fixats",
   "column.public": "Línia de temps federada",
   "column_back_button.label": "Enrere",
   "column_header.hide_settings": "Amaga la configuració",
@@ -72,9 +75,9 @@
   "column_header.unpin": "No fixis",
   "column_subheading.settings": "Configuració",
   "community.column_settings.media_only": "Només multimèdia",
-  "compose_form.direct_message_warning": "Aquest toot només serà enviat als usuaris esmentats.",
+  "compose_form.direct_message_warning": "Aquest tut només serà enviat als usuaris esmentats.",
   "compose_form.direct_message_warning_learn_more": "Aprèn més",
-  "compose_form.hashtag_warning": "Aquest toot no es mostrarà en cap etiqueta ja que no està llistat. Només els toots públics poden ser cercats per etiqueta.",
+  "compose_form.hashtag_warning": "Aquesta tut no es mostrarà en cap etiqueta ja que no està llistat. Només els tuts públics poden ser cercats per etiqueta.",
   "compose_form.lock_disclaimer": "El teu compte no està bloquejat {locked}. Tothom pot seguir-te i veure els teus missatges a seguidors.",
   "compose_form.lock_disclaimer.lock": "bloquejat",
   "compose_form.placeholder": "En què penses?",
@@ -82,7 +85,9 @@
   "compose_form.poll.duration": "Durada de l'enquesta",
   "compose_form.poll.option_placeholder": "Opció {number}",
   "compose_form.poll.remove_option": "Elimina aquesta opció",
-  "compose_form.publish": "Toot",
+  "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_loud": "{publish}!",
   "compose_form.sensitive.hide": "Marcar mèdia com a sensible",
   "compose_form.sensitive.marked": "Mèdia marcat com a sensible",
@@ -95,18 +100,18 @@
   "confirmations.block.confirm": "Bloqueja",
   "confirmations.block.message": "Estàs segur que vols bloquejar a {name}?",
   "confirmations.delete.confirm": "Suprimeix",
-  "confirmations.delete.message": "Estàs segur que vols suprimir aquest toot?",
+  "confirmations.delete.message": "Estàs segur que vols suprimir aquest tut?",
   "confirmations.delete_list.confirm": "Suprimeix",
   "confirmations.delete_list.message": "Estàs segur que vols suprimir permanentment aquesta llista?",
   "confirmations.domain_block.confirm": "Amaga tot el domini",
-  "confirmations.domain_block.message": "Estàs segur, realment segur que vols bloquejar totalment {domain}? En la majoria dels casos bloquejar o silenciar uns pocs objectius és suficient i preferible. No veuràs contingut d’aquest domini en cap de les línies públiques ni en les notificacions. Els teus seguidors d’aquest domini seran eliminats.",
+  "confirmations.domain_block.message": "Estàs segur, realment segur que vols bloquejar totalment {domain}? En la majoria dels casos bloquejar o silenciar uns pocs objectius és suficient i preferible. No veuràs contingut d’aquest domini en cap de les línies de temps ni en les notificacions. Els teus seguidors d’aquest domini seran eliminats.",
   "confirmations.logout.confirm": "Tancar sessió",
   "confirmations.logout.message": "Segur que vols tancar la sessió?",
   "confirmations.mute.confirm": "Silencia",
   "confirmations.mute.explanation": "Això amagarà les seves publicacions i les que els mencionen però encara els permetrà veure les teves i seguir-te.",
   "confirmations.mute.message": "Estàs segur que vols silenciar {name}?",
   "confirmations.redraft.confirm": "Esborrar i refer",
-  "confirmations.redraft.message": "Estàs segur que vols esborrar aquest toot i tornar a redactar-lo? Perderàs totes els impulsos i favorits, i les respostes al toot original es quedaran orfes.",
+  "confirmations.redraft.message": "Estàs segur que vols esborrar aquest tut i tornar a redactar-lo? Perdràs tots els impulsos i favorits, i les respostes al tut original es quedaran orfes.",
   "confirmations.reply.confirm": "Respon",
   "confirmations.reply.message": "Responen ara es sobreescriurà el missatge que estàs editant. Estàs segur que vols continuar?",
   "confirmations.unfollow.confirm": "Deixa de seguir",
@@ -119,8 +124,8 @@
   "directory.local": "Només de {domain}",
   "directory.new_arrivals": "Arribades noves",
   "directory.recently_active": "Recentment actius",
-  "embed.instructions": "Incrusta aquest toot al lloc web copiant el codi a continuació.",
-  "embed.preview": "Aquí tenim quin aspecte tindrá:",
+  "embed.instructions": "Incrusta aquest tut al lloc web copiant el codi a continuació.",
+  "embed.preview": "Aquí tenim quin aspecte tindrà:",
   "emoji_button.activity": "Activitat",
   "emoji_button.custom": "Personalitzat",
   "emoji_button.flags": "Banderes",
@@ -135,19 +140,20 @@
   "emoji_button.search_results": "Resultats de la cerca",
   "emoji_button.symbols": "Símbols",
   "emoji_button.travel": "Viatges i Llocs",
-  "empty_column.account_timeline": "No hi ha toots aquí!",
+  "empty_column.account_timeline": "No hi ha tuts aquí!",
   "empty_column.account_unavailable": "Perfil no disponible",
   "empty_column.blocks": "Encara no has bloquejat cap usuari.",
+  "empty_column.bookmarked_statuses": "Encara no tens marcat cap tut. Quan marquis un apareixerà aquí.",
   "empty_column.community": "La línia de temps local és buida. Escriu alguna cosa públicament per a fer rodar la pilota!",
   "empty_column.direct": "Encara no tens missatges directes. Quan enviïs o rebis un, es mostrarà aquí.",
   "empty_column.domain_blocks": "Encara no hi ha dominis ocults.",
-  "empty_column.favourited_statuses": "Encara no tens cap toot favorit. Quan en tinguis, apareixerà aquí.",
-  "empty_column.favourites": "Encara ningú ha marcat aquest toot com a favorit. Quan algú ho faci, apareixera aquí.",
-  "empty_column.follow_requests": "Encara no teniu cap petició de seguiment. Quan rebis una, apareixerà aquí.",
+  "empty_column.favourited_statuses": "Encara no has marcat com a favorit cap tut. Quan en facis, apareixerà aquí.",
+  "empty_column.favourites": "Ningú no ha marcat aquest tut com a preferit encara. Quan algú ho faci, apareixerà aquí.",
+  "empty_column.follow_requests": "Encara no tens cap petició de seguiment. Quan rebis una, apareixerà aquí.",
   "empty_column.hashtag": "Encara no hi ha res en aquesta etiqueta.",
   "empty_column.home": "Encara no segueixes ningú. Visita {public} o fes cerca per començar i conèixer altres usuaris.",
   "empty_column.home.public_timeline": "la línia de temps pública",
-  "empty_column.list": "Encara no hi ha res en aquesta llista. Quan els membres d'aquesta llista publiquin nous toots, apareixeran aquí.",
+  "empty_column.list": "Encara no hi ha res en aquesta llista. Quan els membres d'aquesta llista publiquin nous tuts, apareixeran aquí.",
   "empty_column.lists": "Encara no tens cap llista. Quan en facis una, apareixerà aquí.",
   "empty_column.mutes": "Encara no has silenciat cap usuari.",
   "empty_column.notifications": "Encara no tens notificacions. Interactua amb altres per iniciar la conversa.",
@@ -163,7 +169,7 @@
   "getting_started.documentation": "Documentació",
   "getting_started.heading": "Començant",
   "getting_started.invite": "Convida gent",
-  "getting_started.open_source_notice": "Mastodon és un programari de codi obert. Pots contribuir o informar de problemes a GitHub a {github}.",
+  "getting_started.open_source_notice": "Mastodon és un programari de codi obert. Pots contribuir-hi o informar de problemes a GitHub a {github}.",
   "getting_started.security": "Seguretat",
   "getting_started.terms": "Termes del servei",
   "hashtag.column_header.tag_mode.all": "i {additional}",
@@ -176,8 +182,8 @@
   "hashtag.column_settings.tag_mode.none": "Cap d’aquests",
   "hashtag.column_settings.tag_toggle": "Inclou etiquetes addicionals per a aquesta columna",
   "home.column_settings.basic": "Bàsic",
-  "home.column_settings.show_reblogs": "Mostrar impulsos",
-  "home.column_settings.show_replies": "Mostrar respostes",
+  "home.column_settings.show_reblogs": "Mostra els impulsos",
+  "home.column_settings.show_replies": "Mostra les respostes",
   "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}}",
@@ -190,27 +196,27 @@
   "introduction.federation.local.text": "Les publicacions públiques de les persones del teu mateix servidor apareixeran a la línia de temps local.",
   "introduction.interactions.action": "Finalitza el tutorial!",
   "introduction.interactions.favourite.headline": "Favorit",
-  "introduction.interactions.favourite.text": "Pots desar un toot per a més tard i deixar que l'autor sàpiga que t'ha agradat, marcant-lo com a favorit.",
+  "introduction.interactions.favourite.text": "Pots desar un tut per a més tard i deixar que l'autor sàpiga que t'ha agradat, marcant-lo com a favorit.",
   "introduction.interactions.reblog.headline": "Impuls",
-  "introduction.interactions.reblog.text": "Pots compartir amb els teus seguidors els toots d'altres usuaris, impulsant-los.",
+  "introduction.interactions.reblog.text": "Pots compartir amb els teus seguidors els tuts d'altres usuaris, impulsant-los.",
   "introduction.interactions.reply.headline": "Respondre",
-  "introduction.interactions.reply.text": "Pots respondre als toots d'altres persones i als teus propis, que els unirà en una conversa.",
+  "introduction.interactions.reply.text": "Pots respondre als tuts d'altres persones i als teus propis, que els unirà en una conversa.",
   "introduction.welcome.action": "Som-hi!",
   "introduction.welcome.headline": "Primers passos",
   "introduction.welcome.text": "Benvingut al fedivers! En uns moments podràs emetre missatges i conversar amb els teus amics en una gran varietat de servidors. Però aquest servidor, {domain}, és especial: allotja el teu perfil així que recorda el seu nom.",
-  "keyboard_shortcuts.back": "navegar enrera",
+  "keyboard_shortcuts.back": "navegar enrere",
   "keyboard_shortcuts.blocked": "per obrir la llista d'usuaris bloquejats",
   "keyboard_shortcuts.boost": "impulsar",
-  "keyboard_shortcuts.column": "per a centrar un toot en una de les columnes",
-  "keyboard_shortcuts.compose": "per centrar l'area de composició de text",
+  "keyboard_shortcuts.column": "per a centrar un tut en una de les columnes",
+  "keyboard_shortcuts.compose": "per centrar l'àrea de composició de text",
   "keyboard_shortcuts.description": "Descripció",
   "keyboard_shortcuts.direct": "per obrir la columna de missatges directes",
   "keyboard_shortcuts.down": "per baixar en la llista",
-  "keyboard_shortcuts.enter": "ampliar el toot",
+  "keyboard_shortcuts.enter": "per a obrir el tut",
   "keyboard_shortcuts.favourite": "afavorir",
   "keyboard_shortcuts.favourites": "per obrir la llista de favorits",
   "keyboard_shortcuts.federated": "per obrir la línia de temps federada",
-  "keyboard_shortcuts.heading": "Dreçeres de teclat",
+  "keyboard_shortcuts.heading": "Dreceres de teclat",
   "keyboard_shortcuts.home": "per a obrir la línia de temps Inici",
   "keyboard_shortcuts.hotkey": "Tecla d'accés directe",
   "keyboard_shortcuts.legend": "per a mostrar aquesta llegenda",
@@ -219,16 +225,17 @@
   "keyboard_shortcuts.muted": "per a obrir la llista d'usuaris silenciats",
   "keyboard_shortcuts.my_profile": "per a obrir el teu perfil",
   "keyboard_shortcuts.notifications": "per a obrir la columna de notificacions",
-  "keyboard_shortcuts.pinned": "per a obrir la llista de toots fixats",
+  "keyboard_shortcuts.open_media": "obrir mèdia",
+  "keyboard_shortcuts.pinned": "per a obrir la llista de tuts fixats",
   "keyboard_shortcuts.profile": "per a obrir el perfil de l'autor",
   "keyboard_shortcuts.reply": "respondre",
   "keyboard_shortcuts.requests": "per a obrir la llista de sol·licituds de seguiment",
   "keyboard_shortcuts.search": "per a centrar la cerca",
   "keyboard_shortcuts.start": "per a obrir la columna \"Començar\"",
-  "keyboard_shortcuts.toggle_hidden": "per a mostrar/amagar text sota CW",
-  "keyboard_shortcuts.toggle_sensitivity": "per a mostrar/amagar mèdia",
-  "keyboard_shortcuts.toot": "per a començar un toot nou de trinca",
-  "keyboard_shortcuts.unfocus": "descentrar l'area de composició de text/cerca",
+  "keyboard_shortcuts.toggle_hidden": "per a mostrar o amagar text sota CW",
+  "keyboard_shortcuts.toggle_sensitivity": "per a mostrar o amagar contingut multimèdia",
+  "keyboard_shortcuts.toot": "per a començar un tut nou de trinca",
+  "keyboard_shortcuts.unfocus": "descentrar l'àrea de composició de text/cerca",
   "keyboard_shortcuts.up": "moure amunt en la llista",
   "lightbox.close": "Tancar",
   "lightbox.next": "Següent",
@@ -251,13 +258,14 @@
   "mute_modal.hide_notifications": "Amagar notificacions d'aquest usuari?",
   "navigation_bar.apps": "Apps mòbils",
   "navigation_bar.blocks": "Usuaris bloquejats",
+  "navigation_bar.bookmarks": "Marcadors",
   "navigation_bar.community_timeline": "Línia de temps Local",
-  "navigation_bar.compose": "Redacta nou toot",
+  "navigation_bar.compose": "Redacta un nou tut",
   "navigation_bar.direct": "Missatges directes",
   "navigation_bar.discover": "Descobreix",
   "navigation_bar.domain_blocks": "Dominis ocults",
   "navigation_bar.edit_profile": "Editar perfil",
-  "navigation_bar.favourites": "Favorits",
+  "navigation_bar.favourites": "Preferits",
   "navigation_bar.filters": "Paraules silenciades",
   "navigation_bar.follow_requests": "Sol·licituds de seguiment",
   "navigation_bar.follows_and_followers": "Seguits i seguidors",
@@ -267,29 +275,32 @@
   "navigation_bar.logout": "Tancar sessió",
   "navigation_bar.mutes": "Usuaris silenciats",
   "navigation_bar.personal": "Personal",
-  "navigation_bar.pins": "Toots fixats",
+  "navigation_bar.pins": "Tuts fixats",
   "navigation_bar.preferences": "Preferències",
   "navigation_bar.public_timeline": "Línia de temps federada",
   "navigation_bar.security": "Seguretat",
   "notification.favourite": "{name} ha afavorit el teu estat",
   "notification.follow": "{name} et segueix",
+  "notification.follow_request": "{name} ha sol·licitat seguir-te",
   "notification.mention": "{name} t'ha esmentat",
+  "notification.own_poll": "La teva enquesta ha finalitzat",
   "notification.poll": "Ha finalitzat una enquesta en la que has votat",
   "notification.reblog": "{name} ha impulsat el teu estat",
   "notifications.clear": "Netejar notificacions",
-  "notifications.clear_confirmation": "Estàs segur que vols esborrar permanenment totes les teves notificacions?",
+  "notifications.clear_confirmation": "Estàs segur que vols esborrar permanentment totes les teves notificacions?",
   "notifications.column_settings.alert": "Notificacions d'escriptori",
-  "notifications.column_settings.favourite": "Favorits:",
+  "notifications.column_settings.favourite": "Preferits:",
   "notifications.column_settings.filter_bar.advanced": "Mostra totes les categories",
   "notifications.column_settings.filter_bar.category": "Barra ràpida de filtres",
   "notifications.column_settings.filter_bar.show": "Mostra",
   "notifications.column_settings.follow": "Nous seguidors:",
+  "notifications.column_settings.follow_request": "Nova sol·licitud de seguiment:",
   "notifications.column_settings.mention": "Mencions:",
   "notifications.column_settings.poll": "Resultats de l’enquesta:",
   "notifications.column_settings.push": "Notificacions push",
   "notifications.column_settings.reblog": "Impulsos:",
-  "notifications.column_settings.show": "Mostrar en la columna",
-  "notifications.column_settings.sound": "Reproduïr so",
+  "notifications.column_settings.show": "Mostra en la columna",
+  "notifications.column_settings.sound": "Reproduir so",
   "notifications.filter.all": "Tots",
   "notifications.filter.boosts": "Impulsos",
   "notifications.filter.favourites": "Favorits",
@@ -333,20 +344,21 @@
   "search_popout.search_format": "Format de cerca avançada",
   "search_popout.tips.full_text": "Text simple recupera publicacions que has escrit, les marcades com a favorites, les impulsades o en les que has estat esmentat, així com usuaris, noms d'usuari i etiquetes.",
   "search_popout.tips.hashtag": "etiqueta",
-  "search_popout.tips.status": "estat",
+  "search_popout.tips.status": "tut",
   "search_popout.tips.text": "El text simple retorna coincidències amb els noms de visualització, els noms d'usuari i les etiquetes",
   "search_popout.tips.user": "usuari",
   "search_results.accounts": "Gent",
   "search_results.hashtags": "Etiquetes",
-  "search_results.statuses": "Toots",
-  "search_results.statuses_fts_disabled": "La cerca de toots pel seu contingut no està habilitada en aquest servidor Mastodon.",
+  "search_results.statuses": "Tuts",
+  "search_results.statuses_fts_disabled": "La cerca de tuts pel seu contingut no està habilitada en aquest servidor Mastodon.",
   "search_results.total": "{count, number} {count, plural, one {resultat} other {resultats}}",
   "status.admin_account": "Obre l'interfície de moderació per a @{name}",
-  "status.admin_status": "Obre aquest toot a la interfície de moderació",
+  "status.admin_status": "Obre aquest tut a la interfície de moderació",
   "status.block": "Bloqueja @{name}",
+  "status.bookmark": "Marcador",
   "status.cancel_reblog_private": "Desfer l'impuls",
   "status.cannot_reblog": "Aquesta publicació no pot ser impulsada",
-  "status.copy": "Copia l'enllaç al toot",
+  "status.copy": "Copia l'enllaç al tut",
   "status.delete": "Esborrar",
   "status.detailed_status": "Visualització detallada de la conversa",
   "status.direct": "Missatge directe @{name}",
@@ -361,13 +373,14 @@
   "status.mute_conversation": "Silenciar conversació",
   "status.open": "Ampliar aquest estat",
   "status.pin": "Fixat en el perfil",
-  "status.pinned": "Toot fixat",
+  "status.pinned": "Tut fixat",
   "status.read_more": "Llegir més",
   "status.reblog": "Impuls",
   "status.reblog_private": "Impulsar a l'audiència original",
   "status.reblogged_by": "{name} ha impulsat",
-  "status.reblogs.empty": "Encara ningú no ha impulsat aquest toot. Quan algú ho faci, apareixeran aquí.",
+  "status.reblogs.empty": "Encara ningú no ha impulsat aquest tut. Quan algú ho faci, apareixeran aquí.",
   "status.redraft": "Esborrar i reescriure",
+  "status.remove_bookmark": "Suprimeix el marcador",
   "status.reply": "Respondre",
   "status.replyAll": "Respondre al tema",
   "status.report": "Informar sobre @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Afegir multimèdia (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_error.limit": "S'ha superat el límit de càrrega d'arxius.",
   "upload_error.poll": "No es permet l'enviament de fitxers en les enquestes.",
+  "upload_form.audio_description": "Descriviu per a les persones amb pèrdua auditiva",
   "upload_form.description": "Descriure els problemes visuals",
   "upload_form.edit": "Edita",
   "upload_form.undo": "Esborra",
+  "upload_form.video_description": "Descriu per a les persones amb pèrdua auditiva o deficiència visual",
   "upload_modal.analyzing_picture": "Analitzant imatge…",
   "upload_modal.apply": "Aplica",
   "upload_modal.description_placeholder": "Uns salts ràpids de guineu marró sobre el gos gandul",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Previsualitza ({ratio})",
   "upload_progress.label": "Pujant...",
   "video.close": "Tancar el vídeo",
+  "video.download": "Descarrega l’arxiu",
   "video.exit_fullscreen": "Sortir de pantalla completa",
   "video.expand": "Ampliar el vídeo",
   "video.fullscreen": "Pantalla completa",
diff --git a/app/javascript/mastodon/locales/co.json b/app/javascript/mastodon/locales/co.json
index 6c680f13f..8f3c99f8a 100644
--- a/app/javascript/mastodon/locales/co.json
+++ b/app/javascript/mastodon/locales/co.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Aghjunghje o toglie da e liste",
   "account.badges.bot": "Bot",
+  "account.badges.group": "Gruppu",
   "account.block": "Bluccà @{name}",
   "account.block_domain": "Piattà tuttu da {domain}",
   "account.blocked": "Bluccatu",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Ghjettu limitatu",
   "alert.unexpected.message": "Un prublemu inaspettatu hè accadutu.",
   "alert.unexpected.title": "Uups!",
+  "announcement.announcement": "Annunziu",
   "autosuggest_hashtag.per_week": "{count} per settimana",
   "boost_modal.combo": "Pudete appughjà nant'à {combo} per saltà quessa a prussima volta",
   "bundle_column_error.body": "C'hè statu un prublemu caricandu st'elementu.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "C'hè statu un prublemu caricandu st'elementu.",
   "bundle_modal_error.retry": "Pruvà torna",
   "column.blocks": "Utilizatori bluccati",
+  "column.bookmarks": "Segnalibri",
   "column.community": "Linea pubblica lucale",
   "column.direct": "Missaghji diretti",
   "column.directory": "Percorre i prufili",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Durata di u scandagliu",
   "compose_form.poll.option_placeholder": "Scelta {number}",
   "compose_form.poll.remove_option": "Toglie sta scelta",
+  "compose_form.poll.switch_to_multiple": "Cambià u scandagliu per accittà parechje scelte",
+  "compose_form.poll.switch_to_single": "Cambià u scandagliu per ùn accittà ch'una scelta",
   "compose_form.publish": "Toot",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Indicà u media cum'è sensibile",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Nisun statutu quì!",
   "empty_column.account_unavailable": "Prufile micca dispunibule",
   "empty_column.blocks": "Per avà ùn avete bluccatu manc'un utilizatore.",
+  "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": "Ùn avete ancu nisun missaghju direttu. S'è voi mandate o ricevete unu, u vidarete quì.",
   "empty_column.domain_blocks": "Ùn c'hè manc'un duminiu bluccatu avà.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "per apre a lista di l'utilizatori piattati",
   "keyboard_shortcuts.my_profile": "per apre u vostru prufile",
   "keyboard_shortcuts.notifications": "per apre a culonna di nutificazione",
+  "keyboard_shortcuts.open_media": "per apre i media",
   "keyboard_shortcuts.pinned": "per apre a lista di statuti puntarulati",
   "keyboard_shortcuts.profile": "per apre u prufile di l'autore",
   "keyboard_shortcuts.reply": "risponde",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Piattà nutificazione da st'utilizatore?",
   "navigation_bar.apps": "Applicazione per u telefuninu",
   "navigation_bar.blocks": "Utilizatori bluccati",
+  "navigation_bar.bookmarks": "Segnalibri",
   "navigation_bar.community_timeline": "Linea pubblica lucale",
   "navigation_bar.compose": "Scrive un novu statutu",
   "navigation_bar.direct": "Missaghji diretti",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Sicurità",
   "notification.favourite": "{name} hà aghjuntu u vostru statutu à i so favuriti",
   "notification.follow": "{name} v'hà seguitatu",
+  "notification.follow_request": "{name} vole abbunassi à u vostru contu",
   "notification.mention": "{name} v'hà mintuvatu",
+  "notification.own_poll": "U vostru scandagliu hè compiu",
   "notification.poll": "Un scandagliu induve avete vutatu hè finitu",
   "notification.reblog": "{name} hà spartutu u vostru statutu",
   "notifications.clear": "Purgà e nutificazione",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Barra di ricerca pronta",
   "notifications.column_settings.filter_bar.show": "Mustrà",
   "notifications.column_settings.follow": "Abbunati novi:",
+  "notifications.column_settings.follow_request": "Nove dumande d'abbunamentu:",
   "notifications.column_settings.mention": "Minzione:",
   "notifications.column_settings.poll": "Risultati:",
   "notifications.column_settings.push": "Nutificazione Push",
@@ -344,6 +355,7 @@
   "status.admin_account": "Apre l'interfaccia di muderazione per @{name}",
   "status.admin_status": "Apre stu statutu in l'interfaccia di muderazione",
   "status.block": "Bluccà @{name}",
+  "status.bookmark": "Segnalibru",
   "status.cancel_reblog_private": "Ùn sparte più",
   "status.cannot_reblog": "Stu statutu ùn pò micca esse spartutu",
   "status.copy": "Cupià ligame indè u statutu",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} hà spartutu",
   "status.reblogs.empty": "Per avà nisunu hà spartutu u statutu. Quandu qualch'unu u sparterà, u so contu sarà mustratu quì.",
   "status.redraft": "Sguassà è riscrive",
+  "status.remove_bookmark": "Toglie segnalibru",
   "status.reply": "Risponde",
   "status.replyAll": "Risponde à tutti",
   "status.report": "Palisà @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Aghjunghje un media (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_error.limit": "Limita di caricamentu di fugliali trapassata.",
   "upload_error.poll": "Ùn si pò micca caricà fugliali cù i scandagli.",
+  "upload_form.audio_description": "Discrizzione per i ciochi",
   "upload_form.description": "Discrive per i malvistosi",
   "upload_form.edit": "Mudificà",
   "upload_form.undo": "Sguassà",
+  "upload_form.video_description": "Discrizzione per i ciochi o cechi",
   "upload_modal.analyzing_picture": "Analisi di u ritrattu…",
   "upload_modal.apply": "Affettà",
   "upload_modal.description_placeholder": "Chì tempi brevi ziu, quandu solfeghji",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Vista ({ratio})",
   "upload_progress.label": "Caricamentu...",
   "video.close": "Chjudà a video",
+  "video.download": "Scaricà fugliale",
   "video.exit_fullscreen": "Caccià u pienu screnu",
   "video.expand": "Ingrandà a video",
   "video.fullscreen": "Pienu screnu",
diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json
index fc807d45e..f180b7f94 100644
--- a/app/javascript/mastodon/locales/cs.json
+++ b/app/javascript/mastodon/locales/cs.json
@@ -1,14 +1,15 @@
 {
   "account.add_or_remove_from_list": "Přidat nebo odstranit ze seznamů",
   "account.badges.bot": "Robot",
+  "account.badges.group": "Skupina",
   "account.block": "Zablokovat uživatele @{name}",
-  "account.block_domain": "Skrýt vše z {domain}",
-  "account.blocked": "Blokován/a",
-  "account.cancel_follow_request": "Zrušit požadavek o sledování",
-  "account.direct": "Poslat přímou zprávu uživateli @{name}",
+  "account.block_domain": "Skrýt vše ze serveru {domain}",
+  "account.blocked": "Blokováno",
+  "account.cancel_follow_request": "Zrušit žádost o sledování",
+  "account.direct": "Poslat uživateli @{name} přímou zprávu",
   "account.domain_blocked": "Doména skryta",
   "account.edit_profile": "Upravit profil",
-  "account.endorse": "Představit na profilu",
+  "account.endorse": "Zvýraznit na profilu",
   "account.follow": "Sledovat",
   "account.followers": "Sledující",
   "account.followers.empty": "Tohoto uživatele ještě nikdo nesleduje.",
@@ -21,42 +22,44 @@
   "account.locked_info": "Stav soukromí tohoto účtu je nastaven na zamčeno. Jeho vlastník ručně posuzuje, kdo ho může sledovat.",
   "account.media": "Média",
   "account.mention": "Zmínit uživatele @{name}",
-  "account.moved_to": "{name} se přesunul/a na:",
+  "account.moved_to": "Uživatel {name} se přesunul na:",
   "account.mute": "Skrýt uživatele @{name}",
   "account.mute_notifications": "Skrýt oznámení od uživatele @{name}",
-  "account.muted": "Skryt/a",
+  "account.muted": "Uživatel skryt",
   "account.never_active": "Nikdy",
   "account.posts": "Tooty",
   "account.posts_with_replies": "Tooty a odpovědi",
   "account.report": "Nahlásit uživatele @{name}",
-  "account.requested": "Čekám na schválení. Kliknutím zrušíte požadavek o sledování",
+  "account.requested": "Čeká na schválení. Kliknutím žádost o sledování zrušíte",
   "account.share": "Sdílet profil uživatele @{name}",
   "account.show_reblogs": "Zobrazit boosty od uživatele @{name}",
   "account.unblock": "Odblokovat uživatele @{name}",
   "account.unblock_domain": "Odkrýt doménu {domain}",
-  "account.unendorse": "Nepředstavit na profilu",
+  "account.unendorse": "Nezvýrazňovat na profilu",
   "account.unfollow": "Přestat sledovat",
   "account.unmute": "Odkrýt uživatele @{name}",
   "account.unmute_notifications": "Odkrýt oznámení od uživatele @{name}",
-  "alert.rate_limited.message": "Prosím zkuste to znovu za {retry_time, time, medium}.",
+  "alert.rate_limited.message": "Zkuste to prosím znovu za {retry_time, time, medium}.",
   "alert.rate_limited.title": "Rychlost omezena",
   "alert.unexpected.message": "Objevila se neočekávaná chyba.",
   "alert.unexpected.title": "Jejda!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} za týden",
   "boost_modal.combo": "Příště můžete pro přeskočení stisknout {combo}",
-  "bundle_column_error.body": "Při načítání tohoto komponentu se něco pokazilo.",
+  "bundle_column_error.body": "Při načítání této komponenty se něco pokazilo.",
   "bundle_column_error.retry": "Zkuste to znovu",
   "bundle_column_error.title": "Chyba sítě",
   "bundle_modal_error.close": "Zavřít",
-  "bundle_modal_error.message": "Při načítání tohoto komponentu se něco pokazilo.",
+  "bundle_modal_error.message": "Při načítání této komponenty se něco pokazilo.",
   "bundle_modal_error.retry": "Zkusit znovu",
   "column.blocks": "Blokovaní uživatelé",
+  "column.bookmarks": "Záložky",
   "column.community": "Místní časová osa",
   "column.direct": "Přímé zprávy",
   "column.directory": "Prozkoumat profily",
   "column.domain_blocks": "Skryté domény",
   "column.favourites": "Oblíbené",
-  "column.follow_requests": "Požadavky o sledování",
+  "column.follow_requests": "Žádosti o sledování",
   "column.home": "Domů",
   "column.lists": "Seznamy",
   "column.mutes": "Skrytí uživatelé",
@@ -75,13 +78,15 @@
   "compose_form.direct_message_warning": "Tento toot bude odeslán pouze zmíněným uživatelům.",
   "compose_form.direct_message_warning_learn_more": "Zjistit více",
   "compose_form.hashtag_warning": "Tento toot nebude zobrazen pod žádným hashtagem, neboť je neuvedený. Pouze veřejné tooty mohou být vyhledány podle hashtagu.",
-  "compose_form.lock_disclaimer": "Váš účet není {locked}. Kdokoliv vás může sledovat a vidět vaše příspěvky pouze pro sledující.",
+  "compose_form.lock_disclaimer": "Váš účet není {locked}. Kdokoliv vás může sledovat a vidět vaše příspěvky učené pouze pro sledující.",
   "compose_form.lock_disclaimer.lock": "uzamčen",
   "compose_form.placeholder": "Co se vám honí hlavou?",
   "compose_form.poll.add_option": "Přidat volbu",
-  "compose_form.poll.duration": "Délka ankety",
+  "compose_form.poll.duration": "Doba trvání ankety",
   "compose_form.poll.option_placeholder": "Volba {number}",
   "compose_form.poll.remove_option": "Odstranit tuto volbu",
+  "compose_form.poll.switch_to_multiple": "Povolit u ankety výběr více možností",
+  "compose_form.poll.switch_to_single": "Povolit u ankety výběr jediné možnosti",
   "compose_form.publish": "Tootnout",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Označit média jako citlivá",
@@ -93,30 +98,30 @@
   "confirmation_modal.cancel": "Zrušit",
   "confirmations.block.block_and_report": "Blokovat a nahlásit",
   "confirmations.block.confirm": "Blokovat",
-  "confirmations.block.message": "Jste si jistý/á, že chcete zablokovat uživatele {name}?",
+  "confirmations.block.message": "Opravdu chcete zablokovat uživatele {name}?",
   "confirmations.delete.confirm": "Smazat",
-  "confirmations.delete.message": "Jste si jistý/á, že chcete smazat tento toot?",
+  "confirmations.delete.message": "Opravdu chcete smazat tento toot?",
   "confirmations.delete_list.confirm": "Smazat",
-  "confirmations.delete_list.message": "Jste si jistý/á, že chcete tento seznam navždy smazat?",
+  "confirmations.delete_list.message": "Opravdu chcete tento seznam navždy smazat?",
   "confirmations.domain_block.confirm": "Skrýt celou doménu",
-  "confirmations.domain_block.message": "Jste si opravdu, opravdu jistý/á, že chcete blokovat celou doménu {domain}? Ve většině případů stačí zablokovat nebo skrýt pár konkrétních uživatelů, což se doporučuje. 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.domain_block.message": "Opravdu chcete blokovat celou doménu {domain}? Ve většině případů stačí zablokovat 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.logout.confirm": "Odhlásit",
-  "confirmations.logout.message": "Jste si jistý/á, že se chcete odhlásit?",
+  "confirmations.logout.message": "Opravdu se chcete odhlásit?",
   "confirmations.mute.confirm": "Skrýt",
-  "confirmations.mute.explanation": "Tohle skryje jeho příspěvky a příspěvky, které ho zmiňují, ale uživatel pořád bude moci vidět vaše příspěvky a sledovat vás.",
-  "confirmations.mute.message": "Jste si jistý/á, že chcete skrýt uživatele {name}?",
+  "confirmations.mute.explanation": "Tohle skryje uživatelovi příspěvky a příspěvky, které ho zmiňují, ale uživatel stále uvidí vaše příspěvky a může vás sledovat.",
+  "confirmations.mute.message": "Opravdu chcete uživatele {name} skrýt?",
   "confirmations.redraft.confirm": "Smazat a přepsat",
   "confirmations.redraft.message": "Jste si jistý/á, že chcete smazat a přepsat tento toot? Oblíbení a boosty budou ztraceny a odpovědi na původní příspěvek budou opuštěny.",
   "confirmations.reply.confirm": "Odpovědět",
-  "confirmations.reply.message": "Odpovězením nyní přepíšete zprávu, kterou aktuálně píšete. Jste si jistý/á, že chcete pokračovat?",
+  "confirmations.reply.message": "Odpověď přepíše vaši rozepsanou zprávu. Opravdu chcete pokračovat?",
   "confirmations.unfollow.confirm": "Přestat sledovat",
-  "confirmations.unfollow.message": "jste si jistý/á, že chcete přestat sledovat uživatele {name}?",
+  "confirmations.unfollow.message": "Opravdu chcete uživatele {name} přestat sledovat?",
   "conversation.delete": "Smazat konverzaci",
   "conversation.mark_as_read": "Označit jako přečtenou",
   "conversation.open": "Zobrazit konverzaci",
   "conversation.with": "S {names}",
   "directory.federated": "Ze známého fedivesmíru",
-  "directory.local": "Pouze z {domain}",
+  "directory.local": "Pouze z domény {domain}",
   "directory.new_arrivals": "Nově příchozí",
   "directory.recently_active": "Nedávno aktivní",
   "embed.instructions": "Pro přidání tootu na vaši webovou stránku zkopírujte níže uvedený kód.",
@@ -127,33 +132,34 @@
   "emoji_button.food": "Jídla a nápoje",
   "emoji_button.label": "Vložit emoji",
   "emoji_button.nature": "Příroda",
-  "emoji_button.not_found": "Žádná emoji!! (╯°□°)╯︵ ┻━┻",
+  "emoji_button.not_found": "Žádné emoji! (╯°□°)╯︵ ┻━┻",
   "emoji_button.objects": "Předměty",
   "emoji_button.people": "Lidé",
-  "emoji_button.recent": "Často používaná",
+  "emoji_button.recent": "Často používané",
   "emoji_button.search": "Hledat…",
   "emoji_button.search_results": "Výsledky hledání",
   "emoji_button.symbols": "Symboly",
   "emoji_button.travel": "Cestování a místa",
-  "empty_column.account_timeline": "Tady nejsou žádné tooty!",
+  "empty_column.account_timeline": "Nejsou tu žádné tooty!",
   "empty_column.account_unavailable": "Profil nedostupný",
-  "empty_column.blocks": "Ještě jste nezablokoval/a žádného uživatele.",
+  "empty_column.blocks": "Ještě jste nezablokovali žádného uživatele.",
+  "empty_column.bookmarked_statuses": "Ještě nemáte v záložkách žádné tooty. Pokud 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": "Ještě nemáte žádné přímé zprávy. Pokud nějakou pošlete nebo dostanete, zobrazí se zde.",
-  "empty_column.domain_blocks": "Ještě nejsou žádné skryté domény.",
+  "empty_column.domain_blocks": "Ještě nemáte žádné skryté domény.",
   "empty_column.favourited_statuses": "Ještě nemáte žádné oblíbené tooty. Pokud si nějaký oblíbíte, zobrazí se zde.",
   "empty_column.favourites": "Tento toot si ještě nikdo neoblíbil. Pokud to někdo udělá, zobrazí se zde.",
-  "empty_column.follow_requests": "Ještě nemáte žádné požadavky o sledování. Pokud nějaký obdržíte, zobrazí se zde.",
+  "empty_column.follow_requests": "Ještě nemáte žádné žádosti o sledování. Pokud nějakou obdržíte, zobrazí se zde.",
   "empty_column.hashtag": "Pod tímto hashtagem ještě nic není.",
-  "empty_column.home": "Vaše domovská časová osa je prázdná! Začněte navštívením {public} nebo použijte hledání a seznamte se s dalšími uživateli.",
+  "empty_column.home": "Vaše domovská časová osa je prázdná! Začněte návštěvou {public} nebo použijte hledání a seznamte se s dalšími uživateli.",
   "empty_column.home.public_timeline": "veřejné časové osy",
-  "empty_column.list": "V tomto seznamu ještě nic není. Pokud budou členové tohoto seznamu psát nové tooty, objeví se zde.",
+  "empty_column.list": "V tomto seznamu ještě nic není. Pokud nějaký člen z tohoto seznamu napíše nový toot, objeví se zde.",
   "empty_column.lists": "Ještě nemáte žádný seznam. Pokud nějaký vytvoříte, zobrazí se zde.",
-  "empty_column.mutes": "Ještě jste neskryl/a žádné uživatele.",
-  "empty_column.notifications": "Ještě nemáte žádná oznámení. Začněte konverzaci komunikováním s ostatními.",
+  "empty_column.mutes": "Ještě jste neskryli žádného uživatele.",
+  "empty_column.notifications": "Ještě nemáte žádná oznámení. Začněte s někým konverzaci.",
   "empty_column.public": "Tady nic není! Napište něco veřejně, nebo začněte ručně sledovat uživatele z jiných serverů, aby tu něco přibylo",
   "error.unexpected_crash.explanation": "Kvůli chybě v našem kódu nebo problému s kompatibilitou prohlížeče nemohla být tato stránka načtena správně.",
-  "error.unexpected_crash.next_steps": "Zkuste obnovit stránku. Pokud to nepomůže, budete možná moci dále používat Mastodon pomocí jiného prohlížeče nebo nativní aplikace.",
+  "error.unexpected_crash.next_steps": "Zkuste stránku načíst znovu. Pokud to nepomůže, zkuste Mastodon používat pomocí jiného prohlížeče nebo nativní aplikace.",
   "errors.unexpected_crash.copy_stacktrace": "Zkopírovat stacktrace do schránky",
   "errors.unexpected_crash.report_issue": "Nahlásit problém",
   "follow_request.authorize": "Autorizovat",
@@ -163,13 +169,13 @@
   "getting_started.documentation": "Dokumentace",
   "getting_started.heading": "Začínáme",
   "getting_started.invite": "Pozvat lidi",
-  "getting_started.open_source_notice": "Mastodon je otevřený software. Na GitHubu k němu můžete přispět nebo nahlásit chyby: {github}.",
+  "getting_started.open_source_notice": "Mastodon je otevřený software. Přispět do jeho vývoje nebo hlásit chyby můžete na GitHubu {github}.",
   "getting_started.security": "Nastavení účtu",
   "getting_started.terms": "Podmínky používání",
   "hashtag.column_header.tag_mode.all": "a {additional}",
   "hashtag.column_header.tag_mode.any": "nebo {additional}",
   "hashtag.column_header.tag_mode.none": "bez {additional}",
-  "hashtag.column_settings.select.no_options_message": "Žádné návrhy nenalezeny",
+  "hashtag.column_settings.select.no_options_message": "Nenalezeny žádné návrhy",
   "hashtag.column_settings.select.placeholder": "Zadejte hashtagy…",
   "hashtag.column_settings.tag_mode.all": "Všechny z těchto",
   "hashtag.column_settings.tag_mode.any": "Jakékoliv z těchto",
@@ -178,14 +184,14 @@
   "home.column_settings.basic": "Základní",
   "home.column_settings.show_reblogs": "Zobrazit boosty",
   "home.column_settings.show_replies": "Zobrazit odpovědi",
-  "intervals.full.days": "{number, plural, one {# den} few {# dny} many {# dne} other {# dní}}",
-  "intervals.full.hours": "{number, plural, one {# hodina} few {# hodiny} many {# hodiny} other {# hodin}}",
-  "intervals.full.minutes": "{number, plural, one {# minuta} few {# minuty} many {# minuty} other {# minut}}",
+  "intervals.full.days": "{number, plural, one {# den} few {# dny} many {# dní} other {# dní}}",
+  "intervals.full.hours": "{number, plural, one {# hodina} few {# hodiny} many {# hodin} other {# hodin}}",
+  "intervals.full.minutes": "{number, plural, one {# minuta} few {# minuty} many {# minut} other {# minut}}",
   "introduction.federation.action": "Další",
   "introduction.federation.federated.headline": "Federovaná",
   "introduction.federation.federated.text": "Veřejné příspěvky z jiných serverů ve fedivesmíru se zobrazí na federované časové ose.",
   "introduction.federation.home.headline": "Domů",
-  "introduction.federation.home.text": "Příspěvky od lidí, které sledujete, se objeví ve vašem domovském proudu. Můžete sledovat kohokoliv na jakémkoliv serveru!",
+  "introduction.federation.home.text": "Příspěvky od lidí, které sledujete, se objeví ve vašem domovském kanálu. Můžete sledovat kohokoliv na jakémkoliv serveru!",
   "introduction.federation.local.headline": "Místní",
   "introduction.federation.local.text": "Veřejné příspěvky od lidí ze stejného serveru jako vy se zobrazí na místní časové ose.",
   "introduction.interactions.action": "Dokončit tutoriál!",
@@ -197,39 +203,40 @@
   "introduction.interactions.reply.text": "Můžete odpovídat na tooty jiných lidí i vaše vlastní, což je propojí do konverzace.",
   "introduction.welcome.action": "Jdeme na to!",
   "introduction.welcome.headline": "První kroky",
-  "introduction.welcome.text": "Vítejte ve fedivesmíru! Za malou chvíli budete moci posílat zprávy a povídat si se svými přátely přes širokou škálu serverů. Tento server, {domain}, je však speciální—je na něm váš profil, proto si zapamatujte jeho jméno.",
-  "keyboard_shortcuts.back": "k návratu zpět",
-  "keyboard_shortcuts.blocked": "k otevření seznamu blokovaných uživatelů",
-  "keyboard_shortcuts.boost": "k boostnutí",
-  "keyboard_shortcuts.column": "k zaměření na toot v jednom ze sloupců",
-  "keyboard_shortcuts.compose": "k zaměření na psací prostor",
+  "introduction.welcome.text": "Vítejte ve fedivesmíru! Za malou chvíli budete moci posílat zprávy a povídat si se svými přáteli z mnoha serverů. Tento server {domain}, je však speciální — je na něm váš profil a proto si zapamatujte jeho jméno.",
+  "keyboard_shortcuts.back": "návrat zpět",
+  "keyboard_shortcuts.blocked": "otevřít seznam blokovaných uživatelů",
+  "keyboard_shortcuts.boost": "boost",
+  "keyboard_shortcuts.column": "zaměření na toot v jednom ze sloupců",
+  "keyboard_shortcuts.compose": "zaměření na oblast pro psaní",
   "keyboard_shortcuts.description": "Popis",
-  "keyboard_shortcuts.direct": "k otevření sloupce s přímými zprávami",
-  "keyboard_shortcuts.down": "k posunutí dolů v seznamu",
-  "keyboard_shortcuts.enter": "k otevření tootu",
-  "keyboard_shortcuts.favourite": "k oblíbení",
-  "keyboard_shortcuts.favourites": "k otevření seznamu oblíbených",
-  "keyboard_shortcuts.federated": "k otevření federované časové osy",
+  "keyboard_shortcuts.direct": "otevření sloupce s přímými zprávami",
+  "keyboard_shortcuts.down": "posunutí dolů v seznamu",
+  "keyboard_shortcuts.enter": "otevření tootu",
+  "keyboard_shortcuts.favourite": "oblíbení",
+  "keyboard_shortcuts.favourites": "otevření seznamu oblíbených",
+  "keyboard_shortcuts.federated": "otevření federované časové osy",
   "keyboard_shortcuts.heading": "Klávesové zkratky",
-  "keyboard_shortcuts.home": "k otevření domovské časové osy",
+  "keyboard_shortcuts.home": "otevření domovské časové osy",
   "keyboard_shortcuts.hotkey": "Klávesová zkratka",
-  "keyboard_shortcuts.legend": "k zobrazení této legendy",
-  "keyboard_shortcuts.local": "k otevření místní časové osy",
-  "keyboard_shortcuts.mention": "ke zmínění autora",
-  "keyboard_shortcuts.muted": "k otevření seznamu skrytých uživatelů",
-  "keyboard_shortcuts.my_profile": "k otevření vašeho profilu",
-  "keyboard_shortcuts.notifications": "k otevření sloupce s oznámeními",
-  "keyboard_shortcuts.pinned": "k otevření seznamu připnutých tootů",
-  "keyboard_shortcuts.profile": "k otevření autorova profilu",
-  "keyboard_shortcuts.reply": "k odpovězení",
-  "keyboard_shortcuts.requests": "k otevření seznamu požadavků o sledování",
-  "keyboard_shortcuts.search": "k zaměření na hledání",
-  "keyboard_shortcuts.start": "k otevření sloupce „začínáme“",
-  "keyboard_shortcuts.toggle_hidden": "k zobrazení/skrytí textu za varováním o obsahu",
-  "keyboard_shortcuts.toggle_sensitivity": "k zobrazení/skrytí médií",
-  "keyboard_shortcuts.toot": "k napsání úplně nového tootu",
-  "keyboard_shortcuts.unfocus": "ke zrušení zaměření na psací prostor/hledání",
-  "keyboard_shortcuts.up": "k posunutí nahoru v seznamu",
+  "keyboard_shortcuts.legend": "zobrazení této legendy",
+  "keyboard_shortcuts.local": "otevření místní časové osy",
+  "keyboard_shortcuts.mention": "zmínění autora",
+  "keyboard_shortcuts.muted": "otevření seznamu skrytých uživatelů",
+  "keyboard_shortcuts.my_profile": "otevření vašeho profilu",
+  "keyboard_shortcuts.notifications": "otevření sloupce s oznámeními",
+  "keyboard_shortcuts.open_media": "otevření médií",
+  "keyboard_shortcuts.pinned": "otevření seznamu připnutých tootů",
+  "keyboard_shortcuts.profile": "otevření autorova profilu",
+  "keyboard_shortcuts.reply": "odpovědět",
+  "keyboard_shortcuts.requests": "otevření seznamu požadavků o sledování",
+  "keyboard_shortcuts.search": "zaměření na hledání",
+  "keyboard_shortcuts.start": "otevření sloupce „začínáme“",
+  "keyboard_shortcuts.toggle_hidden": "zobrazení/skrytí textu za varováním o obsahu",
+  "keyboard_shortcuts.toggle_sensitivity": "zobrazení/skrytí médií",
+  "keyboard_shortcuts.toot": "napsání úplně nového tootu",
+  "keyboard_shortcuts.unfocus": "zrušení zaměření na psací prostor/hledání",
+  "keyboard_shortcuts.up": "posunutí nahoru v seznamu",
   "lightbox.close": "Zavřít",
   "lightbox.next": "Další",
   "lightbox.previous": "Předchozí",
@@ -244,13 +251,14 @@
   "lists.search": "Hledejte mezi lidmi, které sledujete",
   "lists.subheading": "Vaše seznamy",
   "load_pending": "{count, plural, one {# nová položka} few {# nové položky} many {# nových položek} other {# nových položek}}",
-  "loading_indicator.label": "Načítám…",
-  "media_gallery.toggle_visible": "Přepínat viditelnost",
+  "loading_indicator.label": "Načítání…",
+  "media_gallery.toggle_visible": "Přepnout viditelnost",
   "missing_indicator.label": "Nenalezeno",
   "missing_indicator.sublabel": "Tento zdroj se nepodařilo najít",
   "mute_modal.hide_notifications": "Skrýt oznámení od tohoto uživatele?",
   "navigation_bar.apps": "Mobilní aplikace",
   "navigation_bar.blocks": "Blokovaní uživatelé",
+  "navigation_bar.bookmarks": "Záložky",
   "navigation_bar.community_timeline": "Místní časová osa",
   "navigation_bar.compose": "Vytvořit nový toot",
   "navigation_bar.direct": "Přímé zprávy",
@@ -259,7 +267,7 @@
   "navigation_bar.edit_profile": "Upravit profil",
   "navigation_bar.favourites": "Oblíbené",
   "navigation_bar.filters": "Skrytá slova",
-  "navigation_bar.follow_requests": "Požadavky o sledování",
+  "navigation_bar.follow_requests": "Žádosti o sledování",
   "navigation_bar.follows_and_followers": "Sledovaní a sledující",
   "navigation_bar.info": "O tomto serveru",
   "navigation_bar.keyboard_shortcuts": "Klávesové zkratky",
@@ -271,19 +279,22 @@
   "navigation_bar.preferences": "Předvolby",
   "navigation_bar.public_timeline": "Federovaná časová osa",
   "navigation_bar.security": "Zabezpečení",
-  "notification.favourite": "{name} si oblíbil/a váš toot",
-  "notification.follow": "{name} vás začal/a sledovat",
-  "notification.mention": "{name} vás zmínil/a",
-  "notification.poll": "Anketa, ve které jste hlasoval/a, skončila",
-  "notification.reblog": "{name} boostnul/a váš toot",
-  "notifications.clear": "Vymazat oznámení",
-  "notifications.clear_confirmation": "Jste si jistý/á, že chcete trvale vymazat všechna vaše oznámení?",
-  "notifications.column_settings.alert": "Desktopová oznámení",
+  "notification.favourite": "Uživatel {name} si oblíbil váš toot",
+  "notification.follow": "Uživatel {name} vás začal sledovat",
+  "notification.follow_request": "Uživatel {name} požádal o povolení vás sledovat",
+  "notification.mention": "Uživatel {name} vás zmínil",
+  "notification.own_poll": "Vaše anketa skončila",
+  "notification.poll": "Anketa, ve které jste hlasovali, skončila",
+  "notification.reblog": "Uživatel {name} boostnul váš toot",
+  "notifications.clear": "Smazat oznámení",
+  "notifications.clear_confirmation": "Opravdu chcete trvale smazat všechna vaše oznámení?",
+  "notifications.column_settings.alert": "Oznámení na počítači",
   "notifications.column_settings.favourite": "Oblíbení:",
   "notifications.column_settings.filter_bar.advanced": "Zobrazit všechny kategorie",
   "notifications.column_settings.filter_bar.category": "Panel rychlého filtrování",
   "notifications.column_settings.filter_bar.show": "Zobrazit",
   "notifications.column_settings.follow": "Noví sledující:",
+  "notifications.column_settings.follow_request": "Nové žádosti o sledování:",
   "notifications.column_settings.mention": "Zmínky:",
   "notifications.column_settings.poll": "Výsledky anket:",
   "notifications.column_settings.push": "Push oznámení",
@@ -297,12 +308,12 @@
   "notifications.filter.mentions": "Zmínky",
   "notifications.filter.polls": "Výsledky anket",
   "notifications.group": "{count} oznámení",
-  "poll.closed": "Uzavřena",
+  "poll.closed": "Uzavřeno",
   "poll.refresh": "Obnovit",
   "poll.total_people": "{count, plural, one {# člověk} few {# lidé} many {# lidí} other {# lidí}}",
-  "poll.total_votes": "{count, plural, one {# hlas} few {# hlasy} many {# hlasu} other {# hlasů}}",
+  "poll.total_votes": "{count, plural, one {# hlas} few {# hlasy} many {# hlasů} other {# hlasů}}",
   "poll.vote": "Hlasovat",
-  "poll.voted": "Pro tuto odpověď jste hlasoval/a",
+  "poll.voted": "Pro tuto odpověď jste hlasovali",
   "poll_button.add_poll": "Přidat anketu",
   "poll_button.remove_poll": "Odstranit anketu",
   "privacy.change": "Změnit soukromí tootu",
@@ -312,11 +323,11 @@
   "privacy.private.short": "Pouze pro sledující",
   "privacy.public.long": "Odeslat na veřejné časové osy",
   "privacy.public.short": "Veřejný",
-  "privacy.unlisted.long": "Neodeslat na veřejné časové osy",
+  "privacy.unlisted.long": "Neodesílat na veřejné časové osy",
   "privacy.unlisted.short": "Neuvedený",
   "refresh": "Obnovit",
-  "regeneration_indicator.label": "Načítám…",
-  "regeneration_indicator.sublabel": "Váš domovský proud se připravuje!",
+  "regeneration_indicator.label": "Načítání…",
+  "regeneration_indicator.sublabel": "Váš domovský kanál se připravuje!",
   "relative_time.days": "{number} d",
   "relative_time.hours": "{number} h",
   "relative_time.just_now": "teď",
@@ -325,13 +336,13 @@
   "reply_indicator.cancel": "Zrušit",
   "report.forward": "Přeposlat na {target}",
   "report.forward_hint": "Tento účet je z jiného serveru. Chcete na něj také poslat anonymizovanou kopii?",
-  "report.hint": "Nahlášení bude zasláno moderátorům vašeho serveru. Níže můžete uvést, proč tento účet nahlašujete:",
+  "report.hint": "Hlášení bude zasláno moderátorům vašeho serveru. Níže můžete uvést, proč tento účet nahlašujete:",
   "report.placeholder": "Dodatečné komentáře",
   "report.submit": "Odeslat",
   "report.target": "Nahlášení uživatele {target}",
   "search.placeholder": "Hledat",
   "search_popout.search_format": "Pokročilé hledání",
-  "search_popout.tips.full_text": "Jednoduchý text navrátí tooty, které jste napsal/a, oblíbil/a si, boostnul/a, nebo v nich byl/a zmíněn/a, a také odpovídající přezdívky, zobrazovaná jména a hashtagy.",
+  "search_popout.tips.full_text": "Jednoduchý text navrátí tooty, 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": "toot",
   "search_popout.tips.text": "Jednoduchý text navrátí odpovídající zobrazovaná jména, přezdívky a hashtagy",
@@ -340,16 +351,17 @@
   "search_results.hashtags": "Hashtagy",
   "search_results.statuses": "Tooty",
   "search_results.statuses_fts_disabled": "Vyhledávání tootů podle jejich obsahu není na tomto serveru Mastodon povoleno.",
-  "search_results.total": "{count, number} {count, plural, one {výsledek} few {výsledky} many {výsledku} other {výsledků}}",
+  "search_results.total": "{count, number} {count, plural, one {výsledek} few {výsledky} many {výsledků} other {výsledků}}",
   "status.admin_account": "Otevřít moderátorské rozhraní pro uživatele @{name}",
   "status.admin_status": "Otevřít tento toot v moderátorském rozhraní",
   "status.block": "Zablokovat uživatele @{name}",
+  "status.bookmark": "Přidat do záložek",
   "status.cancel_reblog_private": "Zrušit boost",
   "status.cannot_reblog": "Tento příspěvek nemůže být boostnutý",
   "status.copy": "Kopírovat odkaz k tootu",
   "status.delete": "Smazat",
-  "status.detailed_status": "Detailní zobrazení konverzace",
-  "status.direct": "Poslat přímou zprávu uživateli @{name}",
+  "status.detailed_status": "Podrobné zobrazení konverzace",
+  "status.direct": "Poslat uživateli @{name} přímou zprávu",
   "status.embed": "Vložit na web",
   "status.favourite": "Oblíbit",
   "status.filtered": "Filtrováno",
@@ -365,9 +377,10 @@
   "status.read_more": "Číst více",
   "status.reblog": "Boostnout",
   "status.reblog_private": "Boostnout původnímu publiku",
-  "status.reblogged_by": "{name} boostnul/a",
+  "status.reblogged_by": "Uživatel {name} boostnul",
   "status.reblogs.empty": "Tento toot ještě nikdo neboostnul. Pokud to někdo udělá, zobrazí se zde.",
   "status.redraft": "Smazat a přepsat",
+  "status.remove_bookmark": "Odstranit ze záložek",
   "status.reply": "Odpovědět",
   "status.replyAll": "Odpovědět na vlákno",
   "status.report": "Nahlásit uživatele @{name}",
@@ -382,37 +395,40 @@
   "status.unmute_conversation": "Odkrýt konverzaci",
   "status.unpin": "Odepnout z profilu",
   "suggestions.dismiss": "Odmítnout návrh",
-  "suggestions.header": "Mohli by vás zajímat…",
+  "suggestions.header": "Mohlo by vás zajímat…",
   "tabs_bar.federated_timeline": "Federovaná",
   "tabs_bar.home": "Domů",
   "tabs_bar.local_timeline": "Místní",
   "tabs_bar.notifications": "Oznámení",
   "tabs_bar.search": "Hledat",
-  "time_remaining.days": "{number, plural, one {Zbývá # den} few {Zbývají # dny} many {Zbývá # dne} other {Zbývá # dní}}",
-  "time_remaining.hours": "{number, plural, one {Zbývá # hodina} few {Zbývají # hodiny} many {Zbývá # hodiny} other {Zbývá # hodin}}",
-  "time_remaining.minutes": "{number, plural, one {Zbývá # minuta} few {Zbývají # minuty} many {Zbývá # minuty} other {Zbývá # minut}}",
+  "time_remaining.days": "{number, plural, one {Zbývá # den} few {Zbývají # dny} many {Zbývá # dní} other {Zbývá # dní}}",
+  "time_remaining.hours": "{number, plural, one {Zbývá # hodina} few {Zbývají # hodiny} many {Zbývá # hodin} other {Zbývá # hodin}}",
+  "time_remaining.minutes": "{number, plural, one {Zbývá # minuta} few {Zbývají # minuty} many {Zbývá # minut} other {Zbývá # minut}}",
   "time_remaining.moments": "Zbývá několik sekund",
-  "time_remaining.seconds": "{number, plural, one {Zbývá # sekunda} few {Zbývají # sekundy} many {Zbývá # sekundy} other {Zbývá # sekund}}",
+  "time_remaining.seconds": "{number, plural, one {Zbývá # sekunda} few {Zbývají # sekundy} many {Zbývá # sekund} other {Zbývá # sekund}}",
   "trends.count_by_accounts": "{count} {rawCount, plural, one {člověk} few {lidé} many {lidí} other {lidí}} hovoří",
   "trends.trending_now": "Aktuální trendy",
-  "ui.beforeunload": "Váš koncept se ztratí, pokud Mastodon opustíte.",
-  "upload_area.title": "Přetažením nahrajete",
-  "upload_button.label": "Přidat média (JPEG, PNG, GIF, WebM, MP4, MOV)",
+  "ui.beforeunload": "Pokud Mastodon opustíte, váš koncept se ztratí.",
+  "upload_area.title": "Nahrajte přetažením",
+  "upload_button.label": "Přidat média ({formats})",
   "upload_error.limit": "Byl překročen limit nahraných souborů.",
-  "upload_error.poll": "Nahrávání souborů není povoleno u anket.",
+  "upload_error.poll": "U anket není nahrávání souborů povoleno.",
+  "upload_form.audio_description": "Popis pro sluchově postižené",
   "upload_form.description": "Popis pro zrakově postižené",
   "upload_form.edit": "Upravit",
   "upload_form.undo": "Smazat",
+  "upload_form.video_description": "Popis pro sluchově či zrakově postižené",
   "upload_modal.analyzing_picture": "Analyzuji obrázek…",
   "upload_modal.apply": "Použít",
   "upload_modal.description_placeholder": "Příliš žluťoučký kůň úpěl ďábelské ódy",
   "upload_modal.detect_text": "Detekovat text z obrázku",
   "upload_modal.edit_media": "Upravit média",
-  "upload_modal.hint": "Kliknutím na nebo přetáhnutím kruhu na náhledu vyberte bod soustředění, který bude vždy zobrazen na všech náhledech.",
+  "upload_modal.hint": "Kliknutím na nebo přetáhnutím kruhu na náhledu vyberte oblast, která bude na všech náhledech vždy zobrazen.",
   "upload_modal.preview_label": "Náhled ({ratio})",
-  "upload_progress.label": "Nahrávám…",
+  "upload_progress.label": "Nahrávání…",
   "video.close": "Zavřít video",
-  "video.exit_fullscreen": "Ukončit celou obrazovku",
+  "video.download": "Stáhnout soubor",
+  "video.exit_fullscreen": "Ukončit režim celé obrazovky",
   "video.expand": "Otevřít video",
   "video.fullscreen": "Celá obrazovka",
   "video.hide": "Skrýt video",
diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json
index a8a952798..ee58c7168 100644
--- a/app/javascript/mastodon/locales/cy.json
+++ b/app/javascript/mastodon/locales/cy.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Ychwanegu neu Dileu o'r rhestrau",
   "account.badges.bot": "Bot",
+  "account.badges.group": "Grŵp",
   "account.block": "Blocio @{name}",
   "account.block_domain": "Cuddio popeth rhag {domain}",
   "account.blocked": "Blociwyd",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Cyfradd gyfyngedig",
   "alert.unexpected.message": "Digwyddodd gwall annisgwyl.",
   "alert.unexpected.title": "Wps!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} yr wythnos",
   "boost_modal.combo": "Mae modd gwasgu {combo} er mwyn sgipio hyn tro nesa",
   "bundle_column_error.body": "Aeth rhywbeth o'i le tra'n llwytho'r elfen hon.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Aeth rhywbeth o'i le tra'n llwytho'r elfen hon.",
   "bundle_modal_error.retry": "Ceiswich eto",
   "column.blocks": "Defnyddwyr a flociwyd",
+  "column.bookmarks": "Tudalnodau",
   "column.community": "Ffrwd lleol",
   "column.direct": "Negeseuon preifat",
   "column.directory": "Pori proffiliau",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Cyfnod pleidlais",
   "compose_form.poll.option_placeholder": "Dewisiad {number}",
   "compose_form.poll.remove_option": "Tynnu'r dewisiad",
+  "compose_form.poll.switch_to_multiple": "Newid pleidlais i adael mwy nag un dewis",
+  "compose_form.poll.switch_to_single": "Newid pleidlais i gyfyngu i un dewis",
   "compose_form.publish": "Tŵt",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Marcio cyfryngau fel eu bod yn sensitif",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Dim tŵtiau fama!",
   "empty_column.account_unavailable": "Proffil ddim ar gael",
   "empty_column.blocks": "Nid ydych wedi blocio unrhyw ddefnyddwyr eto.",
+  "empty_column.bookmarked_statuses": "Nid oes gennych unrhyw dwtiau tudalnodiedig eto. Pan y byddwch yn tudalnodi un, mi fydd yn ymddangos yma.",
   "empty_column.community": "Mae'r ffrwd lleol yn wag. Ysgrifenwch rhywbeth yn gyhoeddus i gael dechrau arni!",
   "empty_column.direct": "Nid oes gennych unrhyw negeseuon preifat eto. Pan y byddwch yn anfon neu derbyn un, mi fydd yn ymddangos yma.",
   "empty_column.domain_blocks": "Nid oes yna unrhyw barthau cuddiedig eto.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "i agor rhestr defnyddwyr a dawelwyd",
   "keyboard_shortcuts.my_profile": "i agor eich proffil",
   "keyboard_shortcuts.notifications": "i agor colofn hysbysiadau",
+  "keyboard_shortcuts.open_media": "i agor cyfryngau",
   "keyboard_shortcuts.pinned": "i agor rhestr tŵtiau wedi'i pinio",
   "keyboard_shortcuts.profile": "i agor proffil yr awdur",
   "keyboard_shortcuts.reply": "i ateb",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Cuddio hysbysiadau rhag y defnyddiwr hwn?",
   "navigation_bar.apps": "Apiau symudol",
   "navigation_bar.blocks": "Defnyddwyr wedi eu blocio",
+  "navigation_bar.bookmarks": "Tudalnodau",
   "navigation_bar.community_timeline": "Ffrwd leol",
   "navigation_bar.compose": "Cyfansoddi tŵt newydd",
   "navigation_bar.direct": "Negeseuon preifat",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Diogelwch",
   "notification.favourite": "hoffodd {name} eich tŵt",
   "notification.follow": "dilynodd {name} chi",
+  "notification.follow_request": "Mae {name} wedi gwneud cais i'ch dilyn",
   "notification.mention": "Soniodd {name} amdanoch chi",
+  "notification.own_poll": "Mae eich pôl wedi diweddu",
   "notification.poll": "Mae pleidlais rydych wedi pleidleisio ynddi wedi dod i ben",
   "notification.reblog": "Hysbysebodd {name} eich tŵt",
   "notifications.clear": "Clirio hysbysiadau",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Bar hidlo",
   "notifications.column_settings.filter_bar.show": "Dangos",
   "notifications.column_settings.follow": "Dilynwyr newydd:",
+  "notifications.column_settings.follow_request": "Ceisiadau dilyn newydd:",
   "notifications.column_settings.mention": "Crybwylliadau:",
   "notifications.column_settings.poll": "Canlyniadau pleidlais:",
   "notifications.column_settings.push": "Hysbysiadau gwthiadwy",
@@ -344,6 +355,7 @@
   "status.admin_account": "Agor rhyngwyneb goruwchwylio ar gyfer @{name}",
   "status.admin_status": "Agor y tŵt yn y rhyngwyneb goruwchwylio",
   "status.block": "Blocio @{name}",
+  "status.bookmark": "Tudalnodi",
   "status.cancel_reblog_private": "Dadfŵstio",
   "status.cannot_reblog": "Ni ellir sbarduno'r tŵt hwn",
   "status.copy": "Copïo cysylltiad i'r tŵt",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "Bŵstio {name}",
   "status.reblogs.empty": "Does neb wedi bŵstio'r tŵt yma eto. Pan y bydd rhywun yn gwneud, byddent yn ymddangos yma.",
   "status.redraft": "Dileu & ailddrafftio",
+  "status.remove_bookmark": "Tynnu'r tudalnod",
   "status.reply": "Ateb",
   "status.replyAll": "Ateb i edefyn",
   "status.report": "Adrodd @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Ychwanegwch gyfryngau (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_error.limit": "Wedi mynd heibio'r uchafswm terfyn uwchlwytho.",
   "upload_error.poll": "Nid oes modd uwchlwytho ffeiliau â phleidleisiau.",
+  "upload_form.audio_description": "Disgrifio ar gyfer pobl sydd â cholled clyw",
   "upload_form.description": "Disgrifio i'r rheini a nam ar ei golwg",
   "upload_form.edit": "Golygu",
   "upload_form.undo": "Dileu",
+  "upload_form.video_description": "Disgrifio ar gyfer pobl sydd â cholled clyw neu amhariad golwg",
   "upload_modal.analyzing_picture": "Dadansoddi llun…",
   "upload_modal.apply": "Gweithredu",
   "upload_modal.description_placeholder": "Mae ei phen bach llawn jocs, 'run peth a fy nghot golff, rhai dyddiau",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Rhagolwg ({ratio})",
   "upload_progress.label": "Uwchlwytho...",
   "video.close": "Cau fideo",
+  "video.download": "Lawrlwytho ffeil",
   "video.exit_fullscreen": "Gadael sgrîn llawn",
   "video.expand": "Ymestyn fideo",
   "video.fullscreen": "Sgrîn llawn",
diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json
index 4719d5ca5..a699aaf54 100644
--- a/app/javascript/mastodon/locales/da.json
+++ b/app/javascript/mastodon/locales/da.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Tilføj eller fjern fra lister",
   "account.badges.bot": "Robot",
+  "account.badges.group": "Group",
   "account.block": "Bloker @{name}",
   "account.block_domain": "Skjul alt fra {domain}",
   "account.blocked": "Blokeret",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Gradsbegrænset",
   "alert.unexpected.message": "Der opstod en uventet fejl.",
   "alert.unexpected.title": "Ups!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} per uge",
   "boost_modal.combo": "Du kan trykke {combo} for at springe dette over næste gang",
   "bundle_column_error.body": "Noget gik galt under indlæsningen af dette komponent.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Noget gik galt under indlæsningen af dette komponent.",
   "bundle_modal_error.retry": "Prøv igen",
   "column.blocks": "Blokerede brugere",
+  "column.bookmarks": "Bogmærker",
   "column.community": "Lokal tidslinje",
   "column.direct": "Direkte beskeder",
   "column.directory": "Gennemse profiler",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Afstemningens varighed",
   "compose_form.poll.option_placeholder": "Valgmulighed {number}",
   "compose_form.poll.remove_option": "Fjern denne valgmulighed",
+  "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": "Trut",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Markér medie som følsomt",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Ingen bidrag her!",
   "empty_column.account_unavailable": "Profil utilgængelig",
   "empty_column.blocks": "Du har ikke blokeret nogen endnu.",
+  "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "Den lokale tidslinje er tom. Skriv noget offentligt for at starte lavinen!",
   "empty_column.direct": "Du har endnu ingen direkte beskeder. Når du sender eller modtager en, vil den vises her.",
   "empty_column.domain_blocks": "Der er endnu ikke nogle skjulte domæner.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "for at åbne listen over dæmpede brugere",
   "keyboard_shortcuts.my_profile": "for at åbne din profil",
   "keyboard_shortcuts.notifications": "for at åbne notifikations kolonnen",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "for at åbne listen over fastgjorte trut",
   "keyboard_shortcuts.profile": "til profil af åben forfatter",
   "keyboard_shortcuts.reply": "for at svare",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Skjul notifikationer fra denne bruger?",
   "navigation_bar.apps": "Mobil apps",
   "navigation_bar.blocks": "Blokerede brugere",
+  "navigation_bar.bookmarks": "Bogmærker",
   "navigation_bar.community_timeline": "Lokal tidslinje",
   "navigation_bar.compose": "Skriv nyt trut",
   "navigation_bar.direct": "Direkte beskeder",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Sikkerhed",
   "notification.favourite": "{name} favoriserede din status",
   "notification.follow": "{name} fulgte dig",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} nævnte dig",
+  "notification.own_poll": "Din afstemning er afsluttet",
   "notification.poll": "En afstemning, du stemte i, er slut",
   "notification.reblog": "{name} boostede din status",
   "notifications.clear": "Ryd notifikationer",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Hurtigfilter",
   "notifications.column_settings.filter_bar.show": "Vis",
   "notifications.column_settings.follow": "Nye følgere:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "Statusser der nævner dig:",
   "notifications.column_settings.poll": "Afstemningsresultat:",
   "notifications.column_settings.push": "Pushnotifikationer",
@@ -344,6 +355,7 @@
   "status.admin_account": "Åben modereringsvisning for @{name}",
   "status.admin_status": "Åben denne status i modereringsvisningen",
   "status.block": "Bloker @{name}",
+  "status.bookmark": "Bogmærke",
   "status.cancel_reblog_private": "Fjern boost",
   "status.cannot_reblog": "Denne post kan ikke boostes",
   "status.copy": "Kopiér link til status",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} boostede",
   "status.reblogs.empty": "Der er endnu ingen der har boostet dette trut. Når der er nogen der gør, vil det blive vist her.",
   "status.redraft": "Slet og omskriv",
+  "status.remove_bookmark": "Fjern bogmærke",
   "status.reply": "Besvar",
   "status.replyAll": "Besvar samtale",
   "status.report": "Anmeld @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Tilføj medie (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_error.limit": "Uploadgrænse overskredet.",
   "upload_error.poll": "Filupload ikke tilladt sammen med afstemninger.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "Beskriv for svagtseende",
   "upload_form.edit": "Redigér",
   "upload_form.undo": "Slet",
+  "upload_form.video_description": "Describe for people with hearing loss or visual impairment",
   "upload_modal.analyzing_picture": "Analyserer billede…",
   "upload_modal.apply": "Anvend",
   "upload_modal.description_placeholder": "En hurtig brun ræv hopper over den dovne hund",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Forhåndsvisning ({ratio})",
   "upload_progress.label": "Uploader...",
   "video.close": "Luk video",
+  "video.download": "Hent fil",
   "video.exit_fullscreen": "Forlad fuldskærm",
   "video.expand": "Udvid video",
   "video.fullscreen": "Fuldskærm",
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index c916028be..aae5ad1c1 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Hinzufügen oder Entfernen von Listen",
   "account.badges.bot": "Bot",
+  "account.badges.group": "Gruppe",
   "account.block": "@{name} blockieren",
   "account.block_domain": "Alles von {domain} verstecken",
   "account.blocked": "Blockiert",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Anfragelimit überschritten",
   "alert.unexpected.message": "Ein unerwarteter Fehler ist aufgetreten.",
   "alert.unexpected.title": "Hoppla!",
+  "announcement.announcement": "Ankündigung",
   "autosuggest_hashtag.per_week": "{count} pro Woche",
   "boost_modal.combo": "Drücke {combo}, um dieses Fenster zu überspringen",
   "bundle_column_error.body": "Etwas ist beim Laden schiefgelaufen.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Etwas ist beim Laden schiefgelaufen.",
   "bundle_modal_error.retry": "Erneut versuchen",
   "column.blocks": "Blockierte Profile",
+  "column.bookmarks": "Lesezeichen",
   "column.community": "Lokale Zeitleiste",
   "column.direct": "Direktnachrichten",
   "column.directory": "Profile durchsuchen",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Umfragedauer",
   "compose_form.poll.option_placeholder": "Wahl {number}",
   "compose_form.poll.remove_option": "Wahl entfernen",
+  "compose_form.poll.switch_to_multiple": "Umfrage ändern, um mehrere Optionen zu erlauben",
+  "compose_form.poll.switch_to_single": "Umfrage ändern, um eine einzige Wahl zu ermöglichen",
   "compose_form.publish": "Tröt",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Medien als heikel markieren",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Keine Beiträge!",
   "empty_column.account_unavailable": "Konto nicht verfügbar",
   "empty_column.blocks": "Du hast keine Profile blockiert.",
+  "empty_column.bookmarked_statuses": "Du hast bis jetzt keine Beiträge als Lesezeichen gespeichert. Wenn du einen Beitrag als Lesezeichen speicherst wird er hier erscheinen.",
   "empty_column.community": "Die lokale Zeitleiste ist leer. Schreibe einen öffentlichen Beitrag, um den Ball ins Rollen zu bringen!",
   "empty_column.direct": "Du hast noch keine Direktnachrichten erhalten. Wenn du eine sendest oder empfängst, wird sie hier zu sehen sein.",
   "empty_column.domain_blocks": "Es ist noch keine versteckten Domains.",
@@ -164,7 +170,7 @@
   "getting_started.heading": "Erste Schritte",
   "getting_started.invite": "Leute einladen",
   "getting_started.open_source_notice": "Mastodon ist quelloffene Software. Du kannst auf GitHub unter {github} dazu beitragen oder Probleme melden.",
-  "getting_started.security": "Sicherheit",
+  "getting_started.security": "Konto & Sicherheit",
   "getting_started.terms": "Nutzungsbedingungen",
   "hashtag.column_header.tag_mode.all": "und {additional}",
   "hashtag.column_header.tag_mode.any": "oder {additional}",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "Liste stummgeschalteter Profile öffnen",
   "keyboard_shortcuts.my_profile": "Dein Profil öffnen",
   "keyboard_shortcuts.notifications": "Benachrichtigungsspalte öffnen",
+  "keyboard_shortcuts.open_media": "um Medien zu öffnen",
   "keyboard_shortcuts.pinned": "Liste angehefteter Beiträge öffnen",
   "keyboard_shortcuts.profile": "Profil des Autors öffnen",
   "keyboard_shortcuts.reply": "antworten",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Benachrichtigungen von diesem Account verbergen?",
   "navigation_bar.apps": "Mobile Apps",
   "navigation_bar.blocks": "Blockierte Profile",
+  "navigation_bar.bookmarks": "Lesezeichen",
   "navigation_bar.community_timeline": "Lokale Zeitleiste",
   "navigation_bar.compose": "Neuen Beitrag verfassen",
   "navigation_bar.direct": "Direktnachrichten",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Sicherheit",
   "notification.favourite": "{name} hat deinen Beitrag favorisiert",
   "notification.follow": "{name} folgt dir",
+  "notification.follow_request": "{name} möchte dir folgen",
   "notification.mention": "{name} hat dich erwähnt",
+  "notification.own_poll": "Deine Umfrage ist beendet",
   "notification.poll": "Eine Umfrage in der du abgestimmt hast ist vorbei",
   "notification.reblog": "{name} hat deinen Beitrag geteilt",
   "notifications.clear": "Mitteilungen löschen",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Schnellfilterleiste",
   "notifications.column_settings.filter_bar.show": "Anzeigen",
   "notifications.column_settings.follow": "Neue Folgende:",
+  "notifications.column_settings.follow_request": "Neue Folge-Anfragen:",
   "notifications.column_settings.mention": "Erwähnungen:",
   "notifications.column_settings.poll": "Ergebnisse von Umfragen:",
   "notifications.column_settings.push": "Push-Benachrichtigungen",
@@ -344,6 +355,7 @@
   "status.admin_account": "Öffne Moderationsoberfläche für @{name}",
   "status.admin_status": "Öffne Beitrag in der Moderationsoberfläche",
   "status.block": "Blockiere @{name}",
+  "status.bookmark": "Lesezeichen",
   "status.cancel_reblog_private": "Nicht mehr teilen",
   "status.cannot_reblog": "Dieser Beitrag kann nicht geteilt werden",
   "status.copy": "Kopiere Link zum Beitrag",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} teilte",
   "status.reblogs.empty": "Diesen Beitrag hat noch niemand geteilt. Sobald es jemand tut, wird diese Person hier angezeigt.",
   "status.redraft": "Löschen und neu erstellen",
+  "status.remove_bookmark": "Lesezeichen entfernen",
   "status.reply": "Antworten",
   "status.replyAll": "Allen antworten",
   "status.report": "@{name} melden",
@@ -400,18 +413,21 @@
   "upload_button.label": "Mediendatei hinzufügen ({formats})",
   "upload_error.limit": "Dateiupload-Limit erreicht.",
   "upload_error.poll": "Dateiuploads sind in Kombination mit Umfragen nicht erlaubt.",
+  "upload_form.audio_description": "Beschreibe die Audiodatei für Menschen mit Hörschädigungen",
   "upload_form.description": "Für Menschen mit Sehbehinderung beschreiben",
   "upload_form.edit": "Bearbeiten",
   "upload_form.undo": "Löschen",
+  "upload_form.video_description": "Beschreibe das Video für Menschen mit einer Hör- oder Sehbehinderung",
   "upload_modal.analyzing_picture": "Analysiere Bild…",
   "upload_modal.apply": "Übernehmen",
-  "upload_modal.description_placeholder": "Die heiße Zypernsonne quälte Max und Victoria ja böse auf dem Weg bis zur Küste.",
+  "upload_modal.description_placeholder": "Die heiße Zypernsonne quälte Max und Victoria ja böse auf dem Weg bis zur Küste",
   "upload_modal.detect_text": "Text aus Bild erkennen",
   "upload_modal.edit_media": "Medien bearbeiten",
   "upload_modal.hint": "Klicke oder ziehe den Kreis auf die Vorschau, um den Brennpunkt auszuwählen, der immer auf allen Vorschaubilder angezeigt wird.",
   "upload_modal.preview_label": "Vorschau ({ratio})",
   "upload_progress.label": "Wird hochgeladen …",
   "video.close": "Video schließen",
+  "video.download": "Datei herunterladen",
   "video.exit_fullscreen": "Vollbild verlassen",
   "video.expand": "Video vergrößern",
   "video.fullscreen": "Vollbild",
diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json
index 16e3e402a..18692bc44 100644
--- a/app/javascript/mastodon/locales/defaultMessages.json
+++ b/app/javascript/mastodon/locales/defaultMessages.json
@@ -294,6 +294,10 @@
   {
     "descriptors": [
       {
+        "defaultMessage": "today",
+        "id": "relative_time.today"
+      },
+      {
         "defaultMessage": "now",
         "id": "relative_time.just_now"
       },
@@ -777,6 +781,10 @@
         "id": "account.badges.bot"
       },
       {
+        "defaultMessage": "Group",
+        "id": "account.badges.group"
+      },
+      {
         "defaultMessage": "Toots",
         "id": "account.posts"
       },
@@ -1036,6 +1044,14 @@
         "id": "compose_form.poll.duration"
       },
       {
+        "defaultMessage": "Change poll to allow multiple choices",
+        "id": "compose_form.poll.switch_to_multiple"
+      },
+      {
+        "defaultMessage": "Change poll to allow for a single choice",
+        "id": "compose_form.poll.switch_to_single"
+      },
+      {
         "defaultMessage": "{number, plural, one {# minute} other {# minutes}}",
         "id": "intervals.full.minutes"
       },
@@ -1396,6 +1412,10 @@
         "id": "account.unmute"
       },
       {
+        "defaultMessage": "Unfollow",
+        "id": "confirmations.unfollow.confirm"
+      },
+      {
         "defaultMessage": "Are you sure you want to unfollow {name}?",
         "id": "confirmations.unfollow.message"
       },
@@ -1541,6 +1561,27 @@
   {
     "descriptors": [
       {
+        "defaultMessage": "Close",
+        "id": "lightbox.close"
+      },
+      {
+        "defaultMessage": "Previous",
+        "id": "lightbox.previous"
+      },
+      {
+        "defaultMessage": "Next",
+        "id": "lightbox.next"
+      },
+      {
+        "defaultMessage": "Announcement",
+        "id": "announcement.announcement"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/getting_started/components/announcements.json"
+  },
+  {
+    "descriptors": [
+      {
         "defaultMessage": "Trending now",
         "id": "trends.trending_now"
       }
@@ -1706,6 +1747,14 @@
         "id": "column.home"
       },
       {
+        "defaultMessage": "Show announcements",
+        "id": "home.show_announcements"
+      },
+      {
+        "defaultMessage": "Hide announcements",
+        "id": "home.hide_announcements"
+      },
+      {
         "defaultMessage": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
         "id": "empty_column.home"
       },
@@ -2283,6 +2332,10 @@
         "id": "status.bookmark"
       },
       {
+        "defaultMessage": "More",
+        "id": "status.more"
+      },
+      {
         "defaultMessage": "Mute @{name}",
         "id": "status.mute"
       },
diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json
index ab000fa8d..abb6f0d33 100644
--- a/app/javascript/mastodon/locales/el.json
+++ b/app/javascript/mastodon/locales/el.json
@@ -1,8 +1,9 @@
 {
   "account.add_or_remove_from_list": "Προσθήκη ή Αφαίρεση από λίστες",
   "account.badges.bot": "Μποτ",
+  "account.badges.group": "Ομάδα",
   "account.block": "Αποκλεισμός @{name}",
-  "account.block_domain": "Απόκρυψε τα πάντα από το {domain}",
+  "account.block_domain": "Απόκρυψη όλων από {domain}",
   "account.blocked": "Αποκλεισμένος/η",
   "account.cancel_follow_request": "Ακύρωση αιτήματος παρακολούθησης",
   "account.direct": "Προσωπικό μήνυμα προς @{name}",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Περιορισμός συχνότητας",
   "alert.unexpected.message": "Προέκυψε απροσδόκητο σφάλμα.",
   "alert.unexpected.title": "Εεπ!",
+  "announcement.announcement": "Ανακοίνωση",
   "autosuggest_hashtag.per_week": "{count} ανα εβδομάδα",
   "boost_modal.combo": "Μπορείς να πατήσεις {combo} για να το προσπεράσεις αυτό την επόμενη φορά",
   "bundle_column_error.body": "Κάτι πήγε στραβά ενώ φορτωνόταν αυτό το στοιχείο.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Κάτι πήγε στραβά κατά τη φόρτωση του στοιχείου.",
   "bundle_modal_error.retry": "Δοκίμασε ξανά",
   "column.blocks": "Αποκλεισμένοι χρήστες",
+  "column.bookmarks": "Σελιδοδείκτες",
   "column.community": "Τοπική ροή",
   "column.direct": "Προσωπικά μηνύματα",
   "column.directory": "Δες προφίλ",
@@ -82,6 +85,8 @@
   "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_loud": "{publish}!",
   "compose_form.sensitive.hide": "Σημείωσε τα πολυμέσα ως ευαίσθητα",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Δεν έχει τουτ εδώ!",
   "empty_column.account_unavailable": "Μη διαθέσιμο προφίλ",
   "empty_column.blocks": "Δεν έχεις αποκλείσει κανέναν χρήστη ακόμα.",
+  "empty_column.bookmarked_statuses": "Δεν έχεις κανένα αποθηκευμένο τουτ ακόμα. Μόλις αποθηκεύσεις κάποιο, θα εμφανιστεί εδώ.",
   "empty_column.community": "Η τοπική ροή είναι κενή. Γράψε κάτι δημόσιο παραμύθι ν' αρχινίσει!",
   "empty_column.direct": "Δεν έχεις προσωπικά μηνύματα ακόμα. Όταν στείλεις ή λάβεις κανένα, θα εμφανιστεί εδώ.",
   "empty_column.domain_blocks": "Δεν υπάρχουν αποκλεισμένοι τομείς ακόμα.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "άνοιγμα λίστας αποσιωπημενων χρηστών",
   "keyboard_shortcuts.my_profile": "άνοιγμα του προφίλ σου",
   "keyboard_shortcuts.notifications": "άνοιγμα στήλης ειδοποιήσεων",
+  "keyboard_shortcuts.open_media": "εμφάνιση πολυμέσου",
   "keyboard_shortcuts.pinned": "άνοιγμα λίστας καρφιτσωμένων τουτ",
   "keyboard_shortcuts.profile": "άνοιγμα προφίλ συγγραφέα",
   "keyboard_shortcuts.reply": "απάντηση",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Απόκρυψη ειδοποιήσεων αυτού του χρήστη;",
   "navigation_bar.apps": "Εφαρμογές φορητών συσκευών",
   "navigation_bar.blocks": "Αποκλεισμένοι χρήστες",
+  "navigation_bar.bookmarks": "Σελιδοδείκτες",
   "navigation_bar.community_timeline": "Τοπική ροή",
   "navigation_bar.compose": "Γράψε νέο τουτ",
   "navigation_bar.direct": "Προσωπικά μηνύματα",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Ασφάλεια",
   "notification.favourite": "Ο/Η {name} σημείωσε ως αγαπημένη την κατάστασή σου",
   "notification.follow": "Ο/Η {name} σε ακολούθησε",
+  "notification.follow_request": "Ο/H {name} ζήτησε να σε παρακολουθεί",
   "notification.mention": "Ο/Η {name} σε ανέφερε",
+  "notification.own_poll": "Η ψηφοφορία σου έληξε",
   "notification.poll": "Τελείωσε μια από τις ψηφοφορίες που συμμετείχες",
   "notification.reblog": "Ο/Η {name} προώθησε την κατάστασή σου",
   "notifications.clear": "Καθαρισμός ειδοποιήσεων",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Μπάρα γρήγορου φίλτρου",
   "notifications.column_settings.filter_bar.show": "Εμφάνιση",
   "notifications.column_settings.follow": "Νέοι ακόλουθοι:",
+  "notifications.column_settings.follow_request": "Νέο αίτημα παρακολούθησης:",
   "notifications.column_settings.mention": "Αναφορές:",
   "notifications.column_settings.poll": "Αποτελέσματα ψηφοφορίας:",
   "notifications.column_settings.push": "Άμεσες ειδοποιήσεις",
@@ -344,6 +355,7 @@
   "status.admin_account": "Άνοιγμα λειτουργίας διαμεσολάβησης για τον/την @{name}",
   "status.admin_status": "Άνοιγμα αυτής της δημοσίευσης στη λειτουργία διαμεσολάβησης",
   "status.block": "Αποκλεισμός @{name}",
+  "status.bookmark": "Σελιδοδείκτης",
   "status.cancel_reblog_private": "Ακύρωσε την προώθηση",
   "status.cannot_reblog": "Αυτή η δημοσίευση δεν μπορεί να προωθηθεί",
   "status.copy": "Αντιγραφή συνδέσμου της δημοσίευσης",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} προώθησε",
   "status.reblogs.empty": "Κανείς δεν προώθησε αυτό το τουτ ακόμα. Μόλις το κάνει κάποια, θα εμφανιστούν εδώ.",
   "status.redraft": "Σβήσε & ξαναγράψε",
+  "status.remove_bookmark": "Αφαίρεση σελιδοδείκτη",
   "status.reply": "Απάντησε",
   "status.replyAll": "Απάντησε στην συζήτηση",
   "status.report": "Κατάγγειλε @{name}",
@@ -393,16 +406,18 @@
   "time_remaining.minutes": "απομένουν {number, plural, one {# λεπτό} other {# λεπτά}}",
   "time_remaining.moments": "Απομένουν στιγμές",
   "time_remaining.seconds": "απομένουν {number, plural, one {# δευτερόλεπτο} other {# δευτερόλεπτα}}",
-  "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} μιλάνε",
+  "trends.count_by_accounts": "{count} {rawCount, plural, one {άτομο μιλάει} other {άτομα μιλάνε}}",
   "trends.trending_now": "Δημοφιλή τώρα",
   "ui.beforeunload": "Το προσχέδιό σου θα χαθεί αν φύγεις από το Mastodon.",
   "upload_area.title": "Drag & drop για να ανεβάσεις",
   "upload_button.label": "Πρόσθεσε πολυμέσα ({formats})",
   "upload_error.limit": "Υπέρβαση ορίου μεγέθους ανεβασμένων αρχείων.",
   "upload_error.poll": "Στις δημοσκοπήσεις δεν επιτρέπεται η μεταφόρτωση αρχείου.",
+  "upload_form.audio_description": "Περιγραφή για άτομα με προβλήματα ακοής",
   "upload_form.description": "Περιέγραψε για όσους & όσες έχουν προβλήματα όρασης",
   "upload_form.edit": "Ενημέρωση",
   "upload_form.undo": "Διαγραφή",
+  "upload_form.video_description": "Περιγραφή για άτομα με προβλήματα ακοής ή όρασης",
   "upload_modal.analyzing_picture": "Ανάλυση εικόνας…",
   "upload_modal.apply": "Εφαρμογή",
   "upload_modal.description_placeholder": "Λύκος μαύρος και ισχνός του πατέρα του καημός",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Προεπισκόπηση ({ratio})",
   "upload_progress.label": "Ανεβαίνει...",
   "video.close": "Κλείσε το βίντεο",
+  "video.download": "Λήψη αρχείου",
   "video.exit_fullscreen": "Έξοδος από πλήρη οθόνη",
   "video.expand": "Επέκταση βίντεο",
   "video.fullscreen": "Πλήρης οθόνη",
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index 84e40a9fc..e25199905 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -1,6 +1,7 @@
 {
   "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": "Hide everything from {domain}",
   "account.blocked": "Blocked",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Rate limited",
   "alert.unexpected.message": "An unexpected error occurred.",
   "alert.unexpected.title": "Oops!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} per week",
   "boost_modal.combo": "You can press {combo} to skip this next time",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -87,6 +89,8 @@
   "compose_form.poll.duration": "Poll duration",
   "compose_form.poll.option_placeholder": "Choice {number}",
   "compose_form.poll.remove_option": "Remove this choice",
+  "compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices",
+  "compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
   "compose_form.publish": "Toot",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Mark media as sensitive",
@@ -184,6 +188,8 @@
   "home.column_settings.basic": "Basic",
   "home.column_settings.show_reblogs": "Show boosts",
   "home.column_settings.show_replies": "Show replies",
+  "home.hide_announcements": "Hide announcements",
+  "home.show_announcements": "Show announcements",
   "intervals.full.days": "{number, plural, one {# day} other {# days}}",
   "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
   "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}",
@@ -334,6 +340,7 @@
   "relative_time.just_now": "now",
   "relative_time.minutes": "{number}m",
   "relative_time.seconds": "{number}s",
+  "relative_time.today": "today",
   "reply_indicator.cancel": "Cancel",
   "report.forward": "Forward to {target}",
   "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
index 0070cdb5c..e84e59b2e 100644
--- a/app/javascript/mastodon/locales/eo.json
+++ b/app/javascript/mastodon/locales/eo.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Aldoni al aŭ forigi el listoj",
   "account.badges.bot": "Roboto",
+  "account.badges.group": "Grupo",
   "account.block": "Bloki @{name}",
   "account.block_domain": "Kaŝi ĉion de {domain}",
   "account.blocked": "Blokita",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Mesaĝkvante limigita",
   "alert.unexpected.message": "Neatendita eraro okazis.",
   "alert.unexpected.title": "Ups!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} semajne",
   "boost_modal.combo": "Vi povas premi {combo} por preterpasi sekvafoje",
   "bundle_column_error.body": "Io misfunkciis en la ŝargado de ĉi tiu elemento.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Io misfunkciis en la ŝargado de ĉi tiu elemento.",
   "bundle_modal_error.retry": "Bonvolu reprovi",
   "column.blocks": "Blokitaj uzantoj",
+  "column.bookmarks": "Legosignoj",
   "column.community": "Loka tempolinio",
   "column.direct": "Rektaj mesaĝoj",
   "column.directory": "Trarigardi profilojn",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Balotenketa daŭro",
   "compose_form.poll.option_placeholder": "Elekteblo {number}",
   "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_loud": "{publish}!",
   "compose_form.sensitive.hide": "Marki la aŭdovidaĵojn kiel tiklaj",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Neniu mesaĝ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.community": "La loka tempolinio 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.domain_blocks": "Ankoraŭ neniu domajno estas blokita.",
@@ -154,7 +160,7 @@
   "empty_column.public": "Estas nenio ĉi tie! Publike skribu ion, aŭ mane sekvu uzantojn de aliaj serviloj por plenigi la publikan tempolinion",
   "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.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.",
-  "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard",
+  "errors.unexpected_crash.copy_stacktrace": "Kopii stakspuron en tondujo",
   "errors.unexpected_crash.report_issue": "Raporti problemon",
   "follow_request.authorize": "Rajtigi",
   "follow_request.reject": "Rifuzi",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "por malfermi la liston de silentigitaj uzantoj",
   "keyboard_shortcuts.my_profile": "por malfermi vian profilon",
   "keyboard_shortcuts.notifications": "por malfermi la kolumnon de sciigoj",
+  "keyboard_shortcuts.open_media": "por malfermi aŭdovidaĵon",
   "keyboard_shortcuts.pinned": "por malfermi la liston de alpinglitaj mesaĝoj",
   "keyboard_shortcuts.profile": "por malfermi la profilon de la aŭtoro",
   "keyboard_shortcuts.reply": "por respondi",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Ĉu vi volas kaŝi la sciigojn de ĉi tiu uzanto?",
   "navigation_bar.apps": "Telefonaj aplikaĵoj",
   "navigation_bar.blocks": "Blokitaj uzantoj",
+  "navigation_bar.bookmarks": "Legosignoj",
   "navigation_bar.community_timeline": "Loka tempolinio",
   "navigation_bar.compose": "Skribi novan mesaĝon",
   "navigation_bar.direct": "Rektaj mesaĝoj",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Sekureco",
   "notification.favourite": "{name} stelumis vian mesaĝon",
   "notification.follow": "{name} eksekvis vin",
+  "notification.follow_request": "{name} petis sekvi vin",
   "notification.mention": "{name} menciis vin",
+  "notification.own_poll": "Via balotenketo finiĝitis",
   "notification.poll": "Partoprenita balotenketo finiĝis",
   "notification.reblog": "{name} diskonigis vian mesaĝon",
   "notifications.clear": "Forviŝi sciigojn",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Rapida filtra breto",
   "notifications.column_settings.filter_bar.show": "Montri",
   "notifications.column_settings.follow": "Novaj sekvantoj:",
+  "notifications.column_settings.follow_request": "Novaj petoj de sekvado:",
   "notifications.column_settings.mention": "Mencioj:",
   "notifications.column_settings.poll": "Balotenketaj rezultoj:",
   "notifications.column_settings.push": "Puŝsciigoj",
@@ -344,6 +355,7 @@
   "status.admin_account": "Malfermi la kontrolan interfacon por @{name}",
   "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.copy": "Kopii la ligilon al la mesaĝo",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} diskonigis",
   "status.reblogs.empty": "Ankoraŭ neniu diskonigis tiun mesaĝon. Kiam iu faros tion, tiu aperos ĉi tie.",
   "status.redraft": "Forigi kaj reskribi",
+  "status.remove_bookmark": "Forigi legosignon",
   "status.reply": "Respondi",
   "status.replyAll": "Respondi al la fadeno",
   "status.report": "Signali @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Aldoni aŭdovidaĵon (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_error.limit": "Limo de dosiera alŝutado transpasita.",
   "upload_error.poll": "Alŝuto de dosiero ne permesita kun balotenketo.",
+  "upload_form.audio_description": "Priskribi por homoj kiuj malfacile aŭdi",
   "upload_form.description": "Priskribi por misvidantaj homoj",
   "upload_form.edit": "Redakti",
   "upload_form.undo": "Forigi",
+  "upload_form.video_description": "Priskribi por homoj kiuj malfacile aŭdi aŭ vidi",
   "upload_modal.analyzing_picture": "Bilda analizado…",
   "upload_modal.apply": "Apliki",
   "upload_modal.description_placeholder": "Laŭ Ludoviko Zamenhof bongustas freŝa ĉeĥa manĝaĵo kun spicoj",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Antaŭvido ({ratio})",
   "upload_progress.label": "Alŝutado…",
   "video.close": "Fermi la videon",
+  "video.download": "Elŝuti dosieron",
   "video.exit_fullscreen": "Eksigi plenekrana",
   "video.expand": "Grandigi la videon",
   "video.fullscreen": "Igi plenekrana",
diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json
index 56f8781a1..f83e5a251 100644
--- a/app/javascript/mastodon/locales/es-AR.json
+++ b/app/javascript/mastodon/locales/es-AR.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Agregar o quitar de las listas",
   "account.badges.bot": "Bot",
+  "account.badges.group": "Grupo",
   "account.block": "Bloquear a @{name}",
   "account.block_domain": "Ocultar todo de {domain}",
   "account.blocked": "Bloqueado",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Tarifa limitada",
   "alert.unexpected.message": "Ocurrió un error.",
   "alert.unexpected.title": "¡Epa!",
+  "announcement.announcement": "Anuncio",
   "autosuggest_hashtag.per_week": "{count} por semana",
   "boost_modal.combo": "Podés hacer clic en {combo} para saltar esto la próxima vez",
   "bundle_column_error.body": "Algo salió mal al cargar este componente.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Algo salió mal al cargar este componente.",
   "bundle_modal_error.retry": "Intentá de nuevo",
   "column.blocks": "Usuarios bloqueados",
+  "column.bookmarks": "Marcadores",
   "column.community": "Línea temporal local",
   "column.direct": "Mensajes directos",
   "column.directory": "Explorar perfiles",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Duración de la encuesta",
   "compose_form.poll.option_placeholder": "Opción {number}",
   "compose_form.poll.remove_option": "Quitá esta opción",
+  "compose_form.poll.switch_to_multiple": "Cambiar encuesta para permitir opciones múltiples",
+  "compose_form.poll.switch_to_single": "Cambiar encuesta para permitir una sola opción",
   "compose_form.publish": "Tootear",
   "compose_form.publish_loud": "¡{publish}!",
   "compose_form.sensitive.hide": "Marcar medio como sensible",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "¡No hay toots aquí!",
   "empty_column.account_unavailable": "Perfil no disponible",
   "empty_column.blocks": "Todavía no bloqueaste a ningún usuario.",
+  "empty_column.bookmarked_statuses": "Todavía no tenés toots 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.domain_blocks": "Todavía no hay dominios ocultos.",
@@ -185,7 +191,7 @@
   "introduction.federation.federated.headline": "Federado",
   "introduction.federation.federated.text": "Los toots públicos de otros servidores del fediverso aparecerán en la línea temporal federada.",
   "introduction.federation.home.headline": "Principal",
-  "introduction.federation.home.text": "Los toots de las personas que seguíss aparecerán en tu línea temporal principal. ¡Podés seguir a cualquiera en cualquier servidor!",
+  "introduction.federation.home.text": "Los toots de las personas que seguís aparecerán en tu línea temporal principal. ¡Podés seguir a cualquiera en cualquier servidor!",
   "introduction.federation.local.headline": "Local",
   "introduction.federation.local.text": "Los toots públicos de las personas en el mismo servidor aparecerán en la línea temporal local.",
   "introduction.interactions.action": "¡Terminar tutorial!",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "abrir la lista de usuarios silenciados",
   "keyboard_shortcuts.my_profile": "para abrir tu perfil",
   "keyboard_shortcuts.notifications": "para abrir la columna de notificaciones",
+  "keyboard_shortcuts.open_media": "para abrir archivos de medios",
   "keyboard_shortcuts.pinned": "para abrir lista de toots fijados",
   "keyboard_shortcuts.profile": "para abrir el perfil del autor",
   "keyboard_shortcuts.reply": "para responder",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "¿Querés ocultar las notificaciones de este usuario?",
   "navigation_bar.apps": "Aplicaciones móviles",
   "navigation_bar.blocks": "Usuarios bloqueados",
+  "navigation_bar.bookmarks": "Marcadores",
   "navigation_bar.community_timeline": "Línea temporal local",
   "navigation_bar.compose": "Redactar un nuevo toot",
   "navigation_bar.direct": "Mensajes directos",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Seguridad",
   "notification.favourite": "{name} marcó tu estado como favorito",
   "notification.follow": "{name} te empezó a seguir",
+  "notification.follow_request": "{name} solicitó seguirte",
   "notification.mention": "{name} te mencionó",
+  "notification.own_poll": "Tu encuesta finalizó",
   "notification.poll": "Finalizó una encuesta en la que votaste",
   "notification.reblog": "{name} retooteó tu estado",
   "notifications.clear": "Limpiar notificaciones",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Barra de filtrado rápido",
   "notifications.column_settings.filter_bar.show": "Mostrar",
   "notifications.column_settings.follow": "Nuevos seguidores:",
+  "notifications.column_settings.follow_request": "Nuevas solicitudes de seguimiento:",
   "notifications.column_settings.mention": "Menciones:",
   "notifications.column_settings.poll": "Resultados de la encuesta:",
   "notifications.column_settings.push": "Notificaciones push",
@@ -344,6 +355,7 @@
   "status.admin_account": "Abrir interface de moderación para @{name}",
   "status.admin_status": "Abrir este estado en la interface de moderación",
   "status.block": "Bloquear a @{name}",
+  "status.bookmark": "Marcador",
   "status.cancel_reblog_private": "Quitar retoot",
   "status.cannot_reblog": "No se puede retootear este toot",
   "status.copy": "Copiar enlace al estado",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} retooteó",
   "status.reblogs.empty": "Todavía nadie retooteó este toot. Cuando alguien lo haga, se mostrará acá.",
   "status.redraft": "Eliminar toot original y editarlo",
+  "status.remove_bookmark": "Quitar marcador",
   "status.reply": "Responder",
   "status.replyAll": "Responder al hilo",
   "status.report": "Denunciar a @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Agregar medios ({formats})",
   "upload_error.limit": "Se excedió el límite de subida de archivos.",
   "upload_error.poll": "No se permite la subida de archivos en encuestas.",
+  "upload_form.audio_description": "Describir para personas con problemas auditivos",
   "upload_form.description": "Agregar descripción para los usuarios con dificultades visuales",
   "upload_form.edit": "Editar",
   "upload_form.undo": "Eliminar",
+  "upload_form.video_description": "Describir para personas con problemas auditivos o visuales",
   "upload_modal.analyzing_picture": "Analizando imagen…",
   "upload_modal.apply": "Aplicar",
   "upload_modal.description_placeholder": "El veloz murciélago hindú comía feliz cardillo y kiwi. La cigüeña tocaba el saxofón detrás del palenque de paja.",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Previsualización ({ratio})",
   "upload_progress.label": "Subiendo…",
   "video.close": "Cerrar video",
+  "video.download": "Descargar archivo",
   "video.exit_fullscreen": "Salir de pantalla completa",
   "video.expand": "Expandir vídeo",
   "video.fullscreen": "Pantalla completa",
diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json
index c213a03e1..93484b412 100644
--- a/app/javascript/mastodon/locales/es.json
+++ b/app/javascript/mastodon/locales/es.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Agregar o eliminar de listas",
   "account.badges.bot": "Bot",
+  "account.badges.group": "Grupo",
   "account.block": "Bloquear a @{name}",
   "account.block_domain": "Ocultar todo de {domain}",
   "account.blocked": "Bloqueado",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Tarifa limitada",
   "alert.unexpected.message": "Hubo un error inesperado.",
   "alert.unexpected.title": "¡Ups!",
+  "announcement.announcement": "Anuncio",
   "autosuggest_hashtag.per_week": "{count} por semana",
   "boost_modal.combo": "Puedes hacer clic en {combo} para saltar este aviso la próxima vez",
   "bundle_column_error.body": "Algo salió mal al cargar este componente.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Algo salió mal al cargar este componente.",
   "bundle_modal_error.retry": "Inténtalo de nuevo",
   "column.blocks": "Usuarios bloqueados",
+  "column.bookmarks": "Marcadores",
   "column.community": "Línea de tiempo local",
   "column.direct": "Mensajes directos",
   "column.directory": "Buscar perfiles",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Duración de la encuesta",
   "compose_form.poll.option_placeholder": "Elección {number}",
   "compose_form.poll.remove_option": "Eliminar esta opción",
+  "compose_form.poll.switch_to_multiple": "Modificar encuesta para permitir múltiples opciones",
+  "compose_form.poll.switch_to_single": "Modificar encuesta para permitir una única opción",
   "compose_form.publish": "Tootear",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Marcar multimedia como sensible",
@@ -127,7 +132,7 @@
   "emoji_button.food": "Comida y bebida",
   "emoji_button.label": "Insertar emoji",
   "emoji_button.nature": "Naturaleza",
-  "emoji_button.not_found": "No hay emojos!! (╯°□°)╯︵ ┻━┻",
+  "emoji_button.not_found": "No hay emojis!! ¯\\_(ツ)_/¯",
   "emoji_button.objects": "Objetos",
   "emoji_button.people": "Gente",
   "emoji_button.recent": "Usados frecuentemente",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "¡No hay toots aquí!",
   "empty_column.account_unavailable": "Perfil no disponible",
   "empty_column.blocks": "Aún no has bloqueado a ningún usuario.",
+  "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 directo. Cuando envíes o recibas uno, se mostrará aquí.",
   "empty_column.domain_blocks": "Todavía no hay dominios ocultos.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "abrir la lista de usuarios silenciados",
   "keyboard_shortcuts.my_profile": "abrir tu perfil",
   "keyboard_shortcuts.notifications": "abrir la columna de notificaciones",
+  "keyboard_shortcuts.open_media": "para abrir archivos multimedia",
   "keyboard_shortcuts.pinned": "abrir la lista de toots destacados",
   "keyboard_shortcuts.profile": "abrir el perfil del autor",
   "keyboard_shortcuts.reply": "para responder",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Ocultar notificaciones de este usuario?",
   "navigation_bar.apps": "Aplicaciones móviles",
   "navigation_bar.blocks": "Usuarios bloqueados",
+  "navigation_bar.bookmarks": "Marcadores",
   "navigation_bar.community_timeline": "Historia local",
   "navigation_bar.compose": "Escribir un nuevo toot",
   "navigation_bar.direct": "Mensajes directos",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Seguridad",
   "notification.favourite": "{name} marcó tu estado como favorito",
   "notification.follow": "{name} te empezó a seguir",
+  "notification.follow_request": "{name} ha solicitado seguirte",
   "notification.mention": "{name} te ha mencionado",
+  "notification.own_poll": "Tu encuesta ha terminado",
   "notification.poll": "Una encuesta en la que has votado ha terminado",
   "notification.reblog": "{name} ha retooteado tu estado",
   "notifications.clear": "Limpiar notificaciones",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Barra de filtrado rápido",
   "notifications.column_settings.filter_bar.show": "Mostrar",
   "notifications.column_settings.follow": "Nuevos seguidores:",
+  "notifications.column_settings.follow_request": "Nuevas solicitudes de seguimiento:",
   "notifications.column_settings.mention": "Menciones:",
   "notifications.column_settings.poll": "Resultados de la votación:",
   "notifications.column_settings.push": "Notificaciones push",
@@ -344,6 +355,7 @@
   "status.admin_account": "Abrir interfaz de moderación para @{name}",
   "status.admin_status": "Abrir este estado en la interfaz de moderación",
   "status.block": "Bloquear a @{name}",
+  "status.bookmark": "Marcador",
   "status.cancel_reblog_private": "Des-impulsar",
   "status.cannot_reblog": "Este toot no puede retootearse",
   "status.copy": "Copiar enlace al estado",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "Retooteado por {name}",
   "status.reblogs.empty": "Nadie impulsó este toot todavía. Cuando alguien lo haga, aparecerá aqui.",
   "status.redraft": "Borrar y volver a borrador",
+  "status.remove_bookmark": "Eliminar marcador",
   "status.reply": "Responder",
   "status.replyAll": "Responder al hilo",
   "status.report": "Reportar",
@@ -400,9 +413,11 @@
   "upload_button.label": "Subir multimedia (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_error.limit": "Límite de subida de archivos excedido.",
   "upload_error.poll": "Subida de archivos no permitida con encuestas.",
+  "upload_form.audio_description": "Describir para personas con problemas auditivos",
   "upload_form.description": "Describir para los usuarios con dificultad visual",
   "upload_form.edit": "Editar",
   "upload_form.undo": "Borrar",
+  "upload_form.video_description": "Describir para personas con problemas auditivos o visuales",
   "upload_modal.analyzing_picture": "Analizando imagen…",
   "upload_modal.apply": "Aplicar",
   "upload_modal.description_placeholder": "Un rápido zorro marrón salta sobre el perro perezoso",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Vista previa ({ratio})",
   "upload_progress.label": "Subiendo…",
   "video.close": "Cerrar video",
+  "video.download": "Descargar archivo",
   "video.exit_fullscreen": "Salir de pantalla completa",
   "video.expand": "Expandir vídeo",
   "video.fullscreen": "Pantalla completa",
diff --git a/app/javascript/mastodon/locales/et.json b/app/javascript/mastodon/locales/et.json
index 6b97393d2..9a13b4ee0 100644
--- a/app/javascript/mastodon/locales/et.json
+++ b/app/javascript/mastodon/locales/et.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Lisa või Eemalda nimekirjadest",
   "account.badges.bot": "Robot",
+  "account.badges.group": "Grupp",
   "account.block": "Blokeeri @{name}",
   "account.block_domain": "Peida kõik domeenist {domain}",
   "account.blocked": "Blokeeritud",
@@ -14,7 +15,7 @@
   "account.followers.empty": "Keegi ei jälgi seda kasutajat veel.",
   "account.follows": "Jälgib",
   "account.follows.empty": "See kasutaja ei jälgi veel kedagi.",
-  "account.follows_you": "Jälgib sind",
+  "account.follows_you": "Jälgib Teid",
   "account.hide_reblogs": "Peida upitused kasutajalt @{name}",
   "account.last_status": "Viimati aktiivne",
   "account.link_verified_on": "Selle lingi autorsust kontrolliti {date}",
@@ -42,15 +43,17 @@
   "alert.rate_limited.title": "Piiratud",
   "alert.unexpected.message": "Tekkis ootamatu viga.",
   "alert.unexpected.title": "Oih!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} nädalas",
-  "boost_modal.combo": "Saad vajutada {combo}, et see järgmine kord vahele jätta",
-  "bundle_column_error.body": "Mindagi läks valesti selle komponendi laadimisel.",
+  "boost_modal.combo": "Võite vajutada {combo}, et see järgmine kord vahele jätta",
+  "bundle_column_error.body": "Midagi läks valesti selle komponendi laadimisel.",
   "bundle_column_error.retry": "Proovi uuesti",
   "bundle_column_error.title": "Võrgu viga",
   "bundle_modal_error.close": "Sulge",
   "bundle_modal_error.message": "Selle komponendi laadimisel läks midagi viltu.",
   "bundle_modal_error.retry": "Proovi uuesti",
   "column.blocks": "Blokeeritud kasutajad",
+  "column.bookmarks": "Järjehoidjad",
   "column.community": "Kohalik ajajoon",
   "column.direct": "Otsesõnumid",
   "column.directory": "Sirvi profiile",
@@ -61,7 +64,7 @@
   "column.lists": "Nimekirjad",
   "column.mutes": "Vaigistatud kasutajad",
   "column.notifications": "Teated",
-  "column.pins": "Kinnitatud upitused",
+  "column.pins": "Kinnitatud tuututused",
   "column.public": "Föderatiivne ajajoon",
   "column_back_button.label": "Tagasi",
   "column_header.hide_settings": "Peida sätted",
@@ -75,13 +78,15 @@
   "compose_form.direct_message_warning": "See tuut saadetakse ainult mainitud kasutajatele.",
   "compose_form.direct_message_warning_learn_more": "Vaata veel",
   "compose_form.hashtag_warning": "Seda tuuti ei kuvata ühegi sildi all, sest see on kirjendamata. Ainult avalikud tuutid on sildi järgi otsitavad.",
-  "compose_form.lock_disclaimer": "Sinu konto ei ole {locked}. Igaüks saab sind jälgida ja näha su ainult-jälgijatele postitusi.",
+  "compose_form.lock_disclaimer": "Teie konto ei ole {locked}. Igaüks saab Teid jälgida ja näha Teie ainult-jälgijatele postitusi.",
   "compose_form.lock_disclaimer.lock": "lukus",
-  "compose_form.placeholder": "Millest mõtled?",
+  "compose_form.placeholder": "Millest mõtlete?",
   "compose_form.poll.add_option": "Lisa valik",
   "compose_form.poll.duration": "Küsitluse kestus",
   "compose_form.poll.option_placeholder": "Valik {number}",
   "compose_form.poll.remove_option": "Eemalda see valik",
+  "compose_form.poll.switch_to_multiple": "Muuda küsitlust lubamaks mitut valikut",
+  "compose_form.poll.switch_to_single": "Muuda küsitlust lubamaks ainult ühte valikut",
   "compose_form.publish": "Tuut",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Märgista meedia tundlikuks",
@@ -89,28 +94,28 @@
   "compose_form.sensitive.unmarked": "Meedia ei ole sensitiivseks märgitud",
   "compose_form.spoiler.marked": "Tekst on hoiatuse taha peidetud",
   "compose_form.spoiler.unmarked": "Tekst ei ole peidetud",
-  "compose_form.spoiler_placeholder": "Kirjuta oma hoiatus siia",
+  "compose_form.spoiler_placeholder": "Kirjutage oma hoiatus siia",
   "confirmation_modal.cancel": "Katkesta",
   "confirmations.block.block_and_report": "Blokeeri & Teata",
   "confirmations.block.confirm": "Blokeeri",
-  "confirmations.block.message": "Oled kindel, et soovid blokkida {name}?",
+  "confirmations.block.message": "Olete kindel, et soovite blokeerida {name}?",
   "confirmations.delete.confirm": "Kustuta",
-  "confirmations.delete.message": "Oled kindel, et soovid selle staatuse kustutada?",
+  "confirmations.delete.message": "Olete kindel, et soovite selle staatuse kustutada?",
   "confirmations.delete_list.confirm": "Kustuta",
-  "confirmations.delete_list.message": "Oled kindel, et soovid selle nimekirja püsivalt kustutada?",
+  "confirmations.delete_list.message": "Olete kindel, et soovite selle nimekirja püsivalt kustutada?",
   "confirmations.domain_block.confirm": "Peida terve domeen",
-  "confirmations.domain_block.message": "Oled ikka päris kindel, et soovid blokeerida terve  {domain}? Enamikul juhtudel piisab mõnest sihitud blokist või vaigistusest, mis on eelistatav. Sa ei näe selle domeeni sisu üheski avalikus ajajoones või teadetes. Sinu jälgijad sellest domeenist eemaldatakse.",
+  "confirmations.domain_block.message": "Olete ikka päris kindel, et soovite blokeerida terve {domain}? Enamikul juhtudel piisab mõnest sihitud blokist või vaigistusest, mis on eelistatav. Te ei näe selle domeeni sisu üheski avalikus ajajoones või teadetes. Teie jälgijad sellest domeenist eemaldatakse.",
   "confirmations.logout.confirm": "Välju",
-  "confirmations.logout.message": "Kas oled kindel, et soovid välja logida?",
+  "confirmations.logout.message": "Kas olete kindel, et soovite välja logida?",
   "confirmations.mute.confirm": "Vaigista",
   "confirmations.mute.explanation": "See peidab postitusi temalt ning postitusi, kus mainitakse neid, kuid see lubab neil ikka näha Teie postitusi ning Teid jälgida.",
-  "confirmations.mute.message": "Oled kindel, et soovid {name} vaigistada?",
+  "confirmations.mute.message": "Olete kindel, et soovite {name} vaigistada?",
   "confirmations.redraft.confirm": "Kustuta & taasalusta",
-  "confirmations.redraft.message": "Oled kindel, et soovid selle staatuse kustutada ja alustada uuesti? Lemmikud ja upitused lähevad kaotsi ja vastused originaaalpostitusele jäävad orvuks.",
+  "confirmations.redraft.message": "Olete kindel, et soovite selle staatuse kustutada ja alustada uuesti? Lemmikud ja upitused lähevad kaotsi ja vastused originaaalpostitusele jäävad orvuks.",
   "confirmations.reply.confirm": "Vasta",
-  "confirmations.reply.message": "Kohene vastamine kirjutab üle sõnumi, mida hetkel koostad. Oled kindel, et soovid jätkata?",
+  "confirmations.reply.message": "Praegu vastamine kirjutab üle sõnumi, mida hetkel koostate. Olete kindel, et soovite jätkata?",
   "confirmations.unfollow.confirm": "Ära jälgi",
-  "confirmations.unfollow.message": "Oled kindel, et ei soovi jälgida {name}?",
+  "confirmations.unfollow.message": "Olete kindel, et ei soovi rohkem jälgida kasutajat {name}?",
   "conversation.delete": "Kustuta vestlus",
   "conversation.mark_as_read": "Märgi loetuks",
   "conversation.open": "Vaata vestlust",
@@ -138,19 +143,20 @@
   "empty_column.account_timeline": "Siin tuute ei ole!",
   "empty_column.account_unavailable": "Profiil pole saadaval",
   "empty_column.blocks": "Sa ei ole veel ühtegi kasutajat blokeerinud.",
-  "empty_column.community": "Kohalik ajajoon on tühi. Kirjuta midagi avalikult, et pall veerema saada!",
-  "empty_column.direct": "Sul ei veel otsesõnumeid. Kui saadad või võtad mõne vastu, ilmuvad nad siia.",
+  "empty_column.bookmarked_statuses": "Teil pole veel järjehoidjatesse lisatud tuututusi. Kui lisate mõne, näete neid siin.",
+  "empty_column.community": "Kohalik ajajoon on tühi. Kirjutage midagi avalikult, et pall veerema ajada!",
+  "empty_column.direct": "Teil ei ole veel otsesõnumeid. Kui saadate või võtate mõne vastu, ilmuvad nad siia.",
   "empty_column.domain_blocks": "Siin ei ole veel peidetud domeene.",
-  "empty_column.favourited_statuses": "Sul pole veel lemmikuid tuute. Kui märgid mõne, näed neid siin.",
+  "empty_column.favourited_statuses": "Teil pole veel lemmikuid tuute. Kui märgite mõne, näete neid siin.",
   "empty_column.favourites": "Keegi pole veel seda tuuti lemmikuks märkinud. Kui seegi seda teeb, näed seda siin.",
-  "empty_column.follow_requests": "Sul pole veel ühtegi jälgimise taotlust. Kui saad mõne, näed seda siin.",
+  "empty_column.follow_requests": "Teil pole veel ühtegi jälgimise taotlust. Kui saate mõne, näete neid siin.",
   "empty_column.hashtag": "Selle sildiga pole veel midagi.",
-  "empty_column.home": "Sinu kodu ajajoon on tühi! Külasta {public} või kasuta otsingut alustamaks ja kohtamaks teisi kasutajaid.",
+  "empty_column.home": "Teie kodu ajajoon on tühi! Külastage {public} või kasutage otsingut alustamaks ja kohtamaks teisi kasutajaid.",
   "empty_column.home.public_timeline": "avalik ajajoon",
   "empty_column.list": "Siin nimstus pole veel midagi. Kui selle nimistu liikmed postitavad uusi staatusi, näed neid siin.",
-  "empty_column.lists": "Sul ei ole veel ühtegi nimekirja. Kui lood mõne, näed seda siin.",
-  "empty_column.mutes": "Sa pole veel ühtegi kasutajat vaigistanud.",
-  "empty_column.notifications": "Sul ei ole veel teateid. Suhtle teistega alustamaks vestlust.",
+  "empty_column.lists": "Teil ei ole veel ühtegi nimekirja. Kui loote mõne, näete neid siin.",
+  "empty_column.mutes": "Te pole veel ühtegi kasutajat vaigistanud.",
+  "empty_column.notifications": "Teil ei ole veel teateid. Suhelge teistega alustamaks vestlust.",
   "empty_column.public": "Siin pole midagi! Kirjuta midagi avalikut või jälgi ise kasutajaid täitmaks seda ruumi",
   "error.unexpected_crash.explanation": "Meie poolse probleemi või veebilehitseja ühilduvus probleemi tõttu ei suutnud me Teile seda lehekülge korrektselt näidata.",
   "error.unexpected_crash.next_steps": "Proovige lehekülge uuesti avada. Kui see ei aita, võite proovida kasutada Mastodoni mõne muu veebilehitseja või äppi kaudu.",
@@ -163,7 +169,7 @@
   "getting_started.documentation": "Dokumentatsioon",
   "getting_started.heading": "Alustamine",
   "getting_started.invite": "Kutsu inimesi",
-  "getting_started.open_source_notice": "Mastodon on avatud lähtekoodiga tarkvara. Saad panustada või teatada probleemidest GitHubis {github}.",
+  "getting_started.open_source_notice": "Mastodon on avatud lähtekoodiga tarkvara. Saate panustada või teatada probleemidest GitHubis {github}.",
   "getting_started.security": "Turvalisus",
   "getting_started.terms": "Kasutustingimused",
   "hashtag.column_header.tag_mode.all": "ja {additional}",
@@ -185,19 +191,19 @@
   "introduction.federation.federated.headline": "Föderatiivne",
   "introduction.federation.federated.text": "Avalikud postitused teistest föderatsiooni serveritest kuvatakse föderatiivsel ajajoonel.",
   "introduction.federation.home.headline": "Kodu",
-  "introduction.federation.home.text": "Inimest postitused keda jälgid kuvatakse sinu koduajajoonel. Saad jälgida igaüht igas serveris!",
+  "introduction.federation.home.text": "Inimeste, keda jälgite, postitused kuvatakse Teie koduajajoonel. Saate jälgida igaüht igas serveris!",
   "introduction.federation.local.headline": "Kohalik",
   "introduction.federation.local.text": "Samas serveris olevate inimeste postitused kuvatakse kohalikul ajajoonel.",
   "introduction.interactions.action": "Välju õpetusest!",
   "introduction.interactions.favourite.headline": "Lemmik",
-  "introduction.interactions.favourite.text": "Saad tuuti salvestada ja anda autorile teada, et meeldis märkides selle lemmikuks.",
+  "introduction.interactions.favourite.text": "Saate tuuti salvestada ja anda autorile teada, et see meeldis Teile, märkides selle lemmikuks.",
   "introduction.interactions.reblog.headline": "Upita",
-  "introduction.interactions.reblog.text": "Saad jagada teiste inimeste tuute oma jälgijatega upitades neid.",
+  "introduction.interactions.reblog.text": "Saate jagada teiste inimeste tuute oma jälgijatega neid upitades.",
   "introduction.interactions.reply.headline": "Vasta",
-  "introduction.interactions.reply.text": "Saad vastata teiste ja enda tuutidele, mis ühendab nad kokku aruteluks.",
+  "introduction.interactions.reply.text": "Saate vastata teiste ja enda tuutidele, mis ühendab nad kokku aruteluks.",
   "introduction.welcome.action": "Lähme!",
   "introduction.welcome.headline": "Esimesed sammud",
-  "introduction.welcome.text": "Teretulemast fediversumisse! Mõne aja pärast saad avaldada sõnumeid ja rääkida oma sõpradega läbi laia valiku serverite. Aga see server, {domain}, on eriline—ta majutab sinu profiili. Seega jäta ta nimi meelde.",
+  "introduction.welcome.text": "Teretulemast fediversumisse! Mõne aja pärast saate avaldada sõnumeid ja rääkida oma sõpradega läbi laia valiku serverite. Aga see server, {domain}, on eriline—see majutab sinu profiili. Seega jäta selle nimi meelde.",
   "keyboard_shortcuts.back": "tagasiminekuks",
   "keyboard_shortcuts.blocked": "avamaks blokeeritud kasutajate nimistut",
   "keyboard_shortcuts.boost": "upitamiseks",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "avamaks vaigistatud kasutajate nimistut",
   "keyboard_shortcuts.my_profile": "avamaks profiili",
   "keyboard_shortcuts.notifications": "avamaks teadete tulpa",
+  "keyboard_shortcuts.open_media": "et avada meedia",
   "keyboard_shortcuts.pinned": "avamaks kinnitatud tuutide nimistut",
   "keyboard_shortcuts.profile": "avamaks autori profiili",
   "keyboard_shortcuts.reply": "vastamiseks",
@@ -241,8 +248,8 @@
   "lists.edit.submit": "Muuda pealkiri",
   "lists.new.create": "Lisa nimistu",
   "lists.new.title_placeholder": "Uus nimistu pealkiri",
-  "lists.search": "Otsi sinu poolt jälgitavate inimese hulgast",
-  "lists.subheading": "Sinu nimistud",
+  "lists.search": "Otsi Teie poolt jälgitavate inimese hulgast",
+  "lists.subheading": "Teie nimistud",
   "load_pending": "{count, plural, one {# uus kirje} other {# uut kirjet}}",
   "loading_indicator.label": "Laeb..",
   "media_gallery.toggle_visible": "Lülita nähtavus",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Kas peita teated sellelt kasutajalt?",
   "navigation_bar.apps": "Mobiilrakendused",
   "navigation_bar.blocks": "Blokeeritud kasutajad",
+  "navigation_bar.bookmarks": "Järjehoidjad",
   "navigation_bar.community_timeline": "Kohalik ajajoon",
   "navigation_bar.compose": "Koosta uus tuut",
   "navigation_bar.direct": "Otsesõnumid",
@@ -271,19 +279,22 @@
   "navigation_bar.preferences": "Eelistused",
   "navigation_bar.public_timeline": "Föderatiivne ajajoon",
   "navigation_bar.security": "Turvalisus",
-  "notification.favourite": "{name} märkis su staatuse lemmikuks",
-  "notification.follow": "{name} jälgib sind",
-  "notification.mention": "{name} mainis sind",
-  "notification.poll": "Küsitlus, milles osalesid, on lõppenud",
-  "notification.reblog": "{name} upitas su staatust",
+  "notification.favourite": "{name} märkis Teie staatuse lemmikuks",
+  "notification.follow": "{name} jälgib nüüd Teid",
+  "notification.follow_request": "{name} soovib Teid jälgida",
+  "notification.mention": "{name} mainis Teid",
+  "notification.own_poll": "Teie küsitlus on lõppenud",
+  "notification.poll": "Küsitlus, milles osalesite, on lõppenud",
+  "notification.reblog": "{name} upitas Teie staatust",
   "notifications.clear": "Puhasta teated",
-  "notifications.clear_confirmation": "Oled kindel, et soovid püsivalt kõik oma teated puhastada?",
+  "notifications.clear_confirmation": "Olete kindel, et soovite püsivalt kõik oma teated eemaldada?",
   "notifications.column_settings.alert": "Töölauateated",
   "notifications.column_settings.favourite": "Lemmikud:",
   "notifications.column_settings.filter_bar.advanced": "Kuva kõik kategooriad",
   "notifications.column_settings.filter_bar.category": "Kiirfiltri riba",
   "notifications.column_settings.filter_bar.show": "Kuva",
   "notifications.column_settings.follow": "Uued jälgijad:",
+  "notifications.column_settings.follow_request": "Uued jälgimistaotlused:",
   "notifications.column_settings.mention": "Mainimised:",
   "notifications.column_settings.poll": "Küsitluse tulemused:",
   "notifications.column_settings.push": "Push teated",
@@ -316,7 +327,7 @@
   "privacy.unlisted.short": "Määramata",
   "refresh": "Värskenda",
   "regeneration_indicator.label": "Laeb…",
-  "regeneration_indicator.sublabel": "Sinu kodu voog on ettevalmistamisel!",
+  "regeneration_indicator.sublabel": "Teie kodu voog on ettevalmistamisel!",
   "relative_time.days": "{number}p",
   "relative_time.hours": "{number}t",
   "relative_time.just_now": "nüüd",
@@ -325,7 +336,7 @@
   "reply_indicator.cancel": "Tühista",
   "report.forward": "Edasta kasutajale {target}",
   "report.forward_hint": "See kasutaja on teisest serverist. Kas saadan anonümiseeritud koopia sellest teatest sinna ka?",
-  "report.hint": "See teade saadetakse sinu serveri moderaatoritele. Te saate lisada selgituse selle kohta, miks selle kasutaja kohta teate esitasite, siin:",
+  "report.hint": "See teade saadetakse Teie serveri moderaatoritele. Te saate lisada selgituse selle kohta, miks selle kasutaja kohta teate esitasite, siin:",
   "report.placeholder": "Lisaks kommentaarid",
   "report.submit": "Saada",
   "report.target": "Teatamine {target} kohta",
@@ -344,6 +355,7 @@
   "status.admin_account": "Ava moderaatoriliides kasutajale @{name}",
   "status.admin_status": "Ava see staatus moderaatoriliites",
   "status.block": "Blokeeri @{name}",
+  "status.bookmark": "Järjehoidja",
   "status.cancel_reblog_private": "Äraupita",
   "status.cannot_reblog": "Seda postitust ei saa upitada",
   "status.copy": "Kopeeri link staatusesse",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} upitatud",
   "status.reblogs.empty": "Keegi pole seda tuuti veel upitanud. Kui keegi upitab, näed seda siin.",
   "status.redraft": "Kustuta & alga uuesti",
+  "status.remove_bookmark": "Eemalda järjehoidja",
   "status.reply": "Vasta",
   "status.replyAll": "Vasta lõimele",
   "status.report": "Raport @{name}",
@@ -382,7 +395,7 @@
   "status.unmute_conversation": "Ära vaigista vestlust",
   "status.unpin": "Kinnita profiililt lahti",
   "suggestions.dismiss": "Eira soovitust",
-  "suggestions.header": "Sind võib huvitada…",
+  "suggestions.header": "Teid võib huvitada…",
   "tabs_bar.federated_timeline": "Föderatiivne",
   "tabs_bar.home": "Kodu",
   "tabs_bar.local_timeline": "Kohalik",
@@ -395,14 +408,16 @@
   "time_remaining.seconds": "{number, plural, one {# sekund} other {# sekundit}} left",
   "trends.count_by_accounts": "{count} {rawCount, plural, one {inimene} other {inimesed}} talking",
   "trends.trending_now": "Praegu populaarne",
-  "ui.beforeunload": "Sinu mustand läheb kaotsi, kui lahkud Mastodonist.",
+  "ui.beforeunload": "Teie mustand läheb kaotsi, kui lahkute Mastodonist.",
   "upload_area.title": "Lohista & aseta üleslaadimiseks",
   "upload_button.label": "Lisa meedia (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_error.limit": "Faili üleslaadimise limiit ületatud.",
   "upload_error.poll": "Küsitlustes pole faili üleslaadimine lubatud.",
+  "upload_form.audio_description": "Kirjelda kuulmispuudega inimeste jaoks",
   "upload_form.description": "Kirjelda vaegnägijatele",
   "upload_form.edit": "Redigeeri",
   "upload_form.undo": "Kustuta",
+  "upload_form.video_description": "Kirjelda kuulmis- või nägemispuudega inimeste jaoks",
   "upload_modal.analyzing_picture": "Analüüsime pilti…",
   "upload_modal.apply": "Rakenda",
   "upload_modal.description_placeholder": "Kiire pruun rebane hüppab üle laisa koera",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Eelvaade ({ratio})",
   "upload_progress.label": "Laeb üles....",
   "video.close": "Sulge video",
+  "video.download": "Faili allalaadimine",
   "video.exit_fullscreen": "Välju täisekraanist",
   "video.expand": "Suurenda video",
   "video.fullscreen": "Täisekraan",
diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json
index 9e383e85a..c89cb151f 100644
--- a/app/javascript/mastodon/locales/eu.json
+++ b/app/javascript/mastodon/locales/eu.json
@@ -1,6 +1,7 @@
 {
-  "account.add_or_remove_from_list": "Gehitu edo Kendu zerrendetatik",
+  "account.add_or_remove_from_list": "Gehitu edo kendu zerrendetatik",
   "account.badges.bot": "Bot-a",
+  "account.badges.group": "Taldea",
   "account.block": "Blokeatu @{name}",
   "account.block_domain": "Ezkutatu {domain} domeinuko guztia",
   "account.blocked": "Blokeatuta",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Abiadura mugatua",
   "alert.unexpected.message": "Ustekabeko errore bat gertatu da.",
   "alert.unexpected.title": "Ene!",
+  "announcement.announcement": "Iragarpena",
   "autosuggest_hashtag.per_week": "{count} asteko",
   "boost_modal.combo": "{combo} sakatu dezakezu hurrengoan hau saltatzeko",
   "bundle_column_error.body": "Zerbait okerra gertatu da osagai hau kargatzean.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Zerbait okerra gertatu da osagai hau kargatzean.",
   "bundle_modal_error.retry": "Saiatu berriro",
   "column.blocks": "Blokeatutako erabiltzaileak",
+  "column.bookmarks": "Laster-markak",
   "column.community": "Denbora-lerro lokala",
   "column.direct": "Mezu zuzenak",
   "column.directory": "Arakatu profilak",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Inkestaren iraupena",
   "compose_form.poll.option_placeholder": "{number}. aukera",
   "compose_form.poll.remove_option": "Kendu aukera hau",
+  "compose_form.poll.switch_to_multiple": "Aldatu inkesta hainbat aukera onartzeko",
+  "compose_form.poll.switch_to_single": "Aldatu inkesta aukera bakarra onartzeko",
   "compose_form.publish": "Toot",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Markatu multimedia hunkigarri gisa",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Ez dago toot-ik hemen!",
   "empty_column.account_unavailable": "Profila ez dago eskuragarri",
   "empty_column.blocks": "Ez duzu erabiltzailerik blokeatu oraindik.",
+  "empty_column.bookmarked_statuses": "Oraindik ez dituzu toot 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.domain_blocks": "Ez dago ezkutatutako domeinurik oraindik.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "mutututako erabiltzaileen zerrenda irekitzeko",
   "keyboard_shortcuts.my_profile": "zure profila irekitzeko",
   "keyboard_shortcuts.notifications": "jakinarazpenen zutabea irekitzeko",
+  "keyboard_shortcuts.open_media": "media zabaltzeko",
   "keyboard_shortcuts.pinned": "finkatutako toot-en zerrenda irekitzeko",
   "keyboard_shortcuts.profile": "egilearen profila irekitzeko",
   "keyboard_shortcuts.reply": "erantzutea",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Ezkutatu erabiltzaile honen jakinarazpenak?",
   "navigation_bar.apps": "Mugikorrerako aplikazioak",
   "navigation_bar.blocks": "Blokeatutako erabiltzaileak",
+  "navigation_bar.bookmarks": "Laster-markak",
   "navigation_bar.community_timeline": "Denbora-lerro lokala",
   "navigation_bar.compose": "Idatzi toot berria",
   "navigation_bar.direct": "Mezu zuzenak",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Segurtasuna",
   "notification.favourite": "{name}(e)k zure mezua gogoko du",
   "notification.follow": "{name}(e)k jarraitzen zaitu",
+  "notification.follow_request": "{name}(e)k zu jarraitzeko eskaera egin du",
   "notification.mention": "{name}(e)k aipatu zaitu",
+  "notification.own_poll": "Zure inkesta amaitu da",
   "notification.poll": "Zuk erantzun duzun inkesta bat bukatu da",
   "notification.reblog": "{name}(e)k bultzada eman dio zure mezuari",
   "notifications.clear": "Garbitu jakinarazpenak",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Iragazki azkarraren barra",
   "notifications.column_settings.filter_bar.show": "Erakutsi",
   "notifications.column_settings.follow": "Jarraitzaile berriak:",
+  "notifications.column_settings.follow_request": "Jarraitzeko eskaera berriak:",
   "notifications.column_settings.mention": "Aipamenak:",
   "notifications.column_settings.poll": "Inkestaren emaitzak:",
   "notifications.column_settings.push": "Push jakinarazpenak",
@@ -318,7 +329,7 @@
   "regeneration_indicator.label": "Kargatzen…",
   "regeneration_indicator.sublabel": "Zure hasiera-jarioa prestatzen ari da!",
   "relative_time.days": "{number}e",
-  "relative_time.hours": "{number}o",
+  "relative_time.hours": "{number}h",
   "relative_time.just_now": "orain",
   "relative_time.minutes": "{number}m",
   "relative_time.seconds": "{number}s",
@@ -340,10 +351,11 @@
   "search_results.hashtags": "Traolak",
   "search_results.statuses": "Toot-ak",
   "search_results.statuses_fts_disabled": "Mastodon zerbitzari honek ez du Toot-en edukiaren bilaketa gaitu.",
-  "search_results.total": "{count, number} {count, plural, one {emaitza} other {emaitzak}}",
+  "search_results.total": "{count, number} {count, plural, one {emaitza} other {emaitza}}",
   "status.admin_account": "Ireki @{name} erabiltzailearen moderazio interfazea",
   "status.admin_status": "Ireki mezu hau moderazio interfazean",
   "status.block": "Blokeatu @{name}",
+  "status.bookmark": "Laster-marka",
   "status.cancel_reblog_private": "Kendu bultzada",
   "status.cannot_reblog": "Mezu honi ezin zaio bultzada eman",
   "status.copy": "Kopiatu mezuaren esteka",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name}(r)en bultzada",
   "status.reblogs.empty": "Ez dio inork bultzada eman toot honi oraindik. Inork egiten duenean, hemen agertuko dira.",
   "status.redraft": "Ezabatu eta berridatzi",
+  "status.remove_bookmark": "Kendu laster-marka",
   "status.reply": "Erantzun",
   "status.replyAll": "Erantzun harian",
   "status.report": "Salatu @{name}",
@@ -393,25 +406,28 @@
   "time_remaining.minutes": "{number, plural, one {minutu #} other {# minutu}} amaitzeko",
   "time_remaining.moments": "Amaitzekotan",
   "time_remaining.seconds": "{number, plural, one {segundo #} other {# segundo}} amaitzeko",
-  "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} hitz egiten",
+  "trends.count_by_accounts": "{count} {rawCount, plural, one {pertsona} other {pertsona}} hitz egiten",
   "trends.trending_now": "Joera orain",
   "ui.beforeunload": "Zure zirriborroa galduko da Mastodon uzten baduzu.",
   "upload_area.title": "Arrastatu eta jaregin igotzeko",
   "upload_button.label": "Gehitu multimedia  (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_error.limit": "Fitxategi igoera muga gaindituta.",
   "upload_error.poll": "Ez da inkestetan fitxategiak igotzea onartzen.",
+  "upload_form.audio_description": "Deskribatu entzumen galera duten pertsonentzat",
   "upload_form.description": "Deskribatu ikusmen arazoak dituztenentzat",
   "upload_form.edit": "Editatu",
   "upload_form.undo": "Ezabatu",
+  "upload_form.video_description": "Deskribatu entzumen galera edo ikusmen urritasuna duten pertsonentzat",
   "upload_modal.analyzing_picture": "Irudia aztertzen…",
   "upload_modal.apply": "Aplikatu",
-  "upload_modal.description_placeholder": "Azeri marroi azkar batek txakur alferraren gainetik salto egiten du",
+  "upload_modal.description_placeholder": "Vaudeville itxurako filmean yogi ñaño bat jipoitzen dute Quebec-en whiski truk",
   "upload_modal.detect_text": "Antzeman testua iruditik",
   "upload_modal.edit_media": "Editatu media",
   "upload_modal.hint": "Sakatu eta jaregin aurrebistako zirkulua iruditxoetan beti ikusgai egongo den puntu fokala hautatzeko.",
   "upload_modal.preview_label": "Aurreikusi ({ratio})",
   "upload_progress.label": "Igotzen...",
   "video.close": "Itxi bideoa",
+  "video.download": "Deskargatu fitxategia",
   "video.exit_fullscreen": "Irten pantaila osotik",
   "video.expand": "Hedatu bideoa",
   "video.fullscreen": "Pantaila osoa",
diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json
index b651edd32..3bb2f0ee8 100644
--- a/app/javascript/mastodon/locales/fa.json
+++ b/app/javascript/mastodon/locales/fa.json
@@ -1,47 +1,49 @@
 {
-  "account.add_or_remove_from_list": "افزودن یا برداشتن از فهرست",
+  "account.add_or_remove_from_list": "افزودن یا برداشتن از فهرست‌ها",
   "account.badges.bot": "ربات",
+  "account.badges.group": "گروه",
   "account.block": "مسدودسازی @{name}",
-  "account.block_domain": "پنهان‌سازی همه چیز از سرور {domain}",
-  "account.blocked": "مسدود شده",
+  "account.block_domain": "نهفتن همه چیز از {domain}",
+  "account.blocked": "مسدود",
   "account.cancel_follow_request": "لغو درخواست پیگیری",
-  "account.direct": "پیغام خصوصی به @{name}",
-  "account.domain_blocked": "دامین پنهان‌شده",
+  "account.direct": "پیام خصوصی به @{name}",
+  "account.domain_blocked": "دامنهٔ نهفته",
   "account.edit_profile": "ویرایش نمایه",
-  "account.endorse": "نمایش در نمایه",
+  "account.endorse": "معرّفی در نمایه",
   "account.follow": "پی بگیرید",
-  "account.followers": "پیگیران",
-  "account.followers.empty": "هنوز هیچ کسی پیگیر این کاربر نیست.",
+  "account.followers": "پی‌گیران",
+  "account.followers.empty": "هنوز کسی پیگیر این کاربر نیست.",
   "account.follows": "پی می‌گیرد",
-  "account.follows.empty": "این کاربر هنوز هیچ کسی را پی نمی‌گیرد.",
+  "account.follows.empty": "این کاربر هنوز پیگیر کسی نیست.",
   "account.follows_you": "پیگیر شماست",
-  "account.hide_reblogs": "پنهان کردن بازبوق‌های @{name}",
+  "account.hide_reblogs": "نهفتن بازبوق‌های @{name}",
   "account.last_status": "آخرین فعالیت",
-  "account.link_verified_on": "مالکیت این نشانی در تاریخ {date} بررسی شد",
-  "account.locked_info": "این حساب خصوصی است. صاحب این حساب تصمیم می‌گیرد که چه کسی می‌تواند پیگیرش باشد.",
-  "account.media": "عکس و ویدیو",
+  "account.link_verified_on": "مالکیت این پیوند در {date} بررسی شد",
+  "account.locked_info": "این حساب خصوصی است. صاحبش تصمیم می‌گیرد که چه کسی بتواند پیگیرش باشد.",
+  "account.media": "رسانه",
   "account.mention": "نام‌بردن از @{name}",
-  "account.moved_to": "{name} منتقل شده است به:",
-  "account.mute": "بی‌صدا کردن @{name}",
-  "account.mute_notifications": "بی‌صداکردن اعلان‌ها از طرف @{name}",
-  "account.muted": "بی‌صداشده",
+  "account.moved_to": "{name} منتقل شده به:",
+  "account.mute": "خموشی @{name}",
+  "account.mute_notifications": "خموشی اعلان‌ها از @{name}",
+  "account.muted": "خموش",
   "account.never_active": "هرگز",
   "account.posts": "نوشته‌ها",
   "account.posts_with_replies": "نوشته‌ها و پاسخ‌ها",
   "account.report": "گزارش @{name}",
-  "account.requested": "در انتظار پذیرش",
+  "account.requested": "منتظر پذیرش. برای لغو درخواست پی‌گیری کلیک کنید",
   "account.share": "هم‌رسانی نمایهٔ @{name}",
-  "account.show_reblogs": "نشان‌دادن بازبوق‌های  @{name}",
+  "account.show_reblogs": "نمایش بازبوق‌های @{name}",
   "account.unblock": "رفع انسداد @{name}",
-  "account.unblock_domain": "رفع پنهان‌سازی از {domain}",
-  "account.unendorse": "نهفتن از نمایه",
+  "account.unblock_domain": "رفع نهفتن {domain}",
+  "account.unendorse": "معرّفی نکردن در نمایه",
   "account.unfollow": "پایان پیگیری",
-  "account.unmute": "باصدا کردن @{name}",
-  "account.unmute_notifications": "باصداکردن اعلان‌ها از طرف @{name}",
-  "alert.rate_limited.message": "لطفاً پس از {retry_time, time, medium} دوباره تلاش کنید.",
+  "account.unmute": "رفع خموشی @{name}",
+  "account.unmute_notifications": "رفع خموشی اعلان‌ها از @{name}",
+  "alert.rate_limited.message": "لطفاً پس از {retry_time, time, medium} دوباره بیازمایید.",
   "alert.rate_limited.title": "محدودیت تعداد",
-  "alert.unexpected.message": "خطای پیش‌بینی‌نشده‌ای رخ داد.",
+  "alert.unexpected.message": "خطایی غیرمنتظره رخ داد.",
   "alert.unexpected.title": "ای وای!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} در هفته",
   "boost_modal.combo": "دکمهٔ {combo} را بزنید تا دیگر این را نبینید",
   "bundle_column_error.body": "هنگام بازکردن این بخش خطایی رخ داد.",
@@ -50,18 +52,19 @@
   "bundle_modal_error.close": "بستن",
   "bundle_modal_error.message": "هنگام بازکردن این بخش خطایی رخ داد.",
   "bundle_modal_error.retry": "تلاش دوباره",
-  "column.blocks": "کاربران مسدودشده",
+  "column.blocks": "کاربران مسدود",
+  "column.bookmarks": "نشانک‌ها",
   "column.community": "نوشته‌های محلی",
-  "column.direct": "پیغام‌های خصوصی",
+  "column.direct": "پیام‌های خصوصی",
   "column.directory": "مرور نمایه‌ها",
-  "column.domain_blocks": "دامین‌های پنهان‌شده",
+  "column.domain_blocks": "دامنه‌های نهفته",
   "column.favourites": "پسندیده‌ها",
   "column.follow_requests": "درخواست‌های پیگیری",
   "column.home": "خانه",
   "column.lists": "فهرست‌ها",
-  "column.mutes": "کاربران بی‌صداشده",
+  "column.mutes": "کاربران خموش",
   "column.notifications": "اعلان‌ها",
-  "column.pins": "نوشته‌های ثابت",
+  "column.pins": "بوق‌های ثابت",
   "column.public": "نوشته‌های همه‌جا",
   "column_back_button.label": "بازگشت",
   "column_header.hide_settings": "نهفتن تنظیمات",
@@ -71,56 +74,58 @@
   "column_header.show_settings": "نمایش تنظیمات",
   "column_header.unpin": "رهاکردن",
   "column_subheading.settings": "تنظیمات",
-  "community.column_settings.media_only": "فقط عکس و ویدیو",
+  "community.column_settings.media_only": "فقط رسانه",
   "compose_form.direct_message_warning": "این بوق تنها به کاربرانی که از آن‌ها نام برده شده فرستاده خواهد شد.",
   "compose_form.direct_message_warning_learn_more": "بیشتر بدانید",
-  "compose_form.hashtag_warning": "از آن‌جا که این بوق فهرست‌نشده است، در نتایج جستجوی هشتگ‌ها پیدا نخواهد شد. تنها بوق‌های عمومی را می‌توان با جستجوی هشتگ پیدا کرد.",
-  "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": "افزودن گزینه",
   "compose_form.poll.duration": "مدت نظرسنجی",
   "compose_form.poll.option_placeholder": "گزینهٔ {number}",
-  "compose_form.poll.remove_option": "حذف این گزینه",
+  "compose_form.poll.remove_option": "برداشتن این گزینه",
+  "compose_form.poll.switch_to_multiple": "تبدیل به نظرسنجی چندگزینه‌ای",
+  "compose_form.poll.switch_to_single": "تبدیل به نظرسنجی تک‌گزینه‌ای",
   "compose_form.publish": "بوق",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "علامت‌گذاری به عنوان حساس",
-  "compose_form.sensitive.marked": "این تصویر به عنوان حساس علامت‌گذاری شده",
-  "compose_form.sensitive.unmarked": "این تصویر به عنوان حساس علامت‌گذاری نشده",
-  "compose_form.spoiler.marked": "نوشته پشت هشدار محتوا پنهان است",
+  "compose_form.sensitive.marked": "رسانه به عنوان حساس علامت‌گذاری شده",
+  "compose_form.sensitive.unmarked": "رسانه به عنوان حساس علامت‌گذاری نشده",
+  "compose_form.spoiler.marked": "نوشته پشت هشدار پنهان است",
   "compose_form.spoiler.unmarked": "نوشته پنهان نیست",
-  "compose_form.spoiler_placeholder": "هشدار محتوا",
+  "compose_form.spoiler_placeholder": "هشدارتان را این‌جا بنویسید",
   "confirmation_modal.cancel": "بی‌خیال",
   "confirmations.block.block_and_report": "مسدودسازی و گزارش",
   "confirmations.block.confirm": "مسدود کن",
-  "confirmations.block.message": "آیا واقعاً می‌خواهید {name} را مسدود کنید؟",
+  "confirmations.block.message": "مطمئنید که می‌خواهید {name} را مسدود کنید؟",
   "confirmations.delete.confirm": "پاک کن",
-  "confirmations.delete.message": "آیا واقعاً می‌خواهید این نوشته را پاک کنید؟",
+  "confirmations.delete.message": "آیا مطمئنید که می‌خواهید این بوق را پاک کنید؟",
   "confirmations.delete_list.confirm": "پاک کن",
-  "confirmations.delete_list.message": "آیا واقعاً می‌خواهید این فهرست را برای همیشه پاک کنید؟",
-  "confirmations.domain_block.confirm": "پنهان‌سازی کل دامین",
-  "confirmations.domain_block.message": "آیا جدی جدی می‌خواهید کل دامین {domain} را مسدود کنید؟ بیشتر وقت‌ها مسدودکردن یا بی‌صداکردن چند حساب کاربری خاص کافی است و توصیه می‌شود. پس از این کار شما هیچ نوشته‌ای را از این دامین در فهرست نوشته‌های عمومی یا اعلان‌هایتان نخواهید دید. پیگیران شما از این دامین هم حذف خواهد شد.",
+  "confirmations.delete_list.message": "مطمئنید می‌خواهید این فهرست را برای همیشه پاک کنید؟",
+  "confirmations.domain_block.confirm": "نهفتن تمام دامنه",
+  "confirmations.domain_block.message": "آیا جدی جدی می‌خواهید تمام دامنهٔ {domain} را مسدود کنید؟ در بیشتر موارد مسدودسازی یا خموشیدن چند حساب خاص کافی است و توصیه می‌شود. پس از این کار شما هیچ نوشته‌ای را از این دامنه در فهرست نوشته‌های عمومی یا اعلان‌هایتان نخواهید دید. پیگیرانتان از این دامنه هم حذف خواهند شد.",
   "confirmations.logout.confirm": "خروج",
-  "confirmations.logout.message": "آیا مطمئنید که می‌خواهید خارج شوید؟",
-  "confirmations.mute.confirm": "بی‌صدا کن",
-  "confirmations.mute.explanation": "این کار نوشته‌های آن‌ها و نوشته‌هایی را که از آن‌ها نام برده پنهان می‌کند، ولی آن‌ها همچنان اجازه دارند نوشته‌های شما را ببینند و شما را پی بگیرند.",
-  "confirmations.mute.message": "آیا واقعاً می‌خواهید {name} را بی‌صدا کنید؟",
+  "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": "لغو پیگیری",
-  "confirmations.unfollow.message": "آیا واقعاً می‌خواهید به پیگیری از {name} پایان دهید؟",
+  "confirmations.reply.message": "اگر الان پاسخ دهید، چیزی که در حال نوشتنش بودید پاک خواهد شد. می‌خواهید ادامه دهید؟",
+  "confirmations.unfollow.confirm": "پایان پیگیری",
+  "confirmations.unfollow.message": "مطمئنید که می‌خواهید به پیگیری از {name} پایان دهید؟",
   "conversation.delete": "حذف گفتگو",
   "conversation.mark_as_read": "علامت‌گذاری به عنوان خوانده شده",
   "conversation.open": "دیدن گفتگو",
   "conversation.with": "با {names}",
-  "directory.federated": "از سرورهای همسایه",
+  "directory.federated": "از کارسازهای شناخته‌شده",
   "directory.local": "تنها از {domain}",
   "directory.new_arrivals": "تازه‌واردان",
   "directory.recently_active": "کاربران فعال اخیر",
-  "embed.instructions": "برای جاگذاری این نوشته در سایت خودتان، کد زیر را کپی کنید.",
-  "embed.preview": "نوشتهٔ جاگذاری‌شده این گونه به نظر خواهد رسید:",
+  "embed.instructions": "برای جاگذاری این بوق در سایت خودتان، کد زیر را کپی کنید.",
+  "embed.preview": "این گونه دیده خواهد شد:",
   "emoji_button.activity": "فعالیت",
   "emoji_button.custom": "سفارشی",
   "emoji_button.flags": "پرچم‌ها",
@@ -130,42 +135,43 @@
   "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": "نمادها",
   "emoji_button.travel": "سفر و مکان",
   "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.domain_blocks": "هنوز هیچ دامینی پنهان نشده است.",
+  "empty_column.direct": "هنوز هیچ پیام مستقیمی ندارید. هروقت چنین پیامی بگیرید یا بفرستید این‌جا نمایش خواهد یافت.",
+  "empty_column.domain_blocks": "هنوز هیچ دامنه‌ای پنهان نشده است.",
   "empty_column.favourited_statuses": "شما هنوز هیچ بوقی را نپسندیده‌اید. وقتی بوقی را بپسندید، این‌جا نمایش خواهد یافت.",
   "empty_column.favourites": "هنوز هیچ کسی این بوق را نپسندیده است. وقتی کسی آن را بپسندد، نامش این‌جا نمایش خواهد یافت.",
   "empty_column.follow_requests": "شما هنوز هیچ درخواست پیگیری‌ای ندارید. وقتی چنین درخواستی بگیرید، این‌جا نمایش خواهد یافت.",
-  "empty_column.hashtag": "هنوز هیچ چیزی با این برچسب (هشتگ) نیست.",
-  "empty_column.home": "شما هنوز پیگیر کسی نیستید. {public} را ببینید یا چیزی را جستجو کنید تا کاربران دیگر را ببینید.",
+  "empty_column.hashtag": "هنوز هیچ چیزی در این برچسب نیست.",
+  "empty_column.home": "فهرست خانگی شما خالی است! {public} را ببینید یا چیزی را جستجو کنید تا کاربران دیگر را ببینید.",
   "empty_column.home.public_timeline": "فهرست نوشته‌های همه‌جا",
-  "empty_column.list": "در این فهرست هنوز چیزی نیست. وقتی اعضای این فهرست چیزی بنویسند، این‌جا ظاهر خواهد شد.",
-  "empty_column.lists": "شما هنوز هیچ فهرستی ندارید. اگر فهرستی بسازید، این‌جا نمایش خواهد یافت.",
-  "empty_column.mutes": "شما هنوز هیچ کاربری را بی‌صدا نکرده‌اید.",
-  "empty_column.notifications": "هنوز هیچ اعلانی ندارید. به نوشته‌های دیگران واکنش نشان دهید تا گفتگو آغاز شود.",
-  "empty_column.public": "این‌جا هنوز چیزی نیست! خودتان چیزی بنویسید یا کاربران سرورهای دیگر را پی بگیرید تا این‌جا پر شود",
+  "empty_column.list": "در این فهرست هنوز چیزی نیست. وقتی اعضای این فهرست چیزی بفرستند، این‌جا ظاهر خواهد شد.",
+  "empty_column.lists": "هنوز هیچ فهرستی ندارید. هنگامی که فهرستی بسازید، این‌جا دیده خواهد شد.",
+  "empty_column.mutes": "هنوز هیچ کاربری را خموش نکرده‌اید.",
+  "empty_column.notifications": "هنوز هیچ اعلانی ندارید. به دیگران واکنش نشان دهید تا گفتگو آغاز شود.",
+  "empty_column.public": "این‌جا هنوز چیزی نیست! خودتان چیزی بنویسید یا کاربران کارسازهای دیگر را پی بگیرید تا این‌جا پر شود",
   "error.unexpected_crash.explanation": "به خاطر اشکالی در کدهای ما یا ناسازگاری با مرورگر شما، این صفحه به درستی نمایش نیافت.",
-  "error.unexpected_crash.next_steps": "لطفاً صفحه را دوباره باز کنید. اگر آن هم کمکی نکرد، همچنان شاید بتوانید با ماستدون از راه یکی از اپ‌های آن کار کنید.",
-  "errors.unexpected_crash.copy_stacktrace": "کپی جزئیات اشکال",
-  "errors.unexpected_crash.report_issue": "گزارش اشکال",
+  "error.unexpected_crash.next_steps": "لطفاً صفحه را دوباره باز کنید. اگر کمکی نکرد، شاید همچنان بتوانید با ماستدون از راه یک مرورگر دیگر یا با یکی از اپ‌های آن کار کنید.",
+  "errors.unexpected_crash.copy_stacktrace": "رونوشت از جزئیات اشکال",
+  "errors.unexpected_crash.report_issue": "گزارش مشکل",
   "follow_request.authorize": "اجازه دهید",
-  "follow_request.reject": "اجازه ندهید",
-  "getting_started.developers": "برای برنامه‌نویسان",
+  "follow_request.reject": "رد کنید",
+  "getting_started.developers": "توسعه‌دهندگان",
   "getting_started.directory": "فهرست گزیدهٔ کاربران",
-  "getting_started.documentation": "راهنما",
+  "getting_started.documentation": "مستندات",
   "getting_started.heading": "آغاز کنید",
-  "getting_started.invite": "دعوت از دوستان",
-  "getting_started.open_source_notice": "ماستدون یک نرم‌افزار آزاد است. می‌توانید در ساخت آن مشارکت کنید یا مشکلاتش را در {github} گزارش دهید.",
-  "getting_started.security": "امنیت",
-  "getting_started.terms": "شرایط استفاده",
+  "getting_started.invite": "دعوت از دیگران",
+  "getting_started.open_source_notice": "ماستودون نرم‌افزاری آزاد است. می‌توانید روی {github} در آن مشارکت کرده یا مشکلاتش را گزارش دهید.",
+  "getting_started.security": "تنظیمات حساب",
+  "getting_started.terms": "شرایط خدمات",
   "hashtag.column_header.tag_mode.all": "و {additional}",
   "hashtag.column_header.tag_mode.any": "یا {additional}",
   "hashtag.column_header.tag_mode.none": "بدون {additional}",
@@ -174,41 +180,41 @@
   "hashtag.column_settings.tag_mode.all": "همهٔ این‌ها",
   "hashtag.column_settings.tag_mode.any": "هرکدام از این‌ها",
   "hashtag.column_settings.tag_mode.none": "هیچ‌کدام از این‌ها",
-  "hashtag.column_settings.tag_toggle": "برچسب‌های بیشتری به این ستون بیفزایید",
-  "home.column_settings.basic": "اصلی",
+  "hashtag.column_settings.tag_toggle": "افزودن برچسب‌هایی بیشتر به این ستون",
+  "home.column_settings.basic": "پایه‌ای",
   "home.column_settings.show_reblogs": "نمایش بازبوق‌ها",
   "home.column_settings.show_replies": "نمایش پاسخ‌ها",
   "intervals.full.days": "{number, plural, one {# روز} other {# روز}}",
   "intervals.full.hours": "{number, plural, one {# ساعت} other {# ساعت}}",
   "intervals.full.minutes": "{number, plural, one {# دقیقه} other {# دقیقه}}",
   "introduction.federation.action": "بعدی",
-  "introduction.federation.federated.headline": "فهرست همهٔ سرورها",
-  "introduction.federation.federated.text": "نوشته‌های عمومی سرورهای دیگر در این فهرست نمایش می‌یابند.",
+  "introduction.federation.federated.headline": "همگانی",
+  "introduction.federation.federated.text": "نوشته‌های عمومی کارسازهای دیگر در این فهرست نمایش می‌یابند.",
   "introduction.federation.home.headline": "خانه",
-  "introduction.federation.home.text": "نوشته‌های کسانی که شما آن‌ها را پی می‌گیرید این‌جا نمایش می‌یابند. شما می‌توانید هر کسی را از هر سروری پی بگیرید!",
-  "introduction.federation.local.headline": "محلی",
-  "introduction.federation.local.text": "نوشته‌های عمومی کسانی که روی سرور شما هستند در فهرست نوشته‌های محلی نمایش می‌یابند.",
+  "introduction.federation.home.text": "فرسته‌های کسانی که شما آن‌ها را پی می‌گیرید این‌جا نمایش می‌یابند. شما می‌توانید هر کسی را روی هر کارسازی پی بگیرید!",
+  "introduction.federation.local.headline": "محلّی",
+  "introduction.federation.local.text": "فرسته‌های عمومی کسانی که روی کارساز شما هستند در فهرست نوشته‌های محلی نمایش می‌یابند.",
   "introduction.interactions.action": "پایان خودآموز!",
   "introduction.interactions.favourite.headline": "پسندیدن",
   "introduction.interactions.favourite.text": "با پسندیدن یک بوق، شما آن را برای آینده ذخیره می‌کنید و به نویسنده می‌گویید که از بوقش خوشتان آمده.",
   "introduction.interactions.reblog.headline": "بازبوقیدن",
   "introduction.interactions.reblog.text": "اگر بخواهید نوشته‌ای را با پیگیران خودتان به اشتراک بگذارید، آن را بازمی‌بوقید.",
   "introduction.interactions.reply.headline": "پاسخ",
-  "introduction.interactions.reply.text": "شما می‌توانید به بوق‌های خودتان و دیگران پاسخ دهید، تا همهٔ این بوق‌ها به شکل رشتهٔ به‌هم‌پیوسته‌ای در یک گفتگو درآیند.",
+  "introduction.interactions.reply.text": "می‌توانید به بوق‌های خودتان و دیگران پاسخ دهید، تا در یک گفتگو به هم زنجیر شوند.",
   "introduction.welcome.action": "بزن بریم!",
   "introduction.welcome.headline": "نخستین گام‌ها",
-  "introduction.welcome.text": "به دنیای شبکه‌های اجتماعی غیرمتمرکز خوش آمدید! به زودی می‌توانید نوشته‌های خودتان را منتشر کنید و با دوستانتان که روی سرورهای مختلفی هستند حرف بزنید. ولی این سرور، {domain}، با بقیه فرق دارد زیرا حساب شما روی آن ساخته شده است، پس نامش را یادتان نگه دارید.",
+  "introduction.welcome.text": "به دنیای شبکه‌های اجتماعی غیرمتمرکز خوش آمدید! به زودی می‌توانید نوشته‌هایتان را منتشر کرده و با دوستانتان روی دامنهٔ وسیعی از کارسازها حرف بزنید. ولی این کارساز، {domain}، با بقیه فرق دارد، چرا که میزبان نمایهٔ شماست، پس نامش را به خاطر بسپارید.",
   "keyboard_shortcuts.back": "برای بازگشت",
-  "keyboard_shortcuts.blocked": "برای گشودن کاربران بی‌صداشده",
+  "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.down": "برای پایین‌رفتن در فهرست",
+  "keyboard_shortcuts.down": "برای پایین رفتن در فهرست",
   "keyboard_shortcuts.enter": "برای گشودن نوشته",
   "keyboard_shortcuts.favourite": "برای پسندیدن",
-  "keyboard_shortcuts.favourites": "برای گشودن پیغام‌های پسندیده‌شده",
+  "keyboard_shortcuts.favourites": "برای گشودن فهرست پسندیده‌ها",
   "keyboard_shortcuts.federated": "برای گشودن فهرست نوشته‌های همه‌جا",
   "keyboard_shortcuts.heading": "میان‌برهای صفحه‌کلید",
   "keyboard_shortcuts.home": "برای گشودن ستون اصلی پیگیری‌ها",
@@ -216,31 +222,32 @@
   "keyboard_shortcuts.legend": "برای نمایش این راهنما",
   "keyboard_shortcuts.local": "برای گشودن فهرست نوشته‌های محلی",
   "keyboard_shortcuts.mention": "برای نام‌بردن از نویسنده",
-  "keyboard_shortcuts.muted": "برای گشودن فهرست کاربران بی‌صداشده",
-  "keyboard_shortcuts.my_profile": "برای گشودن صفحهٔ نمایهٔ شما",
+  "keyboard_shortcuts.muted": "برای گشودن فهرست کاربران خموش",
+  "keyboard_shortcuts.my_profile": "برای گشودن نمایه‌تان",
   "keyboard_shortcuts.notifications": "برای گشودن ستون اعلان‌ها",
-  "keyboard_shortcuts.pinned": "برای گشودن فهرست نوشته‌‌های ثابت",
-  "keyboard_shortcuts.profile": "گشودن نمایهٔ نویسنده",
-  "keyboard_shortcuts.reply": "برای پاسخ‌دادن",
+  "keyboard_shortcuts.open_media": "برای باز کردن رسانه",
+  "keyboard_shortcuts.pinned": "برای گشودن فهرست بوق‌های ثابت",
+  "keyboard_shortcuts.profile": "برای گشودن نمایهٔ نویسنده",
+  "keyboard_shortcuts.reply": "برای پاسخ",
   "keyboard_shortcuts.requests": "برای گشودن فهرست درخواست‌های پیگیری",
-  "keyboard_shortcuts.search": "برای فعال‌کردن جستجو",
+  "keyboard_shortcuts.search": "برای تمرکز روی جستجو",
   "keyboard_shortcuts.start": "برای گشودن ستون «آغاز کنید»",
   "keyboard_shortcuts.toggle_hidden": "برای نمایش/نهفتن نوشتهٔ پشت هشدار محتوا",
-  "keyboard_shortcuts.toggle_sensitivity": "برای نمایش/نهفتن عکس و ویدیو",
+  "keyboard_shortcuts.toggle_sensitivity": "برای نمایش/نهفتن رسانه",
   "keyboard_shortcuts.toot": "برای آغاز یک بوق تازه",
-  "keyboard_shortcuts.unfocus": "برای برداشتن توجه از نوشتن/جستجو",
+  "keyboard_shortcuts.unfocus": "برای برداشتن تمرکز از نوشتن/جستجو",
   "keyboard_shortcuts.up": "برای بالا رفتن در فهرست",
   "lightbox.close": "بستن",
   "lightbox.next": "بعدی",
   "lightbox.previous": "قبلی",
   "lightbox.view_context": "نمایش گفتگو",
   "lists.account.add": "افزودن به فهرست",
-  "lists.account.remove": "پاک‌کردن از فهرست",
+  "lists.account.remove": "برداشتن از فهرست",
   "lists.delete": "حذف فهرست",
   "lists.edit": "ویرایش فهرست",
   "lists.edit.submit": "تغییر عنوان",
   "lists.new.create": "افزودن فهرست",
-  "lists.new.title_placeholder": "نام فهرست تازه",
+  "lists.new.title_placeholder": "عنوان فهرست تازه",
   "lists.search": "بین کسانی که پی می‌گیرید بگردید",
   "lists.subheading": "فهرست‌های شما",
   "load_pending": "{count, plural, one {# مورد تازه} other {# مورد تازه}}",
@@ -251,39 +258,43 @@
   "mute_modal.hide_notifications": "اعلان‌های این کاربر پنهان شود؟",
   "navigation_bar.apps": "اپ‌های موبایل",
   "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.domain_blocks": "دامنه‌های نهفته",
   "navigation_bar.edit_profile": "ویرایش نمایه",
   "navigation_bar.favourites": "پسندیده‌ها",
-  "navigation_bar.filters": "واژگان بی‌صداشده",
+  "navigation_bar.filters": "واژگان خموش",
   "navigation_bar.follow_requests": "درخواست‌های پیگیری",
   "navigation_bar.follows_and_followers": "پیگیری‌ها و پیگیران",
-  "navigation_bar.info": "دربارهٔ این سرور",
-  "navigation_bar.keyboard_shortcuts": "میان‌برهای صفحه‌کلید",
+  "navigation_bar.info": "دربارهٔ این کارساز",
+  "navigation_bar.keyboard_shortcuts": "میان‌برها",
   "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.security": "امنیت",
   "notification.favourite": "‫{name}‬ نوشتهٔ شما را پسندید",
-  "notification.follow": "‫{name}‬ پیگیر شما شد",
+  "notification.follow": "‫{name}‬ پیگیرتان شد",
+  "notification.follow_request": "{name} می‌خواهد پیگیر شما باشد",
   "notification.mention": "‫{name}‬ از شما نام برد",
+  "notification.own_poll": "نظرسنجی شما به پایان رسید",
   "notification.poll": "نظرسنجی‌ای که در آن رأی دادید به پایان رسیده است",
   "notification.reblog": "‫{name}‬ نوشتهٔ شما را بازبوقید",
   "notifications.clear": "پاک‌کردن اعلان‌ها",
-  "notifications.clear_confirmation": "واقعاً می‌خواهید همهٔ اعلان‌هایتان را برای همیشه پاک کنید؟",
-  "notifications.column_settings.alert": "اعلان در کامپیوتر",
+  "notifications.clear_confirmation": "مطمئنید می‌خواهید همهٔ اعلان‌هایتان را برای همیشه پاک کنید؟",
+  "notifications.column_settings.alert": "اعلان‌های میزکار",
   "notifications.column_settings.favourite": "پسندیده‌ها:",
-  "notifications.column_settings.filter_bar.advanced": "نمایش همهٔ گروه‌ها",
-  "notifications.column_settings.filter_bar.category": "فیلتر سریع",
+  "notifications.column_settings.filter_bar.advanced": "نمایش همهٔ دسته‌ها",
+  "notifications.column_settings.filter_bar.category": "نوار پالایش سریع",
   "notifications.column_settings.filter_bar.show": "نمایش",
   "notifications.column_settings.follow": "پیگیران تازه:",
+  "notifications.column_settings.follow_request": "درخواست‌های جدید پی‌گیری:",
   "notifications.column_settings.mention": "نام‌بردن‌ها:",
   "notifications.column_settings.poll": "نتایج نظرسنجی:",
   "notifications.column_settings.push": "اعلان‌ها از سمت سرور",
@@ -292,9 +303,9 @@
   "notifications.column_settings.sound": "پخش صدا",
   "notifications.filter.all": "همه",
   "notifications.filter.boosts": "بازبوق‌ها",
-  "notifications.filter.favourites": "پسندیده‌ها",
+  "notifications.filter.favourites": "پسندها",
   "notifications.filter.follows": "پیگیری‌ها",
-  "notifications.filter.mentions": "گفتگوها",
+  "notifications.filter.mentions": "نام‌بردن‌ها",
   "notifications.filter.polls": "نتایج نظرسنجی",
   "notifications.group": "{count} اعلان",
   "poll.closed": "پایان‌یافته",
@@ -305,9 +316,9 @@
   "poll.voted": "شما به این گزینه رأی دادید",
   "poll_button.add_poll": "افزودن نظرسنجی",
   "poll_button.remove_poll": "حذف نظرسنجی",
-  "privacy.change": "تنظیم حریم خصوصی نوشته‌ها",
+  "privacy.change": "تنظیم محرمانگی نوشته",
   "privacy.direct.long": "تنها به کاربران نام‌برده‌شده نشان بده",
-  "privacy.direct.short": "مستقیم",
+  "privacy.direct.short": "خصوصی",
   "privacy.private.long": "تنها به پیگیران نشان بده",
   "privacy.private.short": "خصوصی",
   "privacy.public.long": "نمایش در فهرست عمومی",
@@ -324,100 +335,105 @@
   "relative_time.seconds": "{number} ثانیه",
   "reply_indicator.cancel": "لغو",
   "report.forward": "فرستادن به {target}",
-  "report.forward_hint": "این حساب در سرور دیگری ثبت شده. آیا می‌خواهید رونوشتی از این گزارش به طور ناشناس به آن‌جا هم فرستاده شود؟",
-  "report.hint": "این گزارش به مدیران سرور شما فرستاده خواهد شد. می‌توانید دلیل گزارش‌دادن این حساب را در این‌جا بنویسید:",
+  "report.forward_hint": "این حساب در کارساز دیگری ثبت شده. آیا می‌خواهید رونوشتی ناشناس از این گزارش به آن‌جا هم فرستاده شود؟",
+  "report.hint": "این گزارش به مدیران کارسازتان فرستاده خواهد شد. می‌توانید دلیل گزارش این حساب را در ادامه بنویسید:",
   "report.placeholder": "توضیح اضافه",
   "report.submit": "بفرست",
-  "report.target": "گزارش‌دادن",
+  "report.target": "در حال گزارش {target}",
   "search.placeholder": "جستجو",
   "search_popout.search_format": "راهنمای جستجوی پیشرفته",
-  "search_popout.tips.full_text": "جستجوی متنی ساده می‌تواند بوق‌هایی که شما نوشته‌اید، پسندیده‌اید، بازبوقیده‌اید، یا در آن‌ها از شما نام برده شده است را پیدا کند. همچنین نام‌های کاربری، نام نمایش‌یافته، و هشتگ‌ها را هم شامل می‌شود.",
+  "search_popout.tips.full_text": "جست‌وجوی متنی ساده وضعیت‌هایی که که نوشته، برگزیده، تقویت‌کرده یا در آن‌ها اشاره‌شده‌اید را به اضافهٔ نام‌های کاربری، نام‌های نمایشی و برچسب‌های مطابق برمی‌گرداند.",
   "search_popout.tips.hashtag": "برچسب",
-  "search_popout.tips.status": "نوشته",
-  "search_popout.tips.text": "جستجوی متنی ساده برای نام‌ها، نام‌های کاربری، و برچسب‌ها",
+  "search_popout.tips.status": "وضعیت",
+  "search_popout.tips.text": "جست‌وجوی متنی ساده، نام‌های نمایشی، نام‌های کاربری، و برچسب‌ها را برمی‌گرداند",
   "search_popout.tips.user": "کاربر",
   "search_results.accounts": "افراد",
   "search_results.hashtags": "برچسب‌ها",
   "search_results.statuses": "بوق‌ها",
-  "search_results.statuses_fts_disabled": "جستجوی محتوای بوق‌ها در این سرور ماستدون ممکن نیست.",
+  "search_results.statuses_fts_disabled": "جست‌وجوی بوق‌ها بر اساس محتوایشان روی این کارساز ماستودون به کار نیفتاده است.",
   "search_results.total": "{count, number} {count, plural, one {نتیجه} other {نتیجه}}",
-  "status.admin_account": "محیط مدیریت مربوط به @{name} را باز کن",
-  "status.admin_status": "این نوشته را در محیط مدیریت باز کن",
-  "status.block": "مسدودسازی @{name}",
-  "status.cancel_reblog_private": "حذف بازبوق",
-  "status.cannot_reblog": "این نوشته را نمی‌شود بازبوقید",
-  "status.copy": "رونوشت‌برداری از نشانی این نوشته",
-  "status.delete": "پاک‌کردن",
-  "status.detailed_status": "نمایش کامل گفتگو",
-  "status.direct": "پیغام مستقیم به @{name}",
+  "status.admin_account": "گشودن واسط مدیریت برای @{name}",
+  "status.admin_status": "گشودن این وضعیت در واسط مدیریت",
+  "status.block": "انسداد @{name}",
+  "status.bookmark": "نشانک",
+  "status.cancel_reblog_private": "ناتقویت",
+  "status.cannot_reblog": "این نوشته نمی‌تواند تقویت شود",
+  "status.copy": "رونوشت از پیوند به وضعیت",
+  "status.delete": "حذف",
+  "status.detailed_status": "نمای گفت‌وگوی باجزییات",
+  "status.direct": "پیام خصوصی به @{name}",
   "status.embed": "جاگذاری",
-  "status.favourite": "پسندیدن",
-  "status.filtered": "فیلترشده",
-  "status.load_more": "بیشتر نشان بده",
-  "status.media_hidden": "تصویر پنهان شده",
-  "status.mention": "نام‌بردن از @{name}",
+  "status.favourite": "برگزیدن",
+  "status.filtered": "پالوده",
+  "status.load_more": "بار کردن بیشتر",
+  "status.media_hidden": "رسانهٔ نهفته",
+  "status.mention": "اشاره به @{name}",
   "status.more": "بیشتر",
-  "status.mute": "بی‌صدا کردن @{name}",
-  "status.mute_conversation": "بی‌صداکردن گفتگو",
-  "status.open": "این نوشته را باز کن",
-  "status.pin": "نوشتهٔ ثابت نمایه",
-  "status.pinned": "بوق ثابت",
+  "status.mute": "خموشی @{name}",
+  "status.mute_conversation": "خموشی گفت‌وگو",
+  "status.open": "گسترش این وضعیت",
+  "status.pin": "سنجاق به نمایه",
+  "status.pinned": "بوق سنجاق‌شده",
   "status.read_more": "بیشتر بخوانید",
-  "status.reblog": "بازبوقیدن",
-  "status.reblog_private": "بازبوق به مخاطبان اولیه",
-  "status.reblogged_by": "‫{name}‬ بازبوقید",
-  "status.reblogs.empty": "هنوز هیچ کسی این بوق را بازنبوقیده است. وقتی کسی چنین کاری کند، این‌جا نمایش خواهد یافت.",
-  "status.redraft": "پاک‌کردن و بازنویسی",
+  "status.reblog": "تقویت",
+  "status.reblog_private": "تقویت برای مخاطبان نخستین",
+  "status.reblogged_by": "‫{name}‬ تقویت کرد",
+  "status.reblogs.empty": "هنوز کسی این بوق را تقویت نکرده است. وقتی کسی چنین کاری کند، این‌جا دیده خواهد شد.",
+  "status.redraft": "حذف و بازنویسی",
+  "status.remove_bookmark": "برداشتن نشانک",
   "status.reply": "پاسخ",
-  "status.replyAll": "به نوشته پاسخ دهید",
-  "status.report": "گزارش دادن @{name}",
+  "status.replyAll": "پاسخ به رشته",
+  "status.report": "گزارش @{name}",
   "status.sensitive_warning": "محتوای حساس",
   "status.share": "هم‌رسانی",
-  "status.show_less": "نهفتن",
-  "status.show_less_all": "نمایش کمتر همه",
-  "status.show_more": "نمایش",
-  "status.show_more_all": "نمایش بیشتر همه",
-  "status.show_thread": "نمایش گفتگو",
+  "status.show_less": "نمایش کمتر",
+  "status.show_less_all": "نمایش کمتر برای همه",
+  "status.show_more": "نمایش بیشتر",
+  "status.show_more_all": "نمایش بیشتر برای همه",
+  "status.show_thread": "نمایش رشته",
   "status.uncached_media_warning": "ناموجود",
-  "status.unmute_conversation": "باصداکردن گفتگو",
-  "status.unpin": "برداشتن نوشتهٔ ثابت نمایه",
-  "suggestions.dismiss": "پیشنهاد را نادیده بگیر",
+  "status.unmute_conversation": "رفع خموشی گفت‌وگو",
+  "status.unpin": "برداشتن سنجاق از نمایه",
+  "suggestions.dismiss": "نادیده گرفتن پیشنهاد",
   "suggestions.header": "شاید این هم برایتان جالب باشد…",
   "tabs_bar.federated_timeline": "همگانی",
   "tabs_bar.home": "خانه",
-  "tabs_bar.local_timeline": "محلی",
-  "tabs_bar.notifications": "اعلان‌ها",
-  "tabs_bar.search": "جستجو",
+  "tabs_bar.local_timeline": "محلّی",
+  "tabs_bar.notifications": "آگاهی‌ها",
+  "tabs_bar.search": "جست‌وجو",
   "time_remaining.days": "{number, plural, one {# روز} other {# روز}} باقی مانده",
   "time_remaining.hours": "{number, plural, one {# ساعت} other {# ساعت}} باقی مانده",
   "time_remaining.minutes": "{number, plural, one {# دقیقه} other {# دقیقه}} باقی مانده",
-  "time_remaining.moments": "زمان باقی‌مانده",
+  "time_remaining.moments": "لحظاتی باقی‌مانده",
   "time_remaining.seconds": "{number, plural, one {# ثانیه} other {# ثانیه}} باقی مانده",
   "trends.count_by_accounts": "{count} {rawCount, plural, one {نفر نوشته است} other {نفر نوشته‌اند}}",
   "trends.trending_now": "پرطرفدار",
-  "ui.beforeunload": "اگر از ماستدون خارج شوید پیش‌نویس شما پاک خواهد شد.",
+  "ui.beforeunload": "در صورت خروج از از ماستودون، پیش‌نویستان از دست خواهد رفت.",
   "upload_area.title": "برای بارگذاری به این‌جا بکشید",
-  "upload_button.label": "افزودن عکس و ویدیو (JPEG, PNG, GIF, WebM, MP4, MOV)",
-  "upload_error.limit": "از حد مجاز باگذاری فراتر رفتید.",
-  "upload_error.poll": "باگذاری پرونده در نظرسنجی‌ها ممکن نیست.",
-  "upload_form.description": "نوشتهٔ توضیحی برای کم‌بینایان و نابینایان",
+  "upload_button.label": "افزودن رسانه ({formats})",
+  "upload_error.limit": "تجاوز از کران بارگذاری پرونده.",
+  "upload_error.poll": "بارگذاری پرونده در نظرسنجی‌ها مجاز نیست.",
+  "upload_form.audio_description": "توصیف برای افراد فاقد شنوایی",
+  "upload_form.description": "توضیح برای کم‌بینایان",
   "upload_form.edit": "ویرایش",
   "upload_form.undo": "حذف",
+  "upload_form.video_description": "توصیف برای افراد با اختلال دید یا فاقد شنوایی",
   "upload_modal.analyzing_picture": "در حال پردازش تصویر…",
-  "upload_modal.apply": "اجرا",
-  "upload_modal.description_placeholder": "مردی با بیل مادرزنش را کشت",
-  "upload_modal.detect_text": "پیدا کردن نوشته از درون تصویر",
-  "upload_modal.edit_media": "ویرایش تصویر",
-  "upload_modal.hint": "حتی اگر تصویر بریده یا کوچک شود، نقطهٔ کانونی آن همیشه دیده خواهد شد. نقطهٔ کانونی را با کلیک یا جابه‌جا کردن آن تنظیم کنید.",
+  "upload_modal.apply": "اعمال",
+  "upload_modal.description_placeholder": "الا یا ایّها الساقی، ادر کأساً و ناولها",
+  "upload_modal.detect_text": "تشخیص متن درون عکس",
+  "upload_modal.edit_media": "ویرایش رسانه",
+  "upload_modal.hint": "دایره را روی پیش‌نمایش کشیده یا کلیک کنید تا نقطهٔ کانونی‌ای که همواره باید در تمام بندانگشتی‌ها داخل نما باشد را برگزینید.",
   "upload_modal.preview_label": "پیش‌نمایش ({ratio})",
-  "upload_progress.label": "بارگذاری...",
+  "upload_progress.label": "در حال بارگذاری…",
   "video.close": "بستن ویدیو",
-  "video.exit_fullscreen": "خروج از حالت تمام صفحه",
-  "video.expand": "بزرگ‌کردن ویدیو",
-  "video.fullscreen": "تمام صفحه",
+  "video.download": "بارگیری پرونده",
+  "video.exit_fullscreen": "خروج از حالت تمام‌صفحه",
+  "video.expand": "گسترش ویدیو",
+  "video.fullscreen": "تمام‌صفحه",
   "video.hide": "نهفتن ویدیو",
-  "video.mute": "قطع صدا",
-  "video.pause": "توقف",
+  "video.mute": "خموشی صدا",
+  "video.pause": "مکث",
   "video.play": "پخش",
-  "video.unmute": "پخش صدا"
+  "video.unmute": "رفع خموشی صدا"
 }
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
index 60fa0c296..8a069f2a3 100644
--- a/app/javascript/mastodon/locales/fi.json
+++ b/app/javascript/mastodon/locales/fi.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Lisää tai poista listoilta",
   "account.badges.bot": "Botti",
+  "account.badges.group": "Group",
   "account.block": "Estä @{name}",
   "account.block_domain": "Piilota kaikki sisältö verkkotunnuksesta {domain}",
   "account.blocked": "Estetty",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Määrää rajoitettu",
   "alert.unexpected.message": "Tapahtui odottamaton virhe.",
   "alert.unexpected.title": "Hups!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} viikossa",
   "boost_modal.combo": "Ensi kerralla voit ohittaa tämän painamalla {combo}",
   "bundle_column_error.body": "Jokin meni vikaan komponenttia ladattaessa.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Jokin meni vikaan komponenttia ladattaessa.",
   "bundle_modal_error.retry": "Yritä uudestaan",
   "column.blocks": "Estetyt käyttäjät",
+  "column.bookmarks": "Kirjanmerkit",
   "column.community": "Paikallinen aikajana",
   "column.direct": "Viestit",
   "column.directory": "Selaa profiileja",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Äänestyksen kesto",
   "compose_form.poll.option_placeholder": "Valinta numero",
   "compose_form.poll.remove_option": "Poista tämä valinta",
+  "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": "Tuuttaa",
   "compose_form.publish_loud": "Julkista!",
   "compose_form.sensitive.hide": "Valitse tämä arkaluontoisena",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Ei ole 'toots' täällä!",
   "empty_column.account_unavailable": "Profiilia ei löydy",
   "empty_column.blocks": "Et ole vielä estänyt yhtään käyttäjää.",
+  "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "Paikallinen aikajana on tyhjä. Homma lähtee käyntiin, kun kirjoitat jotain julkista!",
   "empty_column.direct": "Sinulla ei ole vielä yhtään viestiä yksittäiselle käyttäjälle. Kun lähetät tai vastaanotat sellaisen, se näkyy täällä.",
   "empty_column.domain_blocks": "Yhtään verkko-osoitetta ei ole vielä piilotettu.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "avaa lista mykistetyistä käyttäjistä",
   "keyboard_shortcuts.my_profile": "avaa profiilisi",
   "keyboard_shortcuts.notifications": "avaa ilmoitukset-sarake",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "avaa lista kiinnitetyistä tuuttauksista",
   "keyboard_shortcuts.profile": "avaa kirjoittajan profiili",
   "keyboard_shortcuts.reply": "vastaa",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Piilota tältä käyttäjältä tulevat ilmoitukset?",
   "navigation_bar.apps": "Mobiilisovellukset",
   "navigation_bar.blocks": "Estetyt käyttäjät",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Paikallinen aikajana",
   "navigation_bar.compose": "Kirjoita uusi tuuttaus",
   "navigation_bar.direct": "Viestit",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Tunnukset",
   "notification.favourite": "{name} tykkäsi tilastasi",
   "notification.follow": "{name} seurasi sinua",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} mainitsi sinut",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "Kysely, johon osallistuit, on päättynyt",
   "notification.reblog": "{name} buustasi tilaasi",
   "notifications.clear": "Tyhjennä ilmoitukset",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Pikasuodatuspalkki",
   "notifications.column_settings.filter_bar.show": "Näytä",
   "notifications.column_settings.follow": "Uudet seuraajat:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "Maininnat:",
   "notifications.column_settings.poll": "Kyselyn tulokset:",
   "notifications.column_settings.push": "Push-ilmoitukset",
@@ -344,6 +355,7 @@
   "status.admin_account": "Avaa moderaattorinäkymä tilistä @{name}",
   "status.admin_status": "Avaa tilapäivitys moderaattorinäkymässä",
   "status.block": "Estä @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Peru buustaus",
   "status.cannot_reblog": "Tätä julkaisua ei voi buustata",
   "status.copy": "Kopioi linkki tilapäivitykseen",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} buustasi",
   "status.reblogs.empty": "Kukaan ei ole vielä buustannut tätä tuuttausta. Kun joku tekee niin, näkyy kyseinen henkilö tässä.",
   "status.redraft": "Poista & palauta muokattavaksi",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "Vastaa",
   "status.replyAll": "Vastaa ketjuun",
   "status.report": "Raportoi @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Lisää mediaa",
   "upload_error.limit": "Tiedostolatauksien raja ylitetty.",
   "upload_error.poll": "Tiedon lataaminen ei ole sallittua kyselyissä.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "Anna kuvaus näkörajoitteisia varten",
   "upload_form.edit": "Muokkaa",
   "upload_form.undo": "Peru",
+  "upload_form.video_description": "Describe for people with hearing loss or visual impairment",
   "upload_modal.analyzing_picture": "Analysoidaan kuvaa…",
   "upload_modal.apply": "Käytä",
   "upload_modal.description_placeholder": "Eräänä jäätävänä ja pimeänä yönä gorilla ratkaisi sudokun kahdessa minuutissa",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Esikatselu ({ratio})",
   "upload_progress.label": "Ladataan...",
   "video.close": "Sulje video",
+  "video.download": "Lataa tiedosto",
   "video.exit_fullscreen": "Poistu koko näytön tilasta",
   "video.expand": "Laajenna video",
   "video.fullscreen": "Koko näyttö",
diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json
index a92f23415..7cd5ec0e3 100644
--- a/app/javascript/mastodon/locales/fr.json
+++ b/app/javascript/mastodon/locales/fr.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Ajouter ou retirer des listes",
   "account.badges.bot": "Robot",
+  "account.badges.group": "Groupe",
   "account.block": "Bloquer @{name}",
   "account.block_domain": "Tout masquer venant de {domain}",
   "account.blocked": "Bloqué·e",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Débit limité",
   "alert.unexpected.message": "Une erreur inattendue s’est produite.",
   "alert.unexpected.title": "Oups !",
+  "announcement.announcement": "Annonce",
   "autosuggest_hashtag.per_week": "{count} par semaine",
   "boost_modal.combo": "Vous pouvez appuyer sur {combo} pour passer ceci, la prochaine fois",
   "bundle_column_error.body": "Une erreur s’est produite lors du chargement de ce composant.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Une erreur s’est produite lors du chargement de ce composant.",
   "bundle_modal_error.retry": "Réessayer",
   "column.blocks": "Comptes bloqués",
+  "column.bookmarks": "Marque-pages",
   "column.community": "Fil public local",
   "column.direct": "Messages privés",
   "column.directory": "Parcourir les profils",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Durée du sondage",
   "compose_form.poll.option_placeholder": "Choix {number}",
   "compose_form.poll.remove_option": "Supprimer ce choix",
+  "compose_form.poll.switch_to_multiple": "Changer le sondage pour autoriser plusieurs choix",
+  "compose_form.poll.switch_to_single": "Changer le sondage pour autoriser qu'un seul choix",
   "compose_form.publish": "Pouet",
   "compose_form.publish_loud": "{publish} !",
   "compose_form.sensitive.hide": "Marquer le média comme sensible",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Aucun pouet ici !",
   "empty_column.account_unavailable": "Profil non disponible",
   "empty_column.blocks": "Vous n’avez bloqué aucun·e utilisateur·rice pour le moment.",
+  "empty_column.bookmarked_statuses": "Vous n'avez pas de pouets enregistrés comme marque-pages pour le moment. 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.domain_blocks": "Il n’y a aucun domaine caché pour le moment.",
@@ -197,7 +203,7 @@
   "introduction.interactions.reply.text": "Vous pouvez répondre aux pouets d'autres personnes et à vos propres pouets, ce qui les enchaînera dans une conversation.",
   "introduction.welcome.action": "Allons-y !",
   "introduction.welcome.headline": "Premiers pas",
-  "introduction.welcome.text": "Bienvenue dans le fediverse ! Dans quelques instants, vous pourrez diffuser des messages et parler à vos amis sur une grande variété de serveurs. Mais ce serveur, {domain}, est spécial - il héberge votre profil, alors souvenez-vous de son nom.",
+  "introduction.welcome.text": "Bienvenue dans le fédiverse ! Dans quelques instants, vous pourrez diffuser des messages et parler à vos ami·e·s sur une grande variété de serveurs. Mais ce serveur, {domain}, est spécial - il héberge votre profil, alors souvenez-vous de son nom.",
   "keyboard_shortcuts.back": "pour revenir en arrière",
   "keyboard_shortcuts.blocked": "pour ouvrir la liste des comptes bloqués",
   "keyboard_shortcuts.boost": "pour partager",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "pour ouvrir la liste des utilisateur·rice·s muté·e·s",
   "keyboard_shortcuts.my_profile": "pour ouvrir votre profil",
   "keyboard_shortcuts.notifications": "pour ouvrir votre colonne de notifications",
+  "keyboard_shortcuts.open_media": "pour ouvrir le média",
   "keyboard_shortcuts.pinned": "pour ouvrir une liste des pouets épinglés",
   "keyboard_shortcuts.profile": "pour ouvrir le profil de l’auteur·rice",
   "keyboard_shortcuts.reply": "pour répondre",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Masquer les notifications de cette personne ?",
   "navigation_bar.apps": "Applications mobiles",
   "navigation_bar.blocks": "Comptes bloqués",
+  "navigation_bar.bookmarks": "Marque-pages",
   "navigation_bar.community_timeline": "Fil public local",
   "navigation_bar.compose": "Rédiger un nouveau pouet",
   "navigation_bar.direct": "Messages directs",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Sécurité",
   "notification.favourite": "{name} a ajouté à ses favoris :",
   "notification.follow": "{name} vous suit",
+  "notification.follow_request": "{name} a demandé à vous suivre",
   "notification.mention": "{name} vous a mentionné·e :",
+  "notification.own_poll": "Votre sondage est terminé",
   "notification.poll": "Un sondage auquel vous avez participé vient de se terminer",
   "notification.reblog": "{name} a partagé votre statut :",
   "notifications.clear": "Nettoyer les notifications",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Barre de filtrage rapide",
   "notifications.column_settings.filter_bar.show": "Afficher",
   "notifications.column_settings.follow": "Nouveaux⋅elles abonné⋅e·s :",
+  "notifications.column_settings.follow_request": "Nouvelles demandes d’abonnement :",
   "notifications.column_settings.mention": "Mentions :",
   "notifications.column_settings.poll": "Résultats des sondage :",
   "notifications.column_settings.push": "Notifications push",
@@ -291,7 +302,7 @@
   "notifications.column_settings.show": "Afficher dans la colonne",
   "notifications.column_settings.sound": "Émettre un son",
   "notifications.filter.all": "Tout",
-  "notifications.filter.boosts": "Repartages",
+  "notifications.filter.boosts": "Partages",
   "notifications.filter.favourites": "Favoris",
   "notifications.filter.follows": "Abonné·e·s",
   "notifications.filter.mentions": "Mentions",
@@ -331,7 +342,7 @@
   "report.target": "Signalement de {target}",
   "search.placeholder": "Rechercher",
   "search_popout.search_format": "Recherche avancée",
-  "search_popout.tips.full_text": "Les textes simples retournent les pouets que vous avez écris, mis en favori, épinglés, ou vous mentionnant, ainsi que les identifiants, les noms affichés, et les hashtags des personnes et messages correspondant.",
+  "search_popout.tips.full_text": "Un texte normal retourne les pouets que vous avez écris, mis en favori, partagés, ou vous mentionnant, ainsi que les identifiants, les noms affichés, et les hashtags des personnes et messages correspondant.",
   "search_popout.tips.hashtag": "hashtag",
   "search_popout.tips.status": "statuts",
   "search_popout.tips.text": "Un texte simple renvoie les noms affichés, les identifiants et les hashtags correspondants",
@@ -344,7 +355,8 @@
   "status.admin_account": "Ouvrir l’interface de modération pour @{name}",
   "status.admin_status": "Ouvrir ce statut dans l’interface de modération",
   "status.block": "Bloquer @{name}",
-  "status.cancel_reblog_private": "Dé-booster",
+  "status.bookmark": "Ajouter aux marque-pages",
+  "status.cancel_reblog_private": "Annuler le partage",
   "status.cannot_reblog": "Ce pouet ne peut pas être partagé",
   "status.copy": "Copier le lien vers le pouet",
   "status.delete": "Effacer",
@@ -364,10 +376,11 @@
   "status.pinned": "Pouet épinglé",
   "status.read_more": "En savoir plus",
   "status.reblog": "Partager",
-  "status.reblog_private": "Booster vers l’audience originale",
+  "status.reblog_private": "Partager à l’audience originale",
   "status.reblogged_by": "{name} a partagé :",
   "status.reblogs.empty": "Personne n’a encore partagé ce pouet. Lorsque quelqu’un le fera, il apparaîtra ici.",
   "status.redraft": "Effacer et ré-écrire",
+  "status.remove_bookmark": "Retirer des marque-pages",
   "status.reply": "Répondre",
   "status.replyAll": "Répondre au fil",
   "status.report": "Signaler @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Joindre un média ({formats})",
   "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 malvoyant·e·s",
   "upload_form.edit": "Modifier",
   "upload_form.undo": "Supprimer",
+  "upload_form.video_description": "Décrire pour les personnes ayant des problèmes d’audition ou de vision",
   "upload_modal.analyzing_picture": "Analyse de l’image en cours…",
   "upload_modal.apply": "Appliquer",
   "upload_modal.description_placeholder": "Buvez de ce whisky que le patron juge fameux",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Aperçu ({ratio})",
   "upload_progress.label": "Envoi en cours…",
   "video.close": "Fermer la vidéo",
+  "video.download": "Télécharger le fichier",
   "video.exit_fullscreen": "Quitter le plein écran",
   "video.expand": "Agrandir la vidéo",
   "video.fullscreen": "Plein écran",
diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json
index 2dd0dbbad..508456736 100644
--- a/app/javascript/mastodon/locales/ga.json
+++ b/app/javascript/mastodon/locales/ga.json
@@ -1,6 +1,7 @@
 {
   "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": "Hide everything from {domain}",
   "account.blocked": "Blocked",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Rate limited",
   "alert.unexpected.message": "An unexpected error occurred.",
   "alert.unexpected.title": "Oops!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} per week",
   "boost_modal.combo": "You can press {combo} to skip this next time",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Something went wrong while loading this component.",
   "bundle_modal_error.retry": "Try again",
   "column.blocks": "Blocked users",
+  "column.bookmarks": "Bookmarks",
   "column.community": "Local timeline",
   "column.direct": "Direct messages",
   "column.directory": "Browse profiles",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Poll duration",
   "compose_form.poll.option_placeholder": "Choice {number}",
   "compose_form.poll.remove_option": "Remove this choice",
+  "compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices",
+  "compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
   "compose_form.publish": "Toot",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Mark media as sensitive",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
   "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no hidden domains yet.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "to open muted users list",
   "keyboard_shortcuts.my_profile": "to open your profile",
   "keyboard_shortcuts.notifications": "to open notifications column",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "to open pinned toots list",
   "keyboard_shortcuts.profile": "to open author's profile",
   "keyboard_shortcuts.reply": "to reply",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blocked users",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Security",
   "notification.favourite": "{name} favourited your status",
   "notification.follow": "{name} followed you",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} mentioned you",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "A poll you have voted in has ended",
   "notification.reblog": "{name} boosted your status",
   "notifications.clear": "Clear notifications",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Quick filter bar",
   "notifications.column_settings.filter_bar.show": "Show",
   "notifications.column_settings.follow": "New followers:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "Mentions:",
   "notifications.column_settings.poll": "Poll results:",
   "notifications.column_settings.push": "Push notifications",
@@ -344,6 +355,7 @@
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_status": "Open this status in the moderation interface",
   "status.block": "Block @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.copy": "Copy link to status",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} boosted",
   "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
   "status.redraft": "Delete & re-draft",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "Reply",
   "status.replyAll": "Reply to thread",
   "status.report": "Report @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Add media ({formats})",
   "upload_error.limit": "File upload limit exceeded.",
   "upload_error.poll": "File upload not allowed with polls.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "Describe for the visually impaired",
   "upload_form.edit": "Edit",
   "upload_form.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.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "Uploading...",
   "video.close": "Close video",
+  "video.download": "Download file",
   "video.exit_fullscreen": "Exit full screen",
   "video.expand": "Expand video",
   "video.fullscreen": "Full screen",
diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json
index 0125754b1..81ba22987 100644
--- a/app/javascript/mastodon/locales/gl.json
+++ b/app/javascript/mastodon/locales/gl.json
@@ -1,27 +1,28 @@
 {
-  "account.add_or_remove_from_list": "Engadir ou Eliminar das listas",
+  "account.add_or_remove_from_list": "Engadir ou eliminar das listaxes",
   "account.badges.bot": "Bot",
+  "account.badges.group": "Grupo",
   "account.block": "Bloquear @{name}",
-  "account.block_domain": "Ocultar calquer contido de {domain}",
+  "account.block_domain": "Agochar todo de {domain}",
   "account.blocked": "Bloqueada",
-  "account.cancel_follow_request": "Cancelar petición de seguemento",
+  "account.cancel_follow_request": "Desbotar solicitude de seguimento",
   "account.direct": "Mensaxe directa @{name}",
   "account.domain_blocked": "Dominio agochado",
   "account.edit_profile": "Editar perfil",
-  "account.endorse": "Mostrado no perfil",
+  "account.endorse": "Amosar no perfil",
   "account.follow": "Seguir",
   "account.followers": "Seguidoras",
-  "account.followers.empty": "Ninguén está a seguir esta usuaria por agora.",
+  "account.followers.empty": "Aínda ninguén segue esta usuaria.",
   "account.follows": "Seguindo",
   "account.follows.empty": "Esta usuaria aínda non segue a ninguén.",
   "account.follows_you": "Séguete",
-  "account.hide_reblogs": "Ocultar repeticións de @{name}",
-  "account.last_status": "Último activo",
-  "account.link_verified_on": "A propiedade de esta ligazón foi comprobada en {date}",
-  "account.locked_info": "O estado da intimidade de esta conta estableceuse en pechado. A persoa dona da conta revisa quen pode seguila.",
-  "account.media": "Medios",
+  "account.hide_reblogs": "Agochar repeticións de @{name}",
+  "account.last_status": "Última actividade",
+  "account.link_verified_on": "A propiedade desta ligazón foi verificada o {date}",
+  "account.locked_info": "Esta é unha conta privada. A propietaria revisa de xeito manual quen pode seguila.",
+  "account.media": "Multimedia",
   "account.mention": "Mencionar @{name}",
-  "account.moved_to": "{name} marchou a:",
+  "account.moved_to": "{name} mudouse a:",
   "account.mute": "Acalar @{name}",
   "account.mute_notifications": "Acalar as notificacións de @{name}",
   "account.muted": "Acalada",
@@ -29,395 +30,410 @@
   "account.posts": "Toots",
   "account.posts_with_replies": "Toots e respostas",
   "account.report": "Informar sobre @{name}",
-  "account.requested": "Agardando aceptación. Pulse para cancelar a solicitude de seguimento",
+  "account.requested": "Agardando aprovación. Preme para desbotar a solicitude de seguimento",
   "account.share": "Compartir o perfil de @{name}",
-  "account.show_reblogs": "Mostrar repeticións de @{name}",
+  "account.show_reblogs": "Amosar compartidos de @{name}",
   "account.unblock": "Desbloquear @{name}",
-  "account.unblock_domain": "Non ocultar {domain}",
-  "account.unendorse": "Non mostrar no perfil",
-  "account.unfollow": "Non seguir",
-  "account.unmute": "Non acalar @{name}",
-  "account.unmute_notifications": "Desbloquear as notificacións de @{name}",
-  "alert.rate_limited.message": "Por favor inténteo tras {retry_time, time, medium}.",
-  "alert.rate_limited.title": "Taxa limitada",
-  "alert.unexpected.message": "Aconteceu un fallo non agardado.",
-  "alert.unexpected.title": "Vaia!",
+  "account.unblock_domain": "Amosar {domain}",
+  "account.unendorse": "Non amosar no perfil",
+  "account.unfollow": "Deixar de seguir",
+  "account.unmute": "Deixar de silenciar a @{name}",
+  "account.unmute_notifications": "Deixar de silenciar as notificacións de @{name}",
+  "alert.rate_limited.message": "Téntao novamente após {retry_time, time, medium}.",
+  "alert.rate_limited.title": "Límite de intentos",
+  "alert.unexpected.message": "Ocorreu un erro non agardado.",
+  "alert.unexpected.title": "Vaites!",
+  "announcement.announcement": "Anuncio",
   "autosuggest_hashtag.per_week": "{count} por semana",
-  "boost_modal.combo": "Pulse {combo} para saltar esto a próxima vez",
-  "bundle_column_error.body": "Houbo un fallo mentras se cargaba este compoñente.",
-  "bundle_column_error.retry": "Inténteo de novo",
+  "boost_modal.combo": "Preme {combo} para ignorar isto na seguinte vez",
+  "bundle_column_error.body": "Ocorreu un erro ó cargar este compoñente.",
+  "bundle_column_error.retry": "Téntao de novo",
   "bundle_column_error.title": "Fallo na rede",
   "bundle_modal_error.close": "Pechar",
-  "bundle_modal_error.message": "Algo fallou mentras se cargaba este compoñente.",
-  "bundle_modal_error.retry": "Inténteo de novo",
+  "bundle_modal_error.message": "Ocorreu un erro ó cargar este compoñente.",
+  "bundle_modal_error.retry": "Téntao de novo",
   "column.blocks": "Usuarias bloqueadas",
-  "column.community": "Liña temporal local",
+  "column.bookmarks": "Marcadores",
+  "column.community": "Cronoloxía local",
   "column.direct": "Mensaxes directas",
-  "column.directory": "Ver perfiles",
+  "column.directory": "Procurar perfís",
   "column.domain_blocks": "Dominios agochados",
-  "column.favourites": "Favoritas",
+  "column.favourites": "Favoritos",
   "column.follow_requests": "Peticións de seguimento",
   "column.home": "Inicio",
-  "column.lists": "Listas",
+  "column.lists": "Listaxes",
   "column.mutes": "Usuarias acaladas",
   "column.notifications": "Notificacións",
-  "column.pins": "Mensaxes fixadas",
-  "column.public": "Liña temporal federada",
-  "column_back_button.label": "Atrás",
+  "column.pins": "Toots fixados",
+  "column.public": "Cronoloxía federada",
+  "column_back_button.label": "Voltar",
   "column_header.hide_settings": "Agochar axustes",
-  "column_header.moveLeft_settings": "Mover a columna hacia a esquerda",
-  "column_header.moveRight_settings": "Mover a columna hacia a dereita",
+  "column_header.moveLeft_settings": "Mover columna cara a esquerda",
+  "column_header.moveRight_settings": "Mover columna cara a dereita",
   "column_header.pin": "Fixar",
-  "column_header.show_settings": "Mostras axustes",
-  "column_header.unpin": "Soltar",
+  "column_header.show_settings": "Amosar axustes",
+  "column_header.unpin": "Desafixar",
   "column_subheading.settings": "Axustes",
-  "community.column_settings.media_only": "Só medios",
-  "compose_form.direct_message_warning": "Este toot enviarase só as usuarias mencionadas. Porén, a súa proveedora de internet e calquera das instancias receptoras poderían examinar esta mensaxe.",
+  "community.column_settings.media_only": "Só multimedia",
+  "compose_form.direct_message_warning": "Este toot só será enviado ás usuarias mencionadas.",
   "compose_form.direct_message_warning_learn_more": "Coñecer máis",
-  "compose_form.hashtag_warning": "Esta mensaxe non será listada baixo ningunha etiqueta xa que está marcada como non listada. Só os toots públicos poden buscarse por etiquetas.",
-  "compose_form.lock_disclaimer": "A súa conta non está {locked}. Calquera pode seguila para ver as súas mensaxes só-para-seguidoras.",
-  "compose_form.lock_disclaimer.lock": "bloqueado",
+  "compose_form.hashtag_warning": "Este toot non aparecerá baixo ningún cancelo (hashtag) porque non está listado. Só se poden procurar toots públicos por cancelos.",
+  "compose_form.lock_disclaimer": "A túa conta non está {locked}. Todas poden seguirte para ollar os teus toots só para seguidoras.",
+  "compose_form.lock_disclaimer.lock": "bloqueada",
   "compose_form.placeholder": "Qué contas?",
   "compose_form.poll.add_option": "Engadir unha opción",
-  "compose_form.poll.duration": "Duración da sondaxe",
+  "compose_form.poll.duration": "Duración da enquisa",
   "compose_form.poll.option_placeholder": "Opción {number}",
   "compose_form.poll.remove_option": "Eliminar esta opción",
+  "compose_form.poll.switch_to_multiple": "Mudar a enquisa para permitir múltiples escollas",
+  "compose_form.poll.switch_to_single": "Mudar a enquisa para permitir unha soa escolla",
   "compose_form.publish": "Toot",
   "compose_form.publish_loud": "{publish}!",
-  "compose_form.sensitive.hide": "Marcar medios como sensibles",
-  "compose_form.sensitive.marked": "Medios marcados como sensibles",
-  "compose_form.sensitive.unmarked": "Os medios non están marcados como sensibles",
+  "compose_form.sensitive.hide": "Marcar coma contido multimedia sensíbel",
+  "compose_form.sensitive.marked": "Contido multimedia marcado coma sensíbel",
+  "compose_form.sensitive.unmarked": "Contido multimedia non marcado coma sensíbel",
   "compose_form.spoiler.marked": "O texto está agochado tras un aviso",
   "compose_form.spoiler.unmarked": "O texto non está agochado",
-  "compose_form.spoiler_placeholder": "Escriba o aviso aquí",
-  "confirmation_modal.cancel": "Cancelar",
-  "confirmations.block.block_and_report": "Bloquear e Informar",
+  "compose_form.spoiler_placeholder": "Escribe o teu aviso aquí",
+  "confirmation_modal.cancel": "Desbotar",
+  "confirmations.block.block_and_report": "Bloquear e denunciar",
   "confirmations.block.confirm": "Bloquear",
-  "confirmations.block.message": "Está segura de querer bloquear a {name}?",
-  "confirmations.delete.confirm": "Borrar",
-  "confirmations.delete.message": "Está segura de que quere eliminar este estado?",
+  "confirmations.block.message": "Tes a certeza de querer bloquear a {name}?",
+  "confirmations.delete.confirm": "Eliminar",
+  "confirmations.delete.message": "Tes a certeza de querer eliminar este estado?",
   "confirmations.delete_list.confirm": "Eliminar",
-  "confirmations.delete_list.message": "Estás seguro de que queres eliminar permanentemente esta lista?",
-  "confirmations.domain_block.confirm": "Agochar un dominio completo",
-  "confirmations.domain_block.message": "Realmente está segura de que quere bloquear por completo o dominio {domain}? Normalmente é suficiente, e preferible, bloquear de xeito selectivo varios elementos. Non verá contidos de ese dominio en ningunha liña temporal ou nas notificacións. As súas seguidoras en ese dominio serán eliminadas.",
-  "confirmations.logout.confirm": "Desconectar",
-  "confirmations.logout.message": "Seguro que desexa desconectar?",
+  "confirmations.delete_list.message": "Tes a certeza de querer eliminar de xeito permanente esta listaxe?",
+  "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.logout.confirm": "Pechar sesión",
+  "confirmations.logout.message": "Desexas pechar a sesión?",
   "confirmations.mute.confirm": "Acalar",
-  "confirmations.mute.explanation": "Esto ocultará as publicacións delas e as que as mencionen, pero poderán seguir lendo as túas publicacións e seguirte.",
-  "confirmations.mute.message": "Está segura de que quere acalar a {name}?",
+  "confirmations.mute.explanation": "Isto agochará as publicacións delas ou nas que as mencionen, mais permitirá que vexan as túas publicacións e sexan seguidoras túas.",
+  "confirmations.mute.message": "Tes a certeza de querer acalar a {name}?",
   "confirmations.redraft.confirm": "Eliminar e reescribir",
-  "confirmations.redraft.message": "Está segura de querer eliminar este estado e voltalo a escribir? Perderá réplicas e favoritas, e as respostas ao orixinal quedarán orfas.",
-  "confirmations.reply.confirm": "Respostar",
-  "confirmations.reply.message": "Respostando agora sobreescribirá a mensaxe que está a compoñer. Segura de querer proceder?",
+  "confirmations.redraft.message": "Tes a certeza de querer eliminar este estado e reescribilo? Perderás os compartidos e favoritos, e as respostas á publicación orixinal ficarán orfas.",
+  "confirmations.reply.confirm": "Responder",
+  "confirmations.reply.message": "Responder agora sobrescribirá a mensaxe que estás a compor. Tes a certeza de que queres continuar?",
   "confirmations.unfollow.confirm": "Deixar de seguir",
-  "confirmations.unfollow.message": "Quere deixar de seguir a {name}?",
+  "confirmations.unfollow.message": "Desexas deixar de seguir a {name}?",
   "conversation.delete": "Eliminar conversa",
   "conversation.mark_as_read": "Marcar como lido",
-  "conversation.open": "Ver conversa",
+  "conversation.open": "Ollar conversa",
   "conversation.with": "Con {names}",
-  "directory.federated": "Desde o fediverso coñecido",
-  "directory.local": "Só desde {domain}",
-  "directory.new_arrivals": "Novas achegas",
-  "directory.recently_active": "Activo recentemente",
-  "embed.instructions": "Copie o código inferior para incrustar no seu sitio web este estado.",
+  "directory.federated": "Do fediverso coñecido",
+  "directory.local": "Só de {domain}",
+  "directory.new_arrivals": "Recén chegadas",
+  "directory.recently_active": "Activas recentemente",
+  "embed.instructions": "Engade este estado ó teu sitio web copiando o seguinte código.",
   "embed.preview": "Así será mostrado:",
   "emoji_button.activity": "Actividade",
-  "emoji_button.custom": "Persoalizado",
-  "emoji_button.flags": "Marcas",
-  "emoji_button.food": "Comida e Bebida",
-  "emoji_button.label": "Insertar emoji",
+  "emoji_button.custom": "Personalizado",
+  "emoji_button.flags": "Bandeiras",
+  "emoji_button.food": "Comida e bebida",
+  "emoji_button.label": "Inserir emoticona",
   "emoji_button.nature": "Natureza",
-  "emoji_button.not_found": "Sen emojos!! (╯°□°)╯︵ ┻━┻",
-  "emoji_button.objects": "Obxetos",
-  "emoji_button.people": "Xente",
-  "emoji_button.recent": "Utilizadas con frecuencia",
-  "emoji_button.search": "Buscar...",
-  "emoji_button.search_results": "Resultados da busca",
+  "emoji_button.not_found": "Non hai emoticonas!! (╯°□°)╯︵ ┻━┻",
+  "emoji_button.objects": "Obxectos",
+  "emoji_button.people": "Persoas",
+  "emoji_button.recent": "Empregadas acotío",
+  "emoji_button.search": "Procurar...",
+  "emoji_button.search_results": "Resultados da procura",
   "emoji_button.symbols": "Símbolos",
-  "emoji_button.travel": "Viaxes e Lugares",
-  "empty_column.account_timeline": "Sen toots por aquí!",
-  "empty_column.account_unavailable": "Perfil non dispoñible",
-  "empty_column.blocks": "Non bloqueou ningunha usuaria polo de agora.",
-  "empty_column.community": "A liña temporal local está baldeira. Escriba algo de xeito público para que rule!",
-  "empty_column.direct": "Aínda non ten mensaxes directas. Cando envíe ou reciba unha, aparecerá aquí.",
-  "empty_column.domain_blocks": "Aínda non  ocultou ningún dominio.",
-  "empty_column.favourited_statuses": "Aínda non ten toots favoritos. Cando favoreza algún, aparecerá aquí.",
-  "empty_column.favourites": "Ninguén favoreceu este toot polo momento. Cando o faga alguén, aparecerán aquí.",
-  "empty_column.follow_requests": "Non ten peticións de seguimento. Cando reciba unha, mostrarase aquí.",
-  "empty_column.hashtag": "Aínda non hai nada con esta etiqueta.",
-  "empty_column.home": "A súa liña temporal de inicio está baldeira! Visite {public} ou utilice a busca para atopar outras usuarias.",
-  "empty_column.home.public_timeline": "a liña temporal pública",
+  "emoji_button.travel": "Viaxes e lugares",
+  "empty_column.account_timeline": "Non hai toots aquí!",
+  "empty_column.account_unavailable": "Perfil non dispoñíbel",
+  "empty_column.blocks": "Aínda non bloqueaches a ningún usuaria.",
+  "empty_column.bookmarked_statuses": "Aínda non marcaches ningún toot. Cando o fagas, amosaranse 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.domain_blocks": "Aínda non hai dominios agochados.",
+  "empty_column.favourited_statuses": "Aínda non tes toots favoritos. Cando che goste algún, aparecerá aquí.",
+  "empty_column.favourites": "A ninguén lle gostou este toot polo momento. Cando a alguén lle goste, aparecerá aquí.",
+  "empty_column.follow_requests": "Non tes peticións de seguimento. Cando recibas unha, amosarase aquí.",
+  "empty_column.hashtag": "Aínda non hai nada con este cancelo.",
+  "empty_column.home": "A túa cronoloxía inicial está baleira! Visita {public} ou emprega a procura para atopar outras usuarias.",
+  "empty_column.home.public_timeline": "a cronoloxía pública",
   "empty_column.list": "Aínda non hai nada en esta lista. Cando as usuarias incluídas na lista publiquen mensaxes, aparecerán aquí.",
-  "empty_column.lists": "Aínda non ten listas. Cando cree unha, mostrarase aquí.",
-  "empty_column.mutes": "Non acalou ningunha usuaria polo de agora.",
-  "empty_column.notifications": "Aínda non ten notificacións. Interactúe con outras para iniciar unha conversa.",
-  "empty_column.public": "Nada por aquí! Escriba algo de xeito público, ou siga manualmente usuarias de outros servidores para ir enchéndoa",
-  "error.unexpected_crash.explanation": "Non se mostra correctamente a páxina debido a un fallo no código ou problema de compatibilidade do navegador.",
-  "error.unexpected_crash.next_steps": "Intenta actualizar a páxina. Se esto non axuda podes tamén utilizar Mastodon en outro navegador ou app nativa.",
-  "errors.unexpected_crash.copy_stacktrace": "Copiar trazas ao portaretallos",
-  "errors.unexpected_crash.report_issue": "Informar de problema",
+  "empty_column.lists": "Aínda non tes listaxes. Cando crees unha, amosarase aquí.",
+  "empty_column.mutes": "Aínda non silenciaches a ningúnha usuaria.",
+  "empty_column.notifications": "Aínda non tes notificacións. Interactúa con outros para comezar unha conversa.",
+  "empty_column.public": "Nada por aquí! Escribe algo de xeito público, ou segue de xeito manual usuarias doutros servidores para ir enchéndoo",
+  "error.unexpected_crash.explanation": "Debido a un erro no noso código ou a unha compatilidade co teu navegador, esta páxina non pode ser amosada correctamente.",
+  "error.unexpected_crash.next_steps": "Tenta actualizar a páxina. Se esto non axuda podes tamén empregar o Mastodon noutro navegador ou aplicación nativa.",
+  "errors.unexpected_crash.copy_stacktrace": "Copiar trazas (stacktrace) ó portapapeis",
+  "errors.unexpected_crash.report_issue": "Denunciar un problema",
   "follow_request.authorize": "Autorizar",
   "follow_request.reject": "Rexeitar",
-  "getting_started.developers": "Desenvolvedoras",
+  "getting_started.developers": "Desenvolvedores",
   "getting_started.directory": "Directorio do perfil",
   "getting_started.documentation": "Documentación",
-  "getting_started.heading": "Comezando",
-  "getting_started.invite": "Convide a xente",
-  "getting_started.open_source_notice": "Mastodon é software de código aberto. Pode contribuír ou informar de fallos en GitHub en {github}.",
-  "getting_started.security": "Seguridade",
+  "getting_started.heading": "Primeiros pasos",
+  "getting_started.invite": "Convidar persoas",
+  "getting_started.open_source_notice": "O Mastodon é software de código aberto. Podes contribuír ou informar de fallos no GitHub en {github}.",
+  "getting_started.security": "Seguranza",
   "getting_started.terms": "Termos do servizo",
   "hashtag.column_header.tag_mode.all": "e {additional}",
   "hashtag.column_header.tag_mode.any": "ou {additional}",
   "hashtag.column_header.tag_mode.none": "sen {additional}",
-  "hashtag.column_settings.select.no_options_message": "Non se atopan suxestións",
-  "hashtag.column_settings.select.placeholder": "Introducir etiquetas…",
-  "hashtag.column_settings.tag_mode.all": "Todos estos",
-  "hashtag.column_settings.tag_mode.any": "Calquera de estos",
-  "hashtag.column_settings.tag_mode.none": "Ningún de estos",
+  "hashtag.column_settings.select.no_options_message": "Non se atoparon suxestións",
+  "hashtag.column_settings.select.placeholder": "Inserir cancelos…",
+  "hashtag.column_settings.tag_mode.all": "Todos estes",
+  "hashtag.column_settings.tag_mode.any": "Calquera destes",
+  "hashtag.column_settings.tag_mode.none": "Ningún destes",
   "hashtag.column_settings.tag_toggle": "Incluír etiquetas adicionais para esta columna",
   "home.column_settings.basic": "Básico",
-  "home.column_settings.show_reblogs": "Mostrar repeticións",
-  "home.column_settings.show_replies": "Mostrar respostas",
+  "home.column_settings.show_reblogs": "Amosar compartidos",
+  "home.column_settings.show_replies": "Amosar respostas",
   "intervals.full.days": "{number, plural,one {# día} other {# días}}",
   "intervals.full.hours": "{number, plural, one {# hora} other {# horas}}",
   "intervals.full.minutes": "{number, plural, one {# minuto} other {# minutos}}",
   "introduction.federation.action": "Seguinte",
   "introduction.federation.federated.headline": "Federado",
-  "introduction.federation.federated.text": "Publicacións públicas desde outros servidores do fediverso aparecerán na liña temporal federada.",
-  "introduction.federation.home.headline": "Inicio",
-  "introduction.federation.home.text": "Publicacións de xente que vostede segue aparecerán no TL de Inicio. Pode seguir a calquera en calquer servidor!",
+  "introduction.federation.federated.text": "Publicacións públicas doutros servidores do fediverso aparecerán na cronoloxía federada.",
+  "introduction.federation.home.headline": "Páxina inicial",
+  "introduction.federation.home.text": "Publicacións de persoas que ti segues aparecerán na cronoloxía do inicio. Podes seguir calquera persoa en calquera servidor!",
   "introduction.federation.local.headline": "Local",
-  "introduction.federation.local.text": "Publicacións públicas de xente no seu mesmo servidor aparecerán na liña temporal local.",
+  "introduction.federation.local.text": "Publicacións públicas de persoas no teu mesmo servidor aparecerán na cronoloxía local.",
   "introduction.interactions.action": "Rematar titorial!",
   "introduction.interactions.favourite.headline": "Favorito",
-  "introduction.interactions.favourite.text": "Pode gardar un toot para máis tarde, e facerlle saber a autora que lle gustou, dándolle a Favorito.",
-  "introduction.interactions.reblog.headline": "Promocionar",
-  "introduction.interactions.reblog.text": "Pode compartir os toots de outra xente coas súas seguidoras promocionándoos.",
-  "introduction.interactions.reply.headline": "Respostar",
-  "introduction.interactions.reply.text": "Pode respostar aos toots de outras persoas e aos seus propios, así quedarán encadeados nunha conversa.",
+  "introduction.interactions.favourite.text": "Podes gardar un toot para depois e facer saber ó autor que che gostou marcandoo coma favorito.",
+  "introduction.interactions.reblog.headline": "Promover",
+  "introduction.interactions.reblog.text": "Podes compartir os toots doutras persoas coas túas seguidoras.",
+  "introduction.interactions.reply.headline": "Responder",
+  "introduction.interactions.reply.text": "Podes responder ós toots doutras persoas e ós teus propios, así ficarán encadeados nunha conversa.",
   "introduction.welcome.action": "Imos!",
   "introduction.welcome.headline": "Primeiros pasos",
-  "introduction.welcome.text": "Benvida ao fediverso! Nun intre poderá difundir mensaxes e falar cos seus amigos nun gran número de servidores. Pero este servidor (dominio) é especial—hospeda o seu perfil, así que lémbreo.",
-  "keyboard_shortcuts.back": "voltar atrás",
+  "introduction.welcome.text": "Benvido ó fediverso! Nun intre poderás difundir mensaxes e falar coas túas amizades nun grande número de servidores. Mais este servidor, {domain}, é especial—hospeda o teu perfil, por iso lémbrate do seu nome.",
+  "keyboard_shortcuts.back": "para voltar atrás",
   "keyboard_shortcuts.blocked": "abrir lista de usuarias bloqueadas",
   "keyboard_shortcuts.boost": "promover",
-  "keyboard_shortcuts.column": "destacar un estado en unha das columnas",
-  "keyboard_shortcuts.compose": "Foco no área de escritura",
+  "keyboard_shortcuts.column": "para destacar un estado nunha das columnas",
+  "keyboard_shortcuts.compose": "para destacar a área de escritura",
   "keyboard_shortcuts.description": "Descrición",
-  "keyboard_shortcuts.direct": "abrir columna de mensaxes directas",
-  "keyboard_shortcuts.down": "ir hacia abaixo na lista",
-  "keyboard_shortcuts.enter": "abrir estado",
-  "keyboard_shortcuts.favourite": "marcar como favorito",
-  "keyboard_shortcuts.favourites": "abrir lista de favoritos",
-  "keyboard_shortcuts.federated": "abrir liña temporal federada",
+  "keyboard_shortcuts.direct": "para abrir a columna de mensaxes directas",
+  "keyboard_shortcuts.down": "para mover cara abaixo na listaxe",
+  "keyboard_shortcuts.enter": "para abrir estado",
+  "keyboard_shortcuts.favourite": "para engadir a favoritos",
+  "keyboard_shortcuts.favourites": "para abrir a listaxe dos favoritos",
+  "keyboard_shortcuts.federated": "para abrir a cronoloxía federada",
   "keyboard_shortcuts.heading": "Atallos do teclado",
-  "keyboard_shortcuts.home": "abrir liña temporal de inicio",
-  "keyboard_shortcuts.hotkey": "Tecla de acceso directo",
-  "keyboard_shortcuts.legend": "para mostrar esta lenda",
-  "keyboard_shortcuts.local": "abrir liña temporal local",
-  "keyboard_shortcuts.mention": "para mencionar o autor",
+  "keyboard_shortcuts.home": "para abrir a cronoloxía inicial",
+  "keyboard_shortcuts.hotkey": "Tecla de atallo",
+  "keyboard_shortcuts.legend": "para amosar esta lenda",
+  "keyboard_shortcuts.local": "para abrir a cronoloxía local",
+  "keyboard_shortcuts.mention": "para mencionar ó autor",
   "keyboard_shortcuts.muted": "abrir lista de usuarias acaladas",
-  "keyboard_shortcuts.my_profile": "abrir o seu perfil",
-  "keyboard_shortcuts.notifications": "abrir columna de notificacións",
-  "keyboard_shortcuts.pinned": "abrir lista de toots fixados",
-  "keyboard_shortcuts.profile": "abrir perfil da autora",
+  "keyboard_shortcuts.my_profile": "para abrir o teu perfil",
+  "keyboard_shortcuts.notifications": "para abrir a columna das notificacións",
+  "keyboard_shortcuts.open_media": "para abrir o contido multimedia",
+  "keyboard_shortcuts.pinned": "para abrir a listaxe dos toots fixados",
+  "keyboard_shortcuts.profile": "para abrir o perfil do autor",
   "keyboard_shortcuts.reply": "para responder",
-  "keyboard_shortcuts.requests": "abrir lista de peticións de seguimento",
-  "keyboard_shortcuts.search": "para centrar a busca",
-  "keyboard_shortcuts.start": "abrir columna \"comezando\"",
-  "keyboard_shortcuts.toggle_hidden": "mostrar/agochar un texto detrás do AC",
-  "keyboard_shortcuts.toggle_sensitivity": "mostrar/ocultar medios",
-  "keyboard_shortcuts.toot": "escribir un toot novo",
-  "keyboard_shortcuts.unfocus": "quitar o foco do área de escritura/busca",
-  "keyboard_shortcuts.up": "ir hacia arriba na lista",
+  "keyboard_shortcuts.requests": "para abrir a listaxe das peticións de seguimento",
+  "keyboard_shortcuts.search": "para destacar a procura",
+  "keyboard_shortcuts.start": "para abrir a columna dos \"primeiros pasos\"",
+  "keyboard_shortcuts.toggle_hidden": "para amosar/agochar texto detrás do aviso de contido (AC)",
+  "keyboard_shortcuts.toggle_sensitivity": "para amosar/agochar contido multimedia",
+  "keyboard_shortcuts.toot": "para escribir un novo toot",
+  "keyboard_shortcuts.unfocus": "para deixar de destacar a área de escritura/procura",
+  "keyboard_shortcuts.up": "para mover cara arriba na listaxe",
   "lightbox.close": "Fechar",
   "lightbox.next": "Seguinte",
   "lightbox.previous": "Anterior",
-  "lightbox.view_context": "Ver contexto",
-  "lists.account.add": "Engadir á lista",
-  "lists.account.remove": "Eliminar da lista",
-  "lists.delete": "Eliminar lista",
-  "lists.edit": "Editar lista",
-  "lists.edit.submit": "Cambiar título",
-  "lists.new.create": "Engadir lista",
-  "lists.new.title_placeholder": "Novo título da lista",
-  "lists.search": "Procurar entre a xente que segues",
-  "lists.subheading": "As túas listas",
+  "lightbox.view_context": "Ollar contexto",
+  "lists.account.add": "Engadir á listaxe",
+  "lists.account.remove": "Eliminar da listaxe",
+  "lists.delete": "Eliminar listaxe",
+  "lists.edit": "Editar listaxe",
+  "lists.edit.submit": "Mudar o título",
+  "lists.new.create": "Engadir listaxe",
+  "lists.new.title_placeholder": "Título da nova listaxe",
+  "lists.search": "Procurar entre as persoas que segues",
+  "lists.subheading": "As túas listaxes",
   "load_pending": "{count, plural, one {# novo elemento} other {# novos elementos}}",
-  "loading_indicator.label": "Cargando...",
-  "media_gallery.toggle_visible": "Ocultar",
+  "loading_indicator.label": "Estase a cargar...",
+  "media_gallery.toggle_visible": "Trocar visibilidade",
   "missing_indicator.label": "Non atopado",
-  "missing_indicator.sublabel": "Non se puido atopar o recurso",
-  "mute_modal.hide_notifications": "Esconder notificacións deste usuario?",
-  "navigation_bar.apps": "Apps móbiles",
+  "missing_indicator.sublabel": "Este recurso non foi atopado",
+  "mute_modal.hide_notifications": "Agochar notificacións desta usuaria?",
+  "navigation_bar.apps": "Aplicacións móbiles",
   "navigation_bar.blocks": "Usuarias bloqueadas",
-  "navigation_bar.community_timeline": "Liña temporal local",
-  "navigation_bar.compose": "Escribir novo toot",
+  "navigation_bar.bookmarks": "Marcadores",
+  "navigation_bar.community_timeline": "Cronoloxía local",
+  "navigation_bar.compose": "Escribir un novo toot",
   "navigation_bar.direct": "Mensaxes directas",
   "navigation_bar.discover": "Descubrir",
   "navigation_bar.domain_blocks": "Dominios agochados",
   "navigation_bar.edit_profile": "Editar perfil",
-  "navigation_bar.favourites": "Favoritas",
-  "navigation_bar.filters": "Palabras acaladas",
+  "navigation_bar.favourites": "Favoritos",
+  "navigation_bar.filters": "Palabras silenciadas",
   "navigation_bar.follow_requests": "Peticións de seguimento",
-  "navigation_bar.follows_and_followers": "Seguindo e seguidoras",
+  "navigation_bar.follows_and_followers": "Seguindo e seguidores",
   "navigation_bar.info": "Sobre este servidor",
-  "navigation_bar.keyboard_shortcuts": "Atallos",
-  "navigation_bar.lists": "Listas",
-  "navigation_bar.logout": "Sair",
-  "navigation_bar.mutes": "Usuarias acaladas",
+  "navigation_bar.keyboard_shortcuts": "Atallos do teclado",
+  "navigation_bar.lists": "Listaxes",
+  "navigation_bar.logout": "Pechar sesión",
+  "navigation_bar.mutes": "Usuarias silenciadas",
   "navigation_bar.personal": "Persoal",
-  "navigation_bar.pins": "Mensaxes fixadas",
+  "navigation_bar.pins": "Toots fixados",
   "navigation_bar.preferences": "Preferencias",
-  "navigation_bar.public_timeline": "Liña temporal federada",
-  "navigation_bar.security": "Seguridade",
-  "notification.favourite": "{name} marcou como favorito o seu estado",
-  "notification.follow": "{name} está a seguila",
+  "navigation_bar.public_timeline": "Cronoloxía federada",
+  "navigation_bar.security": "Seguranza",
+  "notification.favourite": "{name} marcou o teu estado coma favorito",
+  "notification.follow": "{name} comezou a seguirte",
+  "notification.follow_request": "{name} solicitou seguirte",
   "notification.mention": "{name} mencionoute",
-  "notification.poll": "Unha sondaxe na que votou xa rematou",
-  "notification.reblog": "{name} promoveu o seu estado",
+  "notification.own_poll": "A túa enquisa rematou",
+  "notification.poll": "Unha enquisa na que votaches rematou",
+  "notification.reblog": "{name} promoveu o teu estado",
   "notifications.clear": "Limpar notificacións",
-  "notifications.clear_confirmation": "Estás seguro de que queres limpar permanentemente todas as túas notificacións?",
+  "notifications.clear_confirmation": "Tes a certeza de querer limpar de xeito permanente todas as túas notificacións?",
   "notifications.column_settings.alert": "Notificacións de escritorio",
-  "notifications.column_settings.favourite": "Favoritas:",
-  "notifications.column_settings.filter_bar.advanced": "Mostrar todas as categorías",
+  "notifications.column_settings.favourite": "Favoritos:",
+  "notifications.column_settings.filter_bar.advanced": "Amosar todas as categorías",
   "notifications.column_settings.filter_bar.category": "Barra de filtrado rápido",
-  "notifications.column_settings.filter_bar.show": "Mostrar",
+  "notifications.column_settings.filter_bar.show": "Amosar",
   "notifications.column_settings.follow": "Novos seguidores:",
+  "notifications.column_settings.follow_request": "Novas peticións de seguimento:",
   "notifications.column_settings.mention": "Mencións:",
-  "notifications.column_settings.poll": "Resultados da sondaxe:",
-  "notifications.column_settings.push": "Enviar notificacións",
+  "notifications.column_settings.poll": "Resultados da enquisa:",
+  "notifications.column_settings.push": "Notificacións emerxentes",
   "notifications.column_settings.reblog": "Promocións:",
-  "notifications.column_settings.show": "Mostrar en columna",
+  "notifications.column_settings.show": "Amosar en columna",
   "notifications.column_settings.sound": "Reproducir son",
   "notifications.filter.all": "Todo",
-  "notifications.filter.boosts": "Promocións",
+  "notifications.filter.boosts": "Compartidos",
   "notifications.filter.favourites": "Favoritos",
   "notifications.filter.follows": "Seguimentos",
   "notifications.filter.mentions": "Mencións",
-  "notifications.filter.polls": "Resultados da sondaxe",
+  "notifications.filter.polls": "Resultados da enquisa",
   "notifications.group": "{count} notificacións",
   "poll.closed": "Pechado",
   "poll.refresh": "Actualizar",
   "poll.total_people": "{count, plural,one {# persoa}other {# persoas}}",
   "poll.total_votes": "{count, plural, one {# voto} outros {# votos}}",
   "poll.vote": "Votar",
-  "poll.voted": "Votou por esta opción",
-  "poll_button.add_poll": "Engadir sondaxe",
-  "poll_button.remove_poll": "Eliminar sondaxe",
-  "privacy.change": "Axustar a intimidade do estado",
-  "privacy.direct.long": "Enviar exclusivamente as usuarias mencionadas",
-  "privacy.direct.short": "Directa",
-  "privacy.private.long": "Enviar só as seguidoras",
-  "privacy.private.short": "Só-seguidoras",
-  "privacy.public.long": "Publicar na liña temporal pública",
-  "privacy.public.short": "Pública",
-  "privacy.unlisted.long": "Non publicar en liñas temporais públicas",
-  "privacy.unlisted.short": "Non listada",
+  "poll.voted": "Votaches por esta opción",
+  "poll_button.add_poll": "Engadir unha enquisa",
+  "poll_button.remove_poll": "Eliminar enquisa",
+  "privacy.change": "Axustar privacidade",
+  "privacy.direct.long": "Só para as usuarias mencionadas",
+  "privacy.direct.short": "Directo",
+  "privacy.private.long": "Só para os seguidores",
+  "privacy.private.short": "Só seguidores",
+  "privacy.public.long": "Publicar nas cronoloxías públicas",
+  "privacy.public.short": "Público",
+  "privacy.unlisted.long": "Non publicar nas cronoloxías públicas",
+  "privacy.unlisted.short": "Non listado",
   "refresh": "Actualizar",
-  "regeneration_indicator.label": "Cargando…",
-  "regeneration_indicator.sublabel": "Estase a preparar a súa liña temporal de inicio!",
+  "regeneration_indicator.label": "Estase a cargar…",
+  "regeneration_indicator.sublabel": "Estase a preparar a túa cronoloxía de inicio!",
   "relative_time.days": "{number}d",
   "relative_time.hours": "{number}h",
   "relative_time.just_now": "agora",
   "relative_time.minutes": "{number}m",
   "relative_time.seconds": "{number}s",
-  "reply_indicator.cancel": "Cancelar",
+  "reply_indicator.cancel": "Desbotar",
   "report.forward": "Reenviar a {target}",
-  "report.forward_hint": "A conta pertence a outro servidor. Enviar unha copia anónima do informe alí tamén?",
-  "report.hint": "O informe enviarase a moderación do seu servidor. Abaixo pode explicar a razón pola que está a informar:",
+  "report.forward_hint": "A conta é doutro servidor. Enviar unha copia anónima da denuncia aló tamén?",
+  "report.hint": "A denuncia enviarase á moderación do teu servidor. Abaixo podes explicar a razón pola que estás a denunciar:",
   "report.placeholder": "Comentarios adicionais",
   "report.submit": "Enviar",
-  "report.target": "Informar {target}",
-  "search.placeholder": "Buscar",
-  "search_popout.search_format": "Formato de busca avanzada",
-  "search_popout.tips.full_text": "Texto simple devolve estados que vostede escribeu, promoveu, marcou favoritos, ou foi mencionada, así como nomes de usuaria coincidentes, nomes públicos e etiquetas.",
-  "search_popout.tips.hashtag": "etiqueta",
+  "report.target": "Denunciar a {target}",
+  "search.placeholder": "Procurar",
+  "search_popout.search_format": "Formato de procura avanzada",
+  "search_popout.tips.full_text": "Texto simple devolve estados que ti escribiches, promoviches, marcaches  favoritos, ou foches mencionada, así como nomes de usuaria coincidentes, nomes públicos e etiquetas.",
+  "search_popout.tips.hashtag": "cancelo",
   "search_popout.tips.status": "estado",
   "search_popout.tips.text": "Texto simple devolve coincidencias con nomes públicos, nomes de usuaria e etiquetas",
   "search_popout.tips.user": "usuaria",
-  "search_results.accounts": "Xente",
-  "search_results.hashtags": "Etiquetas",
+  "search_results.accounts": "Persoas",
+  "search_results.hashtags": "Cancelos",
   "search_results.statuses": "Toots",
-  "search_results.statuses_fts_disabled": "Non está activada neste servidor Mastodon a busca de toots polo seu contido.",
-  "search_results.total": "{count, number} {count,plural,one {result} outros {results}}",
+  "search_results.statuses_fts_disabled": "Procurar toots polo seu contido non está activado neste servidor do Mastodon.",
+  "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}",
   "status.admin_account": "Abrir interface de moderación para @{name}",
   "status.admin_status": "Abrir este estado na interface de moderación",
   "status.block": "Bloquear @{name}",
-  "status.cancel_reblog_private": "Non promover",
-  "status.cannot_reblog": "Esta mensaxe non pode ser promovida",
-  "status.copy": "Copiar ligazón ao estado",
+  "status.bookmark": "Marcar",
+  "status.cancel_reblog_private": "Desfacer compartido",
+  "status.cannot_reblog": "Esta publicación non pode ser promovida",
+  "status.copy": "Copiar ligazón ó estado",
   "status.delete": "Eliminar",
   "status.detailed_status": "Vista detallada da conversa",
   "status.direct": "Mensaxe directa @{name}",
-  "status.embed": "Incrustar",
-  "status.favourite": "Favorita",
+  "status.embed": "Embeber nunha web",
+  "status.favourite": "Favorito",
   "status.filtered": "Filtrado",
   "status.load_more": "Cargar máis",
-  "status.media_hidden": "Medios ocultos",
+  "status.media_hidden": "Contido multimedia agochado",
   "status.mention": "Mencionar @{name}",
   "status.more": "Máis",
-  "status.mute": "Acalar @{name}",
-  "status.mute_conversation": "Acalar conversa",
+  "status.mute": "Silenciar @{name}",
+  "status.mute_conversation": "Silenciar conversa",
   "status.open": "Expandir este estado",
   "status.pin": "Fixar no perfil",
   "status.pinned": "Toot fixado",
-  "status.read_more": "Lea máis",
+  "status.read_more": "Ler máis",
   "status.reblog": "Promover",
-  "status.reblog_private": "Promover a audiencia orixinal",
+  "status.reblog_private": "Compartir á audiencia orixinal",
   "status.reblogged_by": "{name} promoveu",
-  "status.reblogs.empty": "Ninguén promoveu este toot polo de agora. Cando alguén o faga, mostraránse aquí.",
-  "status.redraft": "Eliminar & reescribir",
-  "status.reply": "Resposta",
-  "status.replyAll": "Resposta a conversa",
-  "status.report": "Informar @{name}",
-  "status.sensitive_warning": "Contido sensible",
+  "status.reblogs.empty": "Aínda ninguén promoveu este toot. Cando alguén o faga, amosarase aquí.",
+  "status.redraft": "Eliminar e reescribir",
+  "status.remove_bookmark": "Eliminar marcador",
+  "status.reply": "Responder",
+  "status.replyAll": "Responder ó fío",
+  "status.report": "Denunciar @{name}",
+  "status.sensitive_warning": "Contido sensíbel",
   "status.share": "Compartir",
-  "status.show_less": "Mostrar menos",
-  "status.show_less_all": "Mostrar menos para todas",
-  "status.show_more": "Mostrar máis",
-  "status.show_more_all": "Mostrar máis para todas",
-  "status.show_thread": "Mostrar fío",
-  "status.uncached_media_warning": "Non dispoñible",
-  "status.unmute_conversation": "Non acalar a conversa",
-  "status.unpin": "Despegar do perfil",
+  "status.show_less": "Amosar menos",
+  "status.show_less_all": "Amosar menos para todos",
+  "status.show_more": "Amosar máis",
+  "status.show_more_all": "Amosar máis para todos",
+  "status.show_thread": "Amosar fío",
+  "status.uncached_media_warning": "Non dispoñíbel",
+  "status.unmute_conversation": "Deixar de silenciar conversa",
+  "status.unpin": "Desafixar do perfil",
   "suggestions.dismiss": "Rexeitar suxestión",
-  "suggestions.header": "Podería estar interesada en…",
-  "tabs_bar.federated_timeline": "Federado",
+  "suggestions.header": "Poderíache interesar…",
+  "tabs_bar.federated_timeline": "Federada",
   "tabs_bar.home": "Inicio",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notificacións",
-  "tabs_bar.search": "Buscar",
-  "time_remaining.days": "{number, plural, one {# dia} other {# días}} restantes",
+  "tabs_bar.search": "Procurar",
+  "time_remaining.days": "{number, plural, one {# día} other {# días}} restantes",
   "time_remaining.hours": "{number, plural, one {# hora} other {# horas}} restantes",
   "time_remaining.minutes": "{number, plural, one {# minuto} other {# minutos}} restantes",
-  "time_remaining.moments": "Está rematando",
+  "time_remaining.moments": "Momentos restantes",
   "time_remaining.seconds": "{number, plural, one {# segundo} other {# segundos}} restantes",
-  "trends.count_by_accounts": "{count} {rawCount, plural, one {person} outras {people}} conversando",
+  "trends.count_by_accounts": "{count} {rawCount, plural, one {persoa} other {persoas}} falando",
   "trends.trending_now": "Tendencias actuais",
-  "ui.beforeunload": "O borrador perderase se sae de Mastodon.",
-  "upload_area.title": "Arrastre e solte para subir",
-  "upload_button.label": "Engadir medios ({formats})",
-  "upload_error.limit": "Excedeu o límite de subida de ficheiros.",
-  "upload_error.poll": "Non se poden subir ficheiros nas sondaxes.",
-  "upload_form.description": "Describa para deficientes visuais",
+  "ui.beforeunload": "O borrador perderase se saes do Mastodon.",
+  "upload_area.title": "Arrastra e solta para subir",
+  "upload_button.label": "Engadir multimedia ({formats})",
+  "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.edit": "Editar",
   "upload_form.undo": "Eliminar",
-  "upload_modal.analyzing_picture": "Analizando imaxe…",
+  "upload_form.video_description": "Describir para persoas con problemas visuais ou auditivos",
+  "upload_modal.analyzing_picture": "Estase a analizar a imaxe…",
   "upload_modal.apply": "Aplicar",
-  "upload_modal.description_placeholder": "Un raposo moi feitiño salta sobre o can preguiceiro",
+  "upload_modal.description_placeholder": "Un raposo veloz brinca sobre o can preguiceiro",
   "upload_modal.detect_text": "Detectar texto na imaxe",
-  "upload_modal.edit_media": "Editar medios",
-  "upload_modal.hint": "Prema ou arrastre o círculo na vista previa para escolle o punto focal que se verá na vista de todas as miniaturas.",
+  "upload_modal.edit_media": "Editar multimedia",
+  "upload_modal.hint": "Preme ou arrastra o círculo na vista previa para escoller o punto focal que sempre estará á vista en todas as miniaturas.",
   "upload_modal.preview_label": "Vista previa ({ratio})",
-  "upload_progress.label": "Subindo...",
-  "video.close": "Pechar video",
+  "upload_progress.label": "Estase a subir...",
+  "video.close": "Pechar vídeo",
+  "video.download": "Baixar ficheiro",
   "video.exit_fullscreen": "Saír da pantalla completa",
   "video.expand": "Expandir vídeo",
   "video.fullscreen": "Pantalla completa",
   "video.hide": "Agochar vídeo",
-  "video.mute": "Acalar son",
-  "video.pause": "Pausar",
+  "video.mute": "Silenciar son",
+  "video.pause": "Deter",
   "video.play": "Reproducir",
   "video.unmute": "Permitir son"
 }
diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json
index 70aec3279..07217364a 100644
--- a/app/javascript/mastodon/locales/he.json
+++ b/app/javascript/mastodon/locales/he.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "הוסף או הסר מהרשימות",
   "account.badges.bot": "בוט",
+  "account.badges.group": "Group",
   "account.block": "חסימת @{name}",
   "account.block_domain": "להסתיר הכל מהקהילה {domain}",
   "account.blocked": "חסום",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Rate limited",
   "alert.unexpected.message": "אירעה שגיאה בלתי צפויה.",
   "alert.unexpected.title": "אופס!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} per week",
   "boost_modal.combo": "ניתן להקיש {combo} כדי לדלג בפעם הבאה",
   "bundle_column_error.body": "משהו השתבש בעת הצגת הרכיב הזה.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "משהו השתבש בעת טעינת הרכיב הזה.",
   "bundle_modal_error.retry": "לנסות שוב",
   "column.blocks": "חסימות",
+  "column.bookmarks": "Bookmarks",
   "column.community": "ציר זמן מקומי",
   "column.direct": "Direct messages",
   "column.directory": "Browse profiles",
@@ -82,6 +85,8 @@
   "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": "ללחוש",
   "compose_form.publish_loud": "לחצרץ!",
   "compose_form.sensitive.hide": "Mark media as sensitive",
@@ -138,6 +143,7 @@
   "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": "טור הסביבה ריק. יש לפרסם משהו כדי שדברים יתרחילו להתגלגל!",
   "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no hidden domains yet.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "to open muted users list",
   "keyboard_shortcuts.my_profile": "to open your profile",
   "keyboard_shortcuts.notifications": "to open notifications column",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "to open pinned toots list",
   "keyboard_shortcuts.profile": "to open author's profile",
   "keyboard_shortcuts.reply": "לענות",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "להסתיר הודעות מחשבון זה?",
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "חסימות",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "ציר זמן מקומי",
   "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Security",
   "notification.favourite": "חצרוצך חובב על ידי {name}",
   "notification.follow": "{name} במעקב אחרייך",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "אוזכרת על ידי {name}",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "A poll you have voted in has ended",
   "notification.reblog": "חצרוצך הודהד על ידי {name}",
   "notifications.clear": "הסרת התראות",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Quick filter bar",
   "notifications.column_settings.filter_bar.show": "Show",
   "notifications.column_settings.follow": "עוקבים חדשים:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "פניות:",
   "notifications.column_settings.poll": "Poll results:",
   "notifications.column_settings.push": "הודעות בדחיפה",
@@ -344,6 +355,7 @@
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_status": "Open this status in the moderation interface",
   "status.block": "Block @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "לא ניתן להדהד הודעה זו",
   "status.copy": "Copy link to status",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "הודהד על ידי {name}",
   "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
   "status.redraft": "Delete & re-draft",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "תגובה",
   "status.replyAll": "תגובה לכולם",
   "status.report": "דיווח על @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "הוספת מדיה",
   "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": "תיאור לכבדי ראיה",
   "upload_form.edit": "Edit",
   "upload_form.undo": "ביטול",
+  "upload_form.video_description": "Describe for people with hearing loss or visual impairment",
   "upload_modal.analyzing_picture": "Analyzing picture…",
   "upload_modal.apply": "Apply",
   "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "עולה...",
   "video.close": "סגירת וידאו",
+  "video.download": "Download file",
   "video.exit_fullscreen": "יציאה ממסך מלא",
   "video.expand": "להרחיב וידאו",
   "video.fullscreen": "Full screen",
diff --git a/app/javascript/mastodon/locales/hi.json b/app/javascript/mastodon/locales/hi.json
index c50567aac..ff7ea9e6a 100644
--- a/app/javascript/mastodon/locales/hi.json
+++ b/app/javascript/mastodon/locales/hi.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "सूची में जोड़ें या हटाए",
   "account.badges.bot": "बॉट",
+  "account.badges.group": "Group",
   "account.block": "@{name} को ब्लॉक करें",
   "account.block_domain": "{domain} के सारी चीज़े छुपाएं",
   "account.blocked": "ब्लॉक",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "सीमित दर",
   "alert.unexpected.message": "एक अप्रत्याशित त्रुटि हुई है!",
   "alert.unexpected.title": "उफ़!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} हर सप्ताह",
   "boost_modal.combo": "अगली बार स्किप करने के लिए आप {combo} दबा सकते है",
   "bundle_column_error.body": "इस कॉम्पोनेन्ट को लोड करते वक्त कुछ गलत हो गया",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "इस कॉम्पोनेन्ट को लोड करते वक्त कुछ गलत हो गया",
   "bundle_modal_error.retry": "दुबारा कोशिश करें",
   "column.blocks": "ब्लॉक्ड यूज़र्स",
+  "column.bookmarks": "Bookmarks",
   "column.community": "लोकल टाइम्लाइन",
   "column.direct": "सीधा संदेश",
   "column.directory": "प्रोफाइल्स खोजें",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "चुनाव की अवधि",
   "compose_form.poll.option_placeholder": "कुल विकल्प {number}",
   "compose_form.poll.remove_option": "इस विकल्प को हटाएँ",
+  "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": "टूट्",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "मीडिया को संवेदनशील के रूप में चिह्नित करें",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "सन्नाटा! यहां कोई टूट्स नहीं!",
   "empty_column.account_unavailable": "प्रोफाइल उपलब्ध नहीं",
   "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": "आपके पास कोई सीधा सन्देश नहीं है, जब आप कोई भेजेंगे प्राप्त करेंगे तो यहाँ दिखेगा।",
   "empty_column.domain_blocks": "अभी तक कोई छुपा हुआ डोमेन नहीं है।",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "म्यूटेड यूजर की लिस्ट खोलने के लिए",
   "keyboard_shortcuts.my_profile": "आपकी प्रोफाइल खोलने के लिए",
   "keyboard_shortcuts.notifications": "नोटिफिकेशन कॉलम खोलने के लिए",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "पिनड टूट्स की लिस्ट खोलने के लिए",
   "keyboard_shortcuts.profile": "लेखक की प्रोफाइल खोलने के लिए",
   "keyboard_shortcuts.reply": "जवाब के लिए",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.apps": "मोबाइल एप्लिकेशंस",
   "navigation_bar.blocks": "ब्लॉक्ड यूज़र्स",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "लोकल टाइम्लाइन",
   "navigation_bar.compose": "नया टूट् लिखें",
   "navigation_bar.direct": "Direct messages",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Security",
   "notification.favourite": "{name} favourited your status",
   "notification.follow": "{name} followed you",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} mentioned you",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "A poll you have voted in has ended",
   "notification.reblog": "{name} boosted your status",
   "notifications.clear": "Clear notifications",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "फ़िल्टर बार",
   "notifications.column_settings.filter_bar.show": "दिखाएँ",
   "notifications.column_settings.follow": "नए फ़ॉलोअर्स",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "उल्लेख:",
   "notifications.column_settings.poll": "चुनाव परिणाम",
   "notifications.column_settings.push": "पुश सूचनाएँ",
@@ -344,6 +355,7 @@
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_status": "Open this status in the moderation interface",
   "status.block": "Block @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.copy": "Copy link to status",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} boosted",
   "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
   "status.redraft": "Delete & re-draft",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "जवाब",
   "status.replyAll": "Reply to thread",
   "status.report": "Report @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Add media ({formats})",
   "upload_error.limit": "File upload limit exceeded.",
   "upload_error.poll": "File upload not allowed with polls.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "Describe for the visually impaired",
   "upload_form.edit": "संशोधन करें",
   "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": "लागू करें",
   "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "अपलोडिंग...",
   "video.close": "Close video",
+  "video.download": "फाइल डाउनलोड करें",
   "video.exit_fullscreen": "Exit full screen",
   "video.expand": "Expand video",
   "video.fullscreen": "Full screen",
diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json
index 09298a7ad..31327e3d3 100644
--- a/app/javascript/mastodon/locales/hr.json
+++ b/app/javascript/mastodon/locales/hr.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Add or Remove from lists",
   "account.badges.bot": "Bot",
+  "account.badges.group": "Group",
   "account.block": "Blokiraj @{name}",
   "account.block_domain": "Sakrij sve sa {domain}",
   "account.blocked": "Blocked",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Rate limited",
   "alert.unexpected.message": "An unexpected error occurred.",
   "alert.unexpected.title": "Oops!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} per week",
   "boost_modal.combo": "Možeš pritisnuti {combo} kako bi ovo preskočio sljedeći put",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Something went wrong while loading this component.",
   "bundle_modal_error.retry": "Try again",
   "column.blocks": "Blokirani korisnici",
+  "column.bookmarks": "Bookmarks",
   "column.community": "Lokalni timeline",
   "column.direct": "Direct messages",
   "column.directory": "Browse profiles",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Poll duration",
   "compose_form.poll.option_placeholder": "Choice {number}",
   "compose_form.poll.remove_option": "Remove this choice",
+  "compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices",
+  "compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
   "compose_form.publish": "Toot",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Mark media as sensitive",
@@ -138,6 +143,7 @@
   "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": "Lokalni timeline je prazan. Napiši nešto javno kako bi pokrenuo 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.domain_blocks": "There are no hidden domains yet.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "to open muted users list",
   "keyboard_shortcuts.my_profile": "to open your profile",
   "keyboard_shortcuts.notifications": "to open notifications column",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "to open pinned toots list",
   "keyboard_shortcuts.profile": "to open author's profile",
   "keyboard_shortcuts.reply": "to reply",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blokirani korisnici",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Lokalni timeline",
   "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Security",
   "notification.favourite": "{name} je lajkao tvoj status",
   "notification.follow": "{name} te sada slijedi",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} te je spomenuo",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "A poll you have voted in has ended",
   "notification.reblog": "{name} je podigao tvoj status",
   "notifications.clear": "Očisti notifikacije",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Quick filter bar",
   "notifications.column_settings.filter_bar.show": "Show",
   "notifications.column_settings.follow": "Novi sljedbenici:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "Spominjanja:",
   "notifications.column_settings.poll": "Poll results:",
   "notifications.column_settings.push": "Push notifications",
@@ -344,6 +355,7 @@
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_status": "Open this status in the moderation interface",
   "status.block": "Block @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Ovaj post ne može biti boostan",
   "status.copy": "Copy link to status",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} je podigao",
   "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
   "status.redraft": "Delete & re-draft",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "Odgovori",
   "status.replyAll": "Odgovori na temu",
   "status.report": "Prijavi @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Dodaj media",
   "upload_error.limit": "File upload limit exceeded.",
   "upload_error.poll": "File upload not allowed with polls.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "Describe for the visually impaired",
   "upload_form.edit": "Edit",
   "upload_form.undo": "Poništi",
+  "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.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "Uploadam...",
   "video.close": "Close video",
+  "video.download": "Download file",
   "video.exit_fullscreen": "Exit full screen",
   "video.expand": "Expand video",
   "video.fullscreen": "Full screen",
diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json
index 72dd6b986..e8dc573fa 100644
--- a/app/javascript/mastodon/locales/hu.json
+++ b/app/javascript/mastodon/locales/hu.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Hozzáadás vagy eltávolítás a listáról",
   "account.badges.bot": "Bot",
+  "account.badges.group": "Csoport",
   "account.block": "@{name} letiltása",
   "account.block_domain": "Minden elrejtése innen: {domain}",
   "account.blocked": "Letiltva",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Forgalomkorlátozás",
   "alert.unexpected.message": "Váratlan hiba történt.",
   "alert.unexpected.title": "Hoppá!",
+  "announcement.announcement": "Közlemény",
   "autosuggest_hashtag.per_week": "{count}/hét",
   "boost_modal.combo": "Hogy átugord ezt következő alkalommal, használd {combo}",
   "bundle_column_error.body": "Hiba történt a komponens betöltése közben.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Hiba történt a komponens betöltésekor.",
   "bundle_modal_error.retry": "Próbáld újra",
   "column.blocks": "Letiltott felhasználók",
+  "column.bookmarks": "Könyvjelzők",
   "column.community": "Helyi idővonal",
   "column.direct": "Közvetlen üzenetek",
   "column.directory": "Profilok böngészése",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Szavazás időtartama",
   "compose_form.poll.option_placeholder": "{number}. lehetőség",
   "compose_form.poll.remove_option": "Lehetőség törlése",
+  "compose_form.poll.switch_to_multiple": "Szavazás megváltoztatása több választásosra",
+  "compose_form.poll.switch_to_single": "Szavazás megváltoztatása egyetlen választásosra",
   "compose_form.publish": "Tülk",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Média megjelölése szenzitívként",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Itt nincs tülkölés!",
   "empty_column.account_unavailable": "A profil nem érhető el",
   "empty_column.blocks": "Még senkit sem tiltottál le.",
+  "empty_column.bookmarked_statuses": "Még nincs egyetlen könyvjelzőzött tülköd sem. Ha könyvjelzőzöl egyet, itt fog megjelenni.",
   "empty_column.community": "A helyi idővonal üres. Tülkölj egyet 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.",
@@ -219,6 +225,7 @@
   "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.pinned": "kitűzött tülkök listájának megnyitása",
   "keyboard_shortcuts.profile": "szerző profiljának megnyitása",
   "keyboard_shortcuts.reply": "válasz",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Rejtsük el a felhasználótól származó értesítéseket?",
   "navigation_bar.apps": "Mobil appok",
   "navigation_bar.blocks": "Letiltott felhasználók",
+  "navigation_bar.bookmarks": "Könyvjelzők",
   "navigation_bar.community_timeline": "Helyi idővonal",
   "navigation_bar.compose": "Új tülk írása",
   "navigation_bar.direct": "Közvetlen üzenetek",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Biztonság",
   "notification.favourite": "{name} kedvencnek jelölte egy tülködet",
   "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.reblog": "{name} megtolta a tülködet",
   "notifications.clear": "Értesítések törlése",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Gyorskereső mező",
   "notifications.column_settings.filter_bar.show": "Mutat",
   "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.push": "Push értesítések",
@@ -344,6 +355,7 @@
   "status.admin_account": "Moderáció megnyitása @{name} felhasználóhoz",
   "status.admin_status": "Tülk megnyitása moderációra",
   "status.block": "@{name} letiltása",
+  "status.bookmark": "Könyvjelzőzés",
   "status.cancel_reblog_private": "Megtolás törlése",
   "status.cannot_reblog": "Ez a tülk nem tolható meg",
   "status.copy": "Link másolása tülkbe",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} megtolta",
   "status.reblogs.empty": "Senki sem tolta még meg ezt a tülköt. Ha valaki megteszi, itt fog megjelenni.",
   "status.redraft": "Törlés és újraírás",
+  "status.remove_bookmark": "Könyvjelző eltávolítása",
   "status.reply": "Válasz",
   "status.replyAll": "Válasz a beszélgetésre",
   "status.report": "@{name} jelentése",
@@ -400,9 +413,11 @@
   "upload_button.label": "Média hozzáadása ({formats})",
   "upload_error.limit": "Túllépted a fájlfeltöltési korlátot.",
   "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.edit": "Szerkesztés",
   "upload_form.undo": "Mégsem",
+  "upload_form.video_description": "Írja le a hallás- vagy látássérültek számára",
   "upload_modal.analyzing_picture": "Kép elemzése…",
   "upload_modal.apply": "Alkalmaz",
   "upload_modal.description_placeholder": "A gyors, barna róka átugrik a lusta kutya fölött",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Előnézet ({ratio})",
   "upload_progress.label": "Feltöltés...",
   "video.close": "Videó bezárása",
+  "video.download": "Fájl letöltése",
   "video.exit_fullscreen": "Kilépés teljes képernyőből",
   "video.expand": "Videó nagyítása",
   "video.fullscreen": "Teljes képernyő",
diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json
index a4c155dfe..1ad3cb0fd 100644
--- a/app/javascript/mastodon/locales/hy.json
+++ b/app/javascript/mastodon/locales/hy.json
@@ -1,32 +1,33 @@
 {
-  "account.add_or_remove_from_list": "Add or Remove from lists",
+  "account.add_or_remove_from_list": "Աւելացնել կամ հեռացնել ցանկերից",
   "account.badges.bot": "Բոտ",
+  "account.badges.group": "Խումբ",
   "account.block": "Արգելափակել @{name}֊ին",
   "account.block_domain": "Թաքցնել ամենը հետեւյալ տիրույթից՝ {domain}",
-  "account.blocked": "Blocked",
-  "account.cancel_follow_request": "Cancel follow request",
+  "account.blocked": "Արգելափակուած է",
+  "account.cancel_follow_request": "չեղարկել հետեւելու հայցը",
   "account.direct": "Direct Message @{name}",
-  "account.domain_blocked": "Domain hidden",
+  "account.domain_blocked": "Թաքցրած տիրոյթ",
   "account.edit_profile": "Խմբագրել անձնական էջը",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "Ցուցադրել անձնական էջում",
   "account.follow": "Հետեւել",
   "account.followers": "Հետեւողներ",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers.empty": "Այս օգտատիրոջը դեռ ոչ մէկ չի հետեւում։",
   "account.follows": "Հետեւում է",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "Այս օգտատէրն դեռ ոչ մէկի չի հետեւում։",
   "account.follows_you": "Հետեւում է քեզ",
   "account.hide_reblogs": "Թաքցնել @{name}֊ի տարածածները",
-  "account.last_status": "Last active",
-  "account.link_verified_on": "Ownership of this link was checked on {date}",
-  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
+  "account.last_status": "Վերջին անգամ ակտիւ էր",
+  "account.link_verified_on": "Սոյն յղման տիրապետումը ստուգուած է՝ {date}֊ին",
+  "account.locked_info": "Սոյն հաշուի գաղտնիութեան մակարդակը նշուած է որպէս՝ փակ։ Հաշուի տէրն ընտրում է, թէ ով կարող է հետեւել իրեն։",
   "account.media": "Մեդիա",
   "account.mention": "Նշել @{name}֊ին",
   "account.moved_to": "{name}֊ը տեղափոխվել է՝",
   "account.mute": "Լռեցնել @{name}֊ին",
   "account.mute_notifications": "Անջատել ծանուցումները @{name}֊ից",
-  "account.muted": "Muted",
-  "account.never_active": "Never",
-  "account.posts": "Գրառումներ",
+  "account.muted": "Լռեցուած",
+  "account.never_active": "Երբեք",
+  "account.posts": "Թութ",
   "account.posts_with_replies": "Toots with replies",
   "account.report": "Բողոքել @{name}֊ից",
   "account.requested": "Հաստատման կարիք ունի։ Սեղմիր՝ հետեւելու հայցը չեղարկելու համար։",
@@ -34,15 +35,16 @@
   "account.show_reblogs": "Ցուցադրել @{name}֊ի տարածածները",
   "account.unblock": "Ապաարգելափակել @{name}֊ին",
   "account.unblock_domain": "Ցուցադրել {domain} թաքցված տիրույթի գրառումները",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "Չցուցադրել անձնական էջում",
   "account.unfollow": "Չհետեւել",
   "account.unmute": "Ապալռեցնել @{name}֊ին",
   "account.unmute_notifications": "Միացնել ծանուցումները @{name}֊ից",
-  "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.",
+  "alert.rate_limited.message": "Փորձէք  որոշ ժամանակ անց՝ {retry_time, time, medium}։",
   "alert.rate_limited.title": "Rate limited",
-  "alert.unexpected.message": "An unexpected error occurred.",
+  "alert.unexpected.message": "Անսպասելի սխալ տեղի ունեցաւ։",
   "alert.unexpected.title": "Վա՜յ",
-  "autosuggest_hashtag.per_week": "{count} per week",
+  "announcement.announcement": "Announcement",
+  "autosuggest_hashtag.per_week": "շաբաթը՝ {count}",
   "boost_modal.combo": "Կարող ես սեղմել {combo}՝ սա հաջորդ անգամ բաց թողնելու համար",
   "bundle_column_error.body": "Այս բաղադրիչը բեռնելու ընթացքում ինչ֊որ բան խափանվեց։",
   "bundle_column_error.retry": "Կրկին փորձել",
@@ -51,10 +53,11 @@
   "bundle_modal_error.message": "Այս բաղադրիչը բեռնելու ընթացքում ինչ֊որ բան խափանվեց։",
   "bundle_modal_error.retry": "Կրկին փորձել",
   "column.blocks": "Արգելափակված օգտատերեր",
+  "column.bookmarks": "Էջանիշեր",
   "column.community": "Տեղական հոսք",
-  "column.direct": "Direct messages",
-  "column.directory": "Browse profiles",
-  "column.domain_blocks": "Hidden domains",
+  "column.direct": "Հասցէագրուած հաղորդագրութիւններ",
+  "column.directory": "Զննել անձնական էջերը",
+  "column.domain_blocks": "Թաքցուած տիրոյթները",
   "column.favourites": "Հավանածներ",
   "column.follow_requests": "Հետեւելու հայցեր",
   "column.home": "Հիմնական",
@@ -78,20 +81,22 @@
   "compose_form.lock_disclaimer": "Քո հաշիվը {locked} չէ։ Յուրաքանչյուր ոք կարող է հետեւել քեզ եւ տեսնել միայն հետեւողների համար նախատեսված գրառումները։",
   "compose_form.lock_disclaimer.lock": "փակ",
   "compose_form.placeholder": "Ի՞նչ կա մտքիդ",
-  "compose_form.poll.add_option": "Add a choice",
-  "compose_form.poll.duration": "Poll duration",
-  "compose_form.poll.option_placeholder": "Choice {number}",
-  "compose_form.poll.remove_option": "Remove this choice",
+  "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": "Change poll to allow multiple choices",
+  "compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
   "compose_form.publish": "Թթել",
   "compose_form.publish_loud": "Թթե՜լ",
-  "compose_form.sensitive.hide": "Mark media as sensitive",
-  "compose_form.sensitive.marked": "Media is marked as sensitive",
-  "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
-  "compose_form.spoiler.marked": "Text is hidden behind warning",
-  "compose_form.spoiler.unmarked": "Text is not hidden",
+  "compose_form.sensitive.hide": "Նշել մեդիան որպէս դիւրազգաց",
+  "compose_form.sensitive.marked": "Մեդիան նշուած է որպէս դիւրազգաց",
+  "compose_form.sensitive.unmarked": "Մեդիան նշուած չէ որպէս դիւրազգաց",
+  "compose_form.spoiler.marked": "Տեքստը թաքցուած է զգուշացման ետեւում",
+  "compose_form.spoiler.unmarked": "Տեքստը թաքցուած չէ",
   "compose_form.spoiler_placeholder": "Գրիր նախազգուշացումդ այստեղ",
   "confirmation_modal.cancel": "Չեղարկել",
-  "confirmations.block.block_and_report": "Block & Report",
+  "confirmations.block.block_and_report": "Արգելափակել եւ բողոքել",
   "confirmations.block.confirm": "Արգելափակել",
   "confirmations.block.message": "Վստա՞հ ես, որ ուզում ես արգելափակել {name}֊ին։",
   "confirmations.delete.confirm": "Ջնջել",
@@ -101,24 +106,24 @@
   "confirmations.domain_block.confirm": "Թաքցնել ամբողջ տիրույթը",
   "confirmations.domain_block.message": "Հաստատ֊հաստա՞տ վստահ ես, որ ուզում ես արգելափակել ամբողջ {domain} տիրույթը։ Սովորաբար մի երկու թիրախավորված արգելափակում կամ լռեցում բավական է ու նախընտրելի։",
   "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.explanation": "Սա թաքցնելու ա իրենց գրառումներն, ինչպէս նաեւ իրենց նշող գրառումներն, բայց իրենք միեւնոյն է կը կարողանան հետեւել ձեզ եւ տեսնել ձեր գրառումները։",
   "confirmations.mute.message": "Վստա՞հ ես, որ ուզում ես {name}֊ին լռեցնել։",
-  "confirmations.redraft.confirm": "Delete & redraft",
+  "confirmations.redraft.confirm": "Ջնջել եւ խմբագրել նորից",
   "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.",
-  "confirmations.reply.confirm": "Reply",
-  "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
+  "confirmations.reply.confirm": "Պատասխանել",
+  "confirmations.reply.message": "Այս պահին պատասխանելը կը չեղարկի ձեր՝ այս պահին անաւարտ հաղորդագրութիւնը։ Համոզուա՞ծ էք։",
   "confirmations.unfollow.confirm": "Ապահետեւել",
   "confirmations.unfollow.message": "Վստա՞հ ես, որ ուզում ես այլեւս չհետեւել {name}֊ին։",
-  "conversation.delete": "Delete conversation",
-  "conversation.mark_as_read": "Mark as read",
-  "conversation.open": "View conversation",
-  "conversation.with": "With {names}",
-  "directory.federated": "From known fediverse",
-  "directory.local": "From {domain} only",
-  "directory.new_arrivals": "New arrivals",
-  "directory.recently_active": "Recently active",
+  "conversation.delete": "Ջնջել խօսակցութիւնը",
+  "conversation.mark_as_read": "Նշել որպէս ընթերցուած",
+  "conversation.open": "Դիտել խօսակցութիւնը",
+  "conversation.with": "{names}֊երի հետ",
+  "directory.federated": "Յայտնի դաշնեզերքից",
+  "directory.local": "{domain} տիրոյթից միայն",
+  "directory.new_arrivals": "Նորութիւններ",
+  "directory.recently_active": "Վերջերս ակտիւ",
   "embed.instructions": "Այս թութը քո կայքում ներդնելու համար կարող ես պատճենել ներքոհիշյալ կոդը։",
   "embed.preview": "Ահա, թե ինչ տեսք կունենա այն՝",
   "emoji_button.activity": "Զբաղմունքներ",
@@ -135,45 +140,46 @@
   "emoji_button.search_results": "Որոնման արդյունքներ",
   "emoji_button.symbols": "Նշաններ",
   "emoji_button.travel": "Ուղեւորություն եւ տեղանքներ",
-  "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.account_timeline": "Այստեղ թթեր չկա՛ն։",
+  "empty_column.account_unavailable": "Անձնական էջը հասանելի չի",
+  "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.domain_blocks": "There are no hidden domains yet.",
-  "empty_column.favourited_statuses": "You don't have any favourite toots yet. When you favourite one, it will show up here.",
-  "empty_column.favourites": "No one has favourited this toot yet. When someone does, they will show up here.",
+  "empty_column.direct": "Դու դեռ չունես ոչ մի հասցէագրուած հաղորդագրութիւն։ Երբ ուղարկես կամ ստանաս որեւէ անձնական նամակ, այն այստեղ կերեւայ։",
+  "empty_column.domain_blocks": "Թաքցուած տիրոյթներ դեռ չկան։",
+  "empty_column.favourited_statuses": "Դու դեռ չունես որեւէ հաւանած թութ։ Երբ հաւանես, դրանք կերեւան այստեղ։",
+  "empty_column.favourites": "Այս թութը ոչ մէկ դեռ չի հաւանել։ Հաւանողները կերեւան այստեղ, երբ նշեն թութը հաւանած։",
   "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
   "empty_column.hashtag": "Այս պիտակով դեռ ոչինչ չկա։",
   "empty_column.home": "Քո հիմնական հոսքը դատա՛րկ է։ Այցելի՛ր {public}ը կամ օգտվիր որոնումից՝ այլ մարդկանց հանդիպելու համար։",
   "empty_column.home.public_timeline": "հրապարակային հոսք",
   "empty_column.list": "Այս ցանկում դեռ ոչինչ չկա։ Երբ ցանկի անդամներից որեւէ մեկը նոր թութ գրի, այն կհայտնվի այստեղ։",
   "empty_column.lists": "You don't have any lists yet. When you create one, it will show up here.",
-  "empty_column.mutes": "You haven't muted any users yet.",
+  "empty_column.mutes": "Առայժմ ոչ ոքի չեք արգելափակել։",
   "empty_column.notifications": "Ոչ մի ծանուցում դեռ չունես։ Բզիր մյուսներին՝ խոսակցությունը սկսելու համար։",
   "empty_column.public": "Այստեղ բան չկա՛։ Հրապարակային մի բան գրիր կամ հետեւիր այլ հանգույցներից էակների՝ այն լցնելու համար։",
   "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.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.",
   "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard",
-  "errors.unexpected_crash.report_issue": "Report issue",
+  "errors.unexpected_crash.report_issue": "Զեկուցել խնդրի մասին",
   "follow_request.authorize": "Վավերացնել",
   "follow_request.reject": "Մերժել",
-  "getting_started.developers": "Developers",
-  "getting_started.directory": "Profile directory",
-  "getting_started.documentation": "Documentation",
+  "getting_started.developers": "Մշակողներ",
+  "getting_started.directory": "Պրոֆիլի տեղադրավայրը",
+  "getting_started.documentation": "Փաստաթղթեր",
   "getting_started.heading": "Ինչպես սկսել",
-  "getting_started.invite": "Invite people",
+  "getting_started.invite": "Հրավիրել մարդկանց",
   "getting_started.open_source_notice": "Մաստոդոնը բաց ելատեքստով ծրագրակազմ է։ Կարող ես ներդրում անել կամ վրեպներ զեկուցել ԳիթՀաբում՝ {github}։",
-  "getting_started.security": "Security",
-  "getting_started.terms": "Terms of service",
-  "hashtag.column_header.tag_mode.all": "and {additional}",
-  "hashtag.column_header.tag_mode.any": "or {additional}",
-  "hashtag.column_header.tag_mode.none": "without {additional}",
+  "getting_started.security": "Հաշվի կարգավորումներ",
+  "getting_started.terms": "Ծառայության պայմանները",
+  "hashtag.column_header.tag_mode.all": "և {additional}",
+  "hashtag.column_header.tag_mode.any": "կամ {additional}",
+  "hashtag.column_header.tag_mode.none": "առանց {additional}",
   "hashtag.column_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_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",
   "home.column_settings.basic": "Հիմնական",
   "home.column_settings.show_reblogs": "Ցուցադրել տարածածները",
@@ -181,25 +187,25 @@
   "intervals.full.days": "{number, plural, one {# day} other {# days}}",
   "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
   "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}",
-  "introduction.federation.action": "Next",
+  "introduction.federation.action": "Հաջորդ",
   "introduction.federation.federated.headline": "Federated",
   "introduction.federation.federated.text": "Public posts from other servers of the fediverse will appear in the federated timeline.",
-  "introduction.federation.home.headline": "Home",
+  "introduction.federation.home.headline": "Հիմնական",
   "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!",
-  "introduction.federation.local.headline": "Local",
+  "introduction.federation.local.headline": "Տեղային",
   "introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
   "introduction.interactions.action": "Finish toot-orial!",
-  "introduction.interactions.favourite.headline": "Favourite",
+  "introduction.interactions.favourite.headline": "Նախընտրելի",
   "introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
-  "introduction.interactions.reblog.headline": "Boost",
+  "introduction.interactions.reblog.headline": "Տարածել",
   "introduction.interactions.reblog.text": "You can share other people's toots with your followers by boosting them.",
-  "introduction.interactions.reply.headline": "Reply",
+  "introduction.interactions.reply.headline": "Պատասխանել",
   "introduction.interactions.reply.text": "You can reply to other people's and your own toots, which will chain them together in a conversation.",
-  "introduction.welcome.action": "Let's go!",
-  "introduction.welcome.headline": "First steps",
+  "introduction.welcome.action": "Գնացի՜նք։",
+  "introduction.welcome.headline": "Առաջին քայլեր",
   "introduction.welcome.text": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.",
   "keyboard_shortcuts.back": "ետ նավարկելու համար",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "արգելափակված օգտատերերի ցանկը բացելու համար",
   "keyboard_shortcuts.boost": "տարածելու համար",
   "keyboard_shortcuts.column": "սյուներից մեկի վրա սեւեռվելու համար",
   "keyboard_shortcuts.compose": "շարադրման տիրույթին սեւեռվելու համար",
@@ -219,26 +225,27 @@
   "keyboard_shortcuts.muted": "to open muted users list",
   "keyboard_shortcuts.my_profile": "to open your profile",
   "keyboard_shortcuts.notifications": "to open notifications column",
-  "keyboard_shortcuts.pinned": "to open pinned toots list",
-  "keyboard_shortcuts.profile": "to open author's profile",
+  "keyboard_shortcuts.open_media": "to open media",
+  "keyboard_shortcuts.pinned": "ամրացուած թթերի ցանկը բացելու համար",
+  "keyboard_shortcuts.profile": "հեղինակի անձնական էջը բացելու համար",
   "keyboard_shortcuts.reply": "պատասխանելու համար",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "հետեւելու հայցերի ցանկը դիտելու համար",
   "keyboard_shortcuts.search": "որոնման դաշտին սեւեռվելու համար",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
-  "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW",
-  "keyboard_shortcuts.toggle_sensitivity": "to show/hide media",
+  "keyboard_shortcuts.start": "«սկսել» սիւնակը բացելու համար",
+  "keyboard_shortcuts.toggle_hidden": "CW֊ի ետեւի տեքստը ցուցադրել֊թաքցնելու համար",
+  "keyboard_shortcuts.toggle_sensitivity": "մեդիան ցուցադրել֊թաքցնելու համար",
   "keyboard_shortcuts.toot": "թարմ թութ սկսելու համար",
   "keyboard_shortcuts.unfocus": "տեքստի/որոնման տիրույթից ապասեւեռվելու համար",
   "keyboard_shortcuts.up": "ցանկով վերեւ շարժվելու համար",
   "lightbox.close": "Փակել",
   "lightbox.next": "Հաջորդ",
   "lightbox.previous": "Նախորդ",
-  "lightbox.view_context": "View context",
+  "lightbox.view_context": "Տեսնել ենթատեքստը",
   "lists.account.add": "Ավելացնել ցանկին",
   "lists.account.remove": "Հանել ցանկից",
   "lists.delete": "Ջնջել ցանկը",
   "lists.edit": "Փոփոխել ցանկը",
-  "lists.edit.submit": "Change title",
+  "lists.edit.submit": "Փոխել վերնագիրը",
   "lists.new.create": "Ավելացնել ցանկ",
   "lists.new.title_placeholder": "Նոր ցանկի վերնագիր",
   "lists.search": "Փնտրել քո հետեւած մարդկանց մեջ",
@@ -247,20 +254,21 @@
   "loading_indicator.label": "Բեռնվում է…",
   "media_gallery.toggle_visible": "Ցուցադրել/թաքցնել",
   "missing_indicator.label": "Չգտնվեց",
-  "missing_indicator.sublabel": "This resource could not be found",
+  "missing_indicator.sublabel": "Պաշարը չի գտնւում",
   "mute_modal.hide_notifications": "Թաքցնե՞լ ցանուցումներն այս օգտատիրոջից։",
-  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.apps": "Դիւրակիր յաւելուածներ",
   "navigation_bar.blocks": "Արգելափակված օգտատերեր",
+  "navigation_bar.bookmarks": "Էջանիշեր",
   "navigation_bar.community_timeline": "Տեղական հոսք",
-  "navigation_bar.compose": "Compose new toot",
-  "navigation_bar.direct": "Direct messages",
-  "navigation_bar.discover": "Discover",
-  "navigation_bar.domain_blocks": "Hidden domains",
+  "navigation_bar.compose": "Գրել նոր թութ",
+  "navigation_bar.direct": "Հասցէագրուած",
+  "navigation_bar.discover": "Բացայայտել",
+  "navigation_bar.domain_blocks": "Թաքցուած տիրոյթներ",
   "navigation_bar.edit_profile": "Խմբագրել անձնական էջը",
   "navigation_bar.favourites": "Հավանածներ",
-  "navigation_bar.filters": "Muted words",
+  "navigation_bar.filters": "Լռեցուած բառեր",
   "navigation_bar.follow_requests": "Հետեւելու հայցեր",
-  "navigation_bar.follows_and_followers": "Follows and followers",
+  "navigation_bar.follows_and_followers": "Հետեւածներ եւ հետեւողներ",
   "navigation_bar.info": "Այս հանգույցի մասին",
   "navigation_bar.keyboard_shortcuts": "Ստեղնաշարի կարճատներ",
   "navigation_bar.lists": "Ցանկեր",
@@ -273,38 +281,41 @@
   "navigation_bar.security": "Անվտանգություն",
   "notification.favourite": "{name} հավանեց թութդ",
   "notification.follow": "{name} սկսեց հետեւել քեզ",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} նշեց քեզ",
-  "notification.poll": "A poll you have voted in has ended",
+  "notification.own_poll": "Հարցումդ աւարտուեց",
+  "notification.poll": "Հարցումը, ուր դու քուէարկել ես, աւարտուեց։",
   "notification.reblog": "{name} տարածեց թութդ",
   "notifications.clear": "Մաքրել ծանուցումները",
   "notifications.clear_confirmation": "Վստա՞հ ես, որ ուզում ես մշտապես մաքրել քո բոլոր ծանուցումները։",
   "notifications.column_settings.alert": "Աշխատատիրույթի ծանուցումներ",
   "notifications.column_settings.favourite": "Հավանածներից՝",
-  "notifications.column_settings.filter_bar.advanced": "Display all categories",
-  "notifications.column_settings.filter_bar.category": "Quick filter bar",
-  "notifications.column_settings.filter_bar.show": "Show",
+  "notifications.column_settings.filter_bar.advanced": "Ցուցադրել բոլոր կատեգորիաները",
+  "notifications.column_settings.filter_bar.category": "Արագ զտման վահանակ",
+  "notifications.column_settings.filter_bar.show": "Ցուցադրել",
   "notifications.column_settings.follow": "Նոր հետեւողներ՝",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "Նշումներ՝",
-  "notifications.column_settings.poll": "Poll results:",
+  "notifications.column_settings.poll": "Հարցման արդիւնքները՝",
   "notifications.column_settings.push": "Հրելու ծանուցումներ",
   "notifications.column_settings.reblog": "Տարածածներից՝",
   "notifications.column_settings.show": "Ցուցադրել սյունում",
   "notifications.column_settings.sound": "Ձայն հանել",
-  "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.group": "{count} notifications",
-  "poll.closed": "Closed",
-  "poll.refresh": "Refresh",
+  "notifications.filter.all": "Բոլորը",
+  "notifications.filter.boosts": "Տարածածները",
+  "notifications.filter.favourites": "Հաւանածները",
+  "notifications.filter.follows": "Հետեւածները",
+  "notifications.filter.mentions": "Նշումները",
+  "notifications.filter.polls": "Հարցման արդիւնքները",
+  "notifications.group": "{count} ծանուցում",
+  "poll.closed": "Փակ",
+  "poll.refresh": "Թարմացնել",
   "poll.total_people": "{count, plural, one {# person} other {# people}}",
   "poll.total_votes": "{count, plural, one {# vote} other {# votes}}",
-  "poll.vote": "Vote",
-  "poll.voted": "You voted for this answer",
-  "poll_button.add_poll": "Add a poll",
-  "poll_button.remove_poll": "Remove poll",
+  "poll.vote": "Քուէարկել",
+  "poll.voted": "Դու քուէարկել ես այս տարբերակի համար",
+  "poll_button.add_poll": "Աւելացնել հարցում",
+  "poll_button.remove_poll": "Հեռացնել հարցումը",
   "privacy.change": "Կարգավորել թթի գաղտնիությունը",
   "privacy.direct.long": "Թթել միայն նշված օգտատերերի համար",
   "privacy.direct.short": "Հասցեագրված",
@@ -314,37 +325,38 @@
   "privacy.public.short": "Հրապարակային",
   "privacy.unlisted.long": "Չթթել հրապարակային հոսքերում",
   "privacy.unlisted.short": "Ծածուկ",
-  "refresh": "Refresh",
-  "regeneration_indicator.label": "Loading…",
-  "regeneration_indicator.sublabel": "Your home feed is being prepared!",
+  "refresh": "Թարմացնել",
+  "regeneration_indicator.label": "Բեռնւում է…",
+  "regeneration_indicator.sublabel": "պատրաստւում է հիմնական հոսքդ",
   "relative_time.days": "{number}օր",
   "relative_time.hours": "{number}ժ",
   "relative_time.just_now": "նոր",
   "relative_time.minutes": "{number}ր",
   "relative_time.seconds": "{number}վ",
   "reply_indicator.cancel": "Չեղարկել",
-  "report.forward": "Forward to {target}",
-  "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+  "report.forward": "Փոխանցել {target}֊ին",
+  "report.forward_hint": "Այս հաշիւ այլ հանգոյցից է։ Ուղարկե՞մ այնտեղ էլ այս բողոքի անոնիմ պատճէնը։",
   "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
   "report.placeholder": "Լրացուցիչ մեկնաբանություններ",
   "report.submit": "Ուղարկել",
   "report.target": "Բողոքել {target}֊ի մասին",
   "search.placeholder": "Փնտրել",
   "search_popout.search_format": "Փնտրելու առաջադեմ ձեւ",
-  "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
+  "search_popout.tips.full_text": "Պարզ տեքստը վերադարձնում է գրառումներդ, հաւանածներդ, տարածածներդ, որտեղ ես նշուած եղել, ինչպէս նաեւ նման օգտանուններ, անուններ եւ պիտակներ։",
   "search_popout.tips.hashtag": "պիտակ",
   "search_popout.tips.status": "թութ",
   "search_popout.tips.text": "Հասարակ տեքստը կվերադարձնի համընկնող անուններ, օգտանուններ ու պիտակներ",
   "search_popout.tips.user": "օգտատեր",
   "search_results.accounts": "Մարդիկ",
-  "search_results.hashtags": "Hashtags",
-  "search_results.statuses": "Toots",
-  "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.",
+  "search_results.hashtags": "Պիտակներ",
+  "search_results.statuses": "Թթեր",
+  "search_results.statuses_fts_disabled": "Այս հանգոյցում միացուած չէ ըստ բովանդակութեան թթեր փնտրելու հնարաւորութիւնը։",
   "search_results.total": "{count, number} {count, plural, one {արդյունք} other {արդյունք}}",
-  "status.admin_account": "Open moderation interface for @{name}",
-  "status.admin_status": "Open this status in the moderation interface",
+  "status.admin_account": "Բացել @{name} օգտատիրոջ մոդերացիայի դիմերէսը։",
+  "status.admin_status": "Բացել այս գրառումը մոդերատորի դիմերէսի մէջ",
   "status.block": "Արգելափակել @{name}֊ին",
-  "status.cancel_reblog_private": "Unboost",
+  "status.bookmark": "Էջանիշ",
+  "status.cancel_reblog_private": "Ապատարածել",
   "status.cannot_reblog": "Այս թութը չի կարող տարածվել",
   "status.copy": "Copy link to status",
   "status.delete": "Ջնջել",
@@ -352,7 +364,7 @@
   "status.direct": "Direct message @{name}",
   "status.embed": "Ներդնել",
   "status.favourite": "Հավանել",
-  "status.filtered": "Filtered",
+  "status.filtered": "Զտված",
   "status.load_more": "Բեռնել ավելին",
   "status.media_hidden": "մեդիաբովանդակությունը թաքցված է",
   "status.mention": "Նշել @{name}֊ին",
@@ -362,12 +374,13 @@
   "status.open": "Ընդարձակել այս թութը",
   "status.pin": "Ամրացնել անձնական էջում",
   "status.pinned": "Pinned toot",
-  "status.read_more": "Read more",
+  "status.read_more": "Կարդալ ավելին",
   "status.reblog": "Տարածել",
   "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} տարածել է",
   "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
   "status.redraft": "Delete & re-draft",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "Պատասխանել",
   "status.replyAll": "Պատասխանել թելին",
   "status.report": "Բողոքել @{name}֊ից",
@@ -378,7 +391,7 @@
   "status.show_more": "Ավելին",
   "status.show_more_all": "Show more for all",
   "status.show_thread": "Show thread",
-  "status.uncached_media_warning": "Not available",
+  "status.uncached_media_warning": "Անհասանելի",
   "status.unmute_conversation": "Ապալռեցնել խոսակցությունը",
   "status.unpin": "Հանել անձնական էջից",
   "suggestions.dismiss": "Dismiss suggestion",
@@ -389,29 +402,32 @@
   "tabs_bar.notifications": "Ծանուցումներ",
   "tabs_bar.search": "Փնտրել",
   "time_remaining.days": "{number, plural, one {# day} other {# days}} left",
-  "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left",
-  "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left",
+  "time_remaining.hours": "{number, plural, one {# ժամ} other {# ժամ}} անց",
+  "time_remaining.minutes": "{number, plural, one {# րոպե} other {# րոպե}} անց",
   "time_remaining.moments": "Moments remaining",
-  "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left",
+  "time_remaining.seconds": "{number, plural, one {# վայրկյան} other {# վայրկյան}} անց",
   "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking",
-  "trends.trending_now": "Trending now",
+  "trends.trending_now": "Այժմ արդիական",
   "ui.beforeunload": "Քո սեւագիրը կկորի, եթե լքես Մաստոդոնը։",
   "upload_area.title": "Քաշիր ու նետիր՝ վերբեռնելու համար",
   "upload_button.label": "Ավելացնել մեդիա",
-  "upload_error.limit": "File upload limit exceeded.",
+  "upload_error.limit": "Ֆայլի վերբեռնման սահմանաչափը գերազանցված է։",
   "upload_error.poll": "File upload not allowed with polls.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "Նկարագրություն ավելացրու տեսողական խնդիրներ ունեցողների համար",
-  "upload_form.edit": "Edit",
+  "upload_form.edit": "Խմբագրել",
   "upload_form.undo": "Հետարկել",
-  "upload_modal.analyzing_picture": "Analyzing picture…",
-  "upload_modal.apply": "Apply",
-  "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_form.video_description": "Describe for people with hearing loss or visual impairment",
+  "upload_modal.analyzing_picture": "Լուսանկարի վերլուծում…",
+  "upload_modal.apply": "Կիրառել",
+  "upload_modal.description_placeholder": "Ճկուն շագանակագույն աղվեսը ցատկում է ծույլ շան վրայով",
+  "upload_modal.detect_text": "Հայտնբերել տեքստը նկարից",
+  "upload_modal.edit_media": "Խմբագրել մեդիան",
   "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.",
-  "upload_modal.preview_label": "Preview ({ratio})",
+  "upload_modal.preview_label": "Նախադիտում ({ratio})",
   "upload_progress.label": "Վերբեռնվում է…",
   "video.close": "Փակել  տեսագրությունը",
+  "video.download": "Ներբեռնել ֆայլը",
   "video.exit_fullscreen": "Անջատել լիաէկրան դիտումը",
   "video.expand": "Ընդարձակել տեսագրությունը",
   "video.fullscreen": "Լիաէկրան",
diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json
index 167c2a766..b1605ac60 100644
--- a/app/javascript/mastodon/locales/id.json
+++ b/app/javascript/mastodon/locales/id.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Tambah atau Hapus dari daftar",
   "account.badges.bot": "Bot",
+  "account.badges.group": "Grup",
   "account.block": "Blokir @{name}",
   "account.block_domain": "Sembunyikan segalanya dari {domain}",
   "account.blocked": "Terblokir",
@@ -39,9 +40,10 @@
   "account.unmute": "Berhenti membisukan @{name}",
   "account.unmute_notifications": "Munculkan notifikasi dari @{name}",
   "alert.rate_limited.message": "Tolong ulangi setelah {retry_time, time, medium}.",
-  "alert.rate_limited.title": "Rate limited",
+  "alert.rate_limited.title": "Batasan tingkat",
   "alert.unexpected.message": "Terjadi kesalahan yang tidak terduga.",
   "alert.unexpected.title": "Oops!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} per minggu",
   "boost_modal.combo": "Anda dapat menekan {combo} untuk melewati ini",
   "bundle_column_error.body": "Kesalahan terjadi saat memuat komponen ini.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Kesalahan terjadi saat memuat komponen ini.",
   "bundle_modal_error.retry": "Coba lagi",
   "column.blocks": "Pengguna diblokir",
+  "column.bookmarks": "Markah",
   "column.community": "Linimasa Lokal",
   "column.direct": "Pesan langsung",
   "column.directory": "Jelajahi profil",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Durasi jajak pendapat",
   "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",
+  "compose_form.poll.switch_to_single": "Ubah japat menjadi pilihan tunggal",
   "compose_form.publish": "Toot",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Tandai sebagai media sensitif",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Tidak ada toot di sini!",
   "empty_column.account_unavailable": "Profil tidak tersedia",
   "empty_column.blocks": "Anda belum memblokir siapapun.",
+  "empty_column.bookmarked_statuses": "Anda belum memiliki toot termarkah. Saat Anda menandainya sebagai markah, ia 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.domain_blocks": "Tidak ada topik tersembunyi.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "buka daftar pengguna terbisukan",
   "keyboard_shortcuts.my_profile": "buka profil Anda",
   "keyboard_shortcuts.notifications": "buka kolom notifikasi",
+  "keyboard_shortcuts.open_media": "membuka media",
   "keyboard_shortcuts.pinned": "buka daftar toot tersemat",
   "keyboard_shortcuts.profile": "buka profil pencipta",
   "keyboard_shortcuts.reply": "balas",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Sembunyikan notifikasi dari pengguna ini?",
   "navigation_bar.apps": "Aplikasi mobile",
   "navigation_bar.blocks": "Pengguna diblokir",
+  "navigation_bar.bookmarks": "Markah",
   "navigation_bar.community_timeline": "Linimasa lokal",
   "navigation_bar.compose": "Tulis toot baru",
   "navigation_bar.direct": "Pesan langsung",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Keamanan",
   "notification.favourite": "{name} menyukai status anda",
   "notification.follow": "{name} mengikuti anda",
+  "notification.follow_request": "{name} ingin mengikuti Anda",
   "notification.mention": "{name} menyebut Anda",
+  "notification.own_poll": "Japat Anda telah berakhir",
   "notification.poll": "Japat yang Anda ikuti telah berakhir",
   "notification.reblog": "{name} mem-boost status anda",
   "notifications.clear": "Hapus notifikasi",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Bilah penyaring cepat",
   "notifications.column_settings.filter_bar.show": "Tampilkan",
   "notifications.column_settings.follow": "Pengikut baru:",
+  "notifications.column_settings.follow_request": "Permintaan mengikuti baru:",
   "notifications.column_settings.mention": "Balasan:",
   "notifications.column_settings.poll": "Hasil japat:",
   "notifications.column_settings.push": "Notifikasi dorong",
@@ -344,6 +355,7 @@
   "status.admin_account": "Buka antar muka moderasi untuk @{name}",
   "status.admin_status": "Buka status ini dalam antar muka moderasi",
   "status.block": "Blokir @{name}",
+  "status.bookmark": "Markah",
   "status.cancel_reblog_private": "Batalkan boost",
   "status.cannot_reblog": "Pos ini tak dapat di-boost",
   "status.copy": "Salin tautan ke status",
@@ -368,15 +380,16 @@
   "status.reblogged_by": "di-boost {name}",
   "status.reblogs.empty": "Belum ada yang mem-boost toot ini. Ketika seseorang melakukannya, maka akan muncul di sini.",
   "status.redraft": "Hapus & redraf",
+  "status.remove_bookmark": "Hapus markah",
   "status.reply": "Balas",
   "status.replyAll": "Balas ke semua",
   "status.report": "Laporkan @{name}",
   "status.sensitive_warning": "Konten sensitif",
   "status.share": "Bagikan",
   "status.show_less": "Tampilkan lebih sedikit",
-  "status.show_less_all": "Show less for all",
+  "status.show_less_all": "Tampilkan lebih sedikit",
   "status.show_more": "Tampilkan semua",
-  "status.show_more_all": "Show more for all",
+  "status.show_more_all": "Tampilkan lebih banyak",
   "status.show_thread": "Tampilkan utas",
   "status.uncached_media_warning": "Tak tersedia",
   "status.unmute_conversation": "Bunyikan percakapan",
@@ -400,9 +413,11 @@
   "upload_button.label": "Tambahkan media",
   "upload_error.limit": "Batas unggah berkas terlampaui.",
   "upload_error.poll": "Unggah berkas tak diizinkan di japat ini.",
+  "upload_form.audio_description": "Penjelasan untuk orang dengan gangguan pendengaran",
   "upload_form.description": "Deskripsikan untuk mereka yang tidak bisa melihat dengan jelas",
   "upload_form.edit": "Sunting",
   "upload_form.undo": "Undo",
+  "upload_form.video_description": "Penjelasan untuk orang dengan gangguan pendengaran atau penglihatan",
   "upload_modal.analyzing_picture": "Analisis gambar…",
   "upload_modal.apply": "Terapkan",
   "upload_modal.description_placeholder": "Muharjo seorang xenofobia universal yang takut pada warga jazirah, contohnya Qatar",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Pratinjau ({ratio})",
   "upload_progress.label": "Mengunggah...",
   "video.close": "Tutup video",
+  "video.download": "Unduh berkas",
   "video.exit_fullscreen": "Keluar dari layar penuh",
   "video.expand": "Perbesar video",
   "video.fullscreen": "Layar penuh",
diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json
index 076835218..36bf1a588 100644
--- a/app/javascript/mastodon/locales/io.json
+++ b/app/javascript/mastodon/locales/io.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Add or Remove from lists",
   "account.badges.bot": "Bot",
+  "account.badges.group": "Group",
   "account.block": "Blokusar @{name}",
   "account.block_domain": "Hide everything from {domain}",
   "account.blocked": "Blocked",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Rate limited",
   "alert.unexpected.message": "An unexpected error occurred.",
   "alert.unexpected.title": "Oops!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} per week",
   "boost_modal.combo": "Tu povas presar sur {combo} por omisar co en la venonta foyo",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Something went wrong while loading this component.",
   "bundle_modal_error.retry": "Try again",
   "column.blocks": "Blokusita uzeri",
+  "column.bookmarks": "Bookmarks",
   "column.community": "Lokala tempolineo",
   "column.direct": "Direct messages",
   "column.directory": "Browse profiles",
@@ -82,6 +85,8 @@
   "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": "Siflar",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Mark media as sensitive",
@@ -138,6 +143,7 @@
   "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": "La lokala tempolineo esas vakua. Skribez ulo publike por iniciar la agiveso!",
   "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no hidden domains yet.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "to open muted users list",
   "keyboard_shortcuts.my_profile": "to open your profile",
   "keyboard_shortcuts.notifications": "to open notifications column",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "to open pinned toots list",
   "keyboard_shortcuts.profile": "to open author's profile",
   "keyboard_shortcuts.reply": "to reply",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blokusita uzeri",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Lokala tempolineo",
   "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Security",
   "notification.favourite": "{name} favorizis tua mesajo",
   "notification.follow": "{name} sequeskis tu",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} mencionis tu",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "A poll you have voted in has ended",
   "notification.reblog": "{name} repetis tua mesajo",
   "notifications.clear": "Efacar savigi",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Quick filter bar",
   "notifications.column_settings.filter_bar.show": "Show",
   "notifications.column_settings.follow": "Nova sequanti:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "Mencioni:",
   "notifications.column_settings.poll": "Poll results:",
   "notifications.column_settings.push": "Push notifications",
@@ -344,6 +355,7 @@
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_status": "Open this status in the moderation interface",
   "status.block": "Block @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.copy": "Copy link to status",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} repetita",
   "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
   "status.redraft": "Delete & re-draft",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "Respondar",
   "status.replyAll": "Respondar a filo",
   "status.report": "Denuncar @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Adjuntar kontenajo",
   "upload_error.limit": "File upload limit exceeded.",
   "upload_error.poll": "File upload not allowed with polls.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "Describe for the visually impaired",
   "upload_form.edit": "Edit",
   "upload_form.undo": "Desfacar",
+  "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.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "Kargante...",
   "video.close": "Close video",
+  "video.download": "Download file",
   "video.exit_fullscreen": "Exit full screen",
   "video.expand": "Expand video",
   "video.fullscreen": "Full screen",
diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json
new file mode 100644
index 000000000..53b2a150a
--- /dev/null
+++ b/app/javascript/mastodon/locales/is.json
@@ -0,0 +1,439 @@
+{
+  "account.add_or_remove_from_list": "Bæta á eða fjarlægja af listum",
+  "account.badges.bot": "Róbót",
+  "account.badges.group": "Hópur",
+  "account.block": "Útiloka @{name}",
+  "account.block_domain": "Fela allt frá {domain}",
+  "account.blocked": "Útilokaður",
+  "account.cancel_follow_request": "Hætta við beiðni um að fylgjast með",
+  "account.direct": "Bein skilaboð til @{name}",
+  "account.domain_blocked": "Lén falið",
+  "account.edit_profile": "Breyta notandasniði",
+  "account.endorse": "Birta á notandasniði",
+  "account.follow": "Fylgjast með",
+  "account.followers": "Fylgjendur",
+  "account.followers.empty": "Ennþá fylgist enginn með þessum notanda.",
+  "account.follows": "Fylgist með",
+  "account.follows.empty": "Þessi notandi fylgist ennþá ekki með neinum.",
+  "account.follows_you": "Fylgir þér",
+  "account.hide_reblogs": "Fela endurbirtingar fyrir @{name}",
+  "account.last_status": "Síðasta virkni",
+  "account.link_verified_on": "Eignarhald á þessum tengli var athugað þann {date}",
+  "account.locked_info": "Staða gagnaleyndar á þessum aðgangi er stillt á læsingu. Eigandinn yfirfer handvirkt hverjir geti fylgst með honum.",
+  "account.media": "Myndskrár",
+  "account.mention": "Minnast á @{name}",
+  "account.moved_to": "{name} hefur verið færður til:",
+  "account.mute": "Þagga niður í @{name}",
+  "account.mute_notifications": "Þagga tilkynningar frá @{name}",
+  "account.muted": "Þaggað",
+  "account.never_active": "Aldrei",
+  "account.posts": "Tíst",
+  "account.posts_with_replies": "Tíst og svör",
+  "account.report": "Kæra @{name}",
+  "account.requested": "Bíður eftir samþykki. Smelltu til að hætta við beiðni um að fylgjast með",
+  "account.share": "Deila notandasniði fyrir @{name}",
+  "account.show_reblogs": "Sýna endurbirtingar frá @{name}",
+  "account.unblock": "Aflétta útilokun af @{name}",
+  "account.unblock_domain": "Hætta að fela {domain}",
+  "account.unendorse": "Ekki birta á notandasniði",
+  "account.unfollow": "Hætta að fylgja",
+  "account.unmute": "Hætta að þagga niður í @{name}",
+  "account.unmute_notifications": "Hætta að þagga tilkynningar frá @{name}",
+  "alert.rate_limited.message": "Prófaðu aftur eftir {retry_time, time, medium}.",
+  "alert.rate_limited.title": "Með takmörkum",
+  "alert.unexpected.message": "Upp kom óvænt villa.",
+  "alert.unexpected.title": "Úbbs!",
+  "announcement.announcement": "Announcement",
+  "autosuggest_hashtag.per_week": "{count} á viku",
+  "boost_modal.combo": "Þú getur ýtt á {combo} til að sleppa þessu næst",
+  "bundle_column_error.body": "Eitthvað fór úrskeiðis við að hlaða inn þessari einingu.",
+  "bundle_column_error.retry": "Reyndu aftur",
+  "bundle_column_error.title": "Villa í netkerfi",
+  "bundle_modal_error.close": "Loka",
+  "bundle_modal_error.message": "Eitthvað fór úrskeiðis við að hlaða inn þessari einingu.",
+  "bundle_modal_error.retry": "Reyndu aftur",
+  "column.blocks": "Útilokaðir notendur",
+  "column.bookmarks": "Bókamerki",
+  "column.community": "Staðvær tímalína",
+  "column.direct": "Bein skilaboð",
+  "column.directory": "Skoða notandasnið",
+  "column.domain_blocks": "Falin lén",
+  "column.favourites": "Eftirlæti",
+  "column.follow_requests": "Fylgja beiðnum",
+  "column.home": "Heim",
+  "column.lists": "Listar",
+  "column.mutes": "Þaggaðir notendur",
+  "column.notifications": "Tilkynningar",
+  "column.pins": "Föst tíst",
+  "column.public": "Sameiginleg tímalína",
+  "column_back_button.label": "Til baka",
+  "column_header.hide_settings": "Fela stillingar",
+  "column_header.moveLeft_settings": "Færa dálk til vinstri",
+  "column_header.moveRight_settings": "Færa dálk til hægri",
+  "column_header.pin": "Festa",
+  "column_header.show_settings": "Birta stillingar",
+  "column_header.unpin": "Losa",
+  "column_subheading.settings": "Stillingar",
+  "community.column_settings.media_only": "Einungis myndskrár",
+  "compose_form.direct_message_warning": "Þetta tíst verður aðeins sent á notendur sem minnst er á.",
+  "compose_form.direct_message_warning_learn_more": "Kanna nánar",
+  "compose_form.hashtag_warning": "Þetta tíst verður ekki talið með undir nokkru myllumerki þar sem það er óskráð. Einungis er hægt að leita að opinberum tístum eftir myllumerkjum.",
+  "compose_form.lock_disclaimer": "Aðgangurinn þinn er ekki {locked}. Hver sem er getur fylgst með þeim færslum þínum sem einungis eru til fylgjenda þinna.",
+  "compose_form.lock_disclaimer.lock": "læst",
+  "compose_form.placeholder": "Hvað varstu að hugsa?",
+  "compose_form.poll.add_option": "Bæta við valkosti",
+  "compose_form.poll.duration": "Tímalengd könnunar",
+  "compose_form.poll.option_placeholder": "Valkostur {number}",
+  "compose_form.poll.remove_option": "Fjarlægja þennan valkost",
+  "compose_form.poll.switch_to_multiple": "Breyta könnun svo hægt sé að hafa marga valkosti",
+  "compose_form.poll.switch_to_single": "Breyta könnun svo hægt sé að hafa einn stakan valkost",
+  "compose_form.publish": "Tíst",
+  "compose_form.publish_loud": "{publish}!",
+  "compose_form.sensitive.hide": "Merkja myndir sem viðkvæmar",
+  "compose_form.sensitive.marked": "Mynd er merkt sem viðkvæm",
+  "compose_form.sensitive.unmarked": "Mynd er ekki merkt sem viðkvæm",
+  "compose_form.spoiler.marked": "Texti er falinn á bak við aðvörun",
+  "compose_form.spoiler.unmarked": "Texti er ekki falinn",
+  "compose_form.spoiler_placeholder": "Skrifaðu aðvörunina þína hér",
+  "confirmation_modal.cancel": "Hætta við",
+  "confirmations.block.block_and_report": "Útiloka og kæra",
+  "confirmations.block.confirm": "Útiloka",
+  "confirmations.block.message": "Ertu viss um að þú viljir loka á {name}?",
+  "confirmations.delete.confirm": "Eyða",
+  "confirmations.delete.message": "Ertu viss um að þú viljir eyða þessari stöðufærslu?",
+  "confirmations.delete_list.confirm": "Eyða",
+  "confirmations.delete_list.message": "Ertu viss um að þú viljir eyða þessum lista endanlega?",
+  "confirmations.domain_block.confirm": "Fela 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.logout.confirm": "Skrá út",
+  "confirmations.logout.message": "Ertu viss um að þú viljir skrá þig út?",
+  "confirmations.mute.confirm": "Þagga",
+  "confirmations.mute.explanation": "Þetta mun fela færslur frá þeim og þær færslur þar sem minnst er á þau, en það mun samt sem áður gera þeim kleift að sjá færslurnar þínar og að fylgjast með þér.",
+  "confirmations.mute.message": "Ertu viss um að þú viljir þagga niður í {name}?",
+  "confirmations.redraft.confirm": "Eyða og enduvinna drög",
+  "confirmations.redraft.message": "Ertu viss um að þú viljir eyða þessari stöðufærslu og enduvinna drögin? Eftirlæti og endurbirtingar munu tapast og svör við upprunalegu fæerslunni munu verða munaðarlaus.",
+  "confirmations.reply.confirm": "Svara",
+  "confirmations.reply.message": "Ef þú svarar núna verður skrifað yfir skilaboðin sem þú ert að semja núna. Ertu viss um að þú viljir halda áfram?",
+  "confirmations.unfollow.confirm": "Hætta að fylgja",
+  "confirmations.unfollow.message": "Ertu viss um að þú viljir hætta að fylgjast með {name}?",
+  "conversation.delete": "Eyða samtali",
+  "conversation.mark_as_read": "Merkja sem lesið",
+  "conversation.open": "Skoða samtal",
+  "conversation.with": "Með {names}",
+  "directory.federated": "Frá samtengdum vefþjónum",
+  "directory.local": "Einungis frá {domain}",
+  "directory.new_arrivals": "Nýkomnir",
+  "directory.recently_active": "Nýleg virkni",
+  "embed.instructions": "Felldu þessa færslu inn í vefsvæðið þitt með því að afrita kóðann hér fyrir neðan.",
+  "embed.preview": "Svona mun þetta líta út:",
+  "emoji_button.activity": "Virkni",
+  "emoji_button.custom": "Sérsniðið",
+  "emoji_button.flags": "Flögg",
+  "emoji_button.food": "Matur og drykkur",
+  "emoji_button.label": "Setja inn tjáningartákn",
+  "emoji_button.nature": "Náttúra",
+  "emoji_button.not_found": "Engin tjáningartákn!! (╯°□°)╯︵ ┻━┻",
+  "emoji_button.objects": "Hlutir",
+  "emoji_button.people": "Fólk",
+  "emoji_button.recent": "Oft notað",
+  "emoji_button.search": "Leita...",
+  "emoji_button.search_results": "Leitarniðurstöður",
+  "emoji_button.symbols": "Tákn",
+  "emoji_button.travel": "Ferðalög og staðir",
+  "empty_column.account_timeline": "Engin tíst hér!",
+  "empty_column.account_unavailable": "Notandasnið ekki tiltækt",
+  "empty_column.blocks": "Þú hefur ekki ennþá útilokað neina notendur.",
+  "empty_column.bookmarked_statuses": "Þú ert ekki ennþá með nein bókamerkt tíst. Þegar þú gefur tísti bókamerki, munu þ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.domain_blocks": "Það eru engin falin lén ennþá.",
+  "empty_column.favourited_statuses": "Þú átt ennþá engin eftirlætistíst. Þegar þú setur tíst í eftirlæti, munu þau birtast hér.",
+  "empty_column.favourites": "Enginn hefur ennþá set þetta tíst í eftirlæti. Þegar einhverjir gera það, munu þeir birtast hér.",
+  "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.hashtag": "Það er ekkert ennþá undir þessu myllumerki.",
+  "empty_column.home": "Heimatímalínan þín er tóm! Skoðaðu {public} eða notaðu leitina til að komast í ganf og finna annað fólk.",
+  "empty_column.home.public_timeline": "opinbera tímalínan",
+  "empty_column.list": "Það er ennþá ekki neitt á þessum lista. Þegar meðlimir á listanum senda inn nýjar stöðufærslur, munu þær birtast hér.",
+  "empty_column.lists": "Þú ert ennþá ekki með neina lista. Þegar þú byrð til einhvern lista, munu hann birtast hér.",
+  "empty_column.mutes": "Þú hefur ekki þaggað niður í neinum notendum ennþá.",
+  "empty_column.notifications": "Þú ert ekki ennþá með neinar tilkynningar. Vertu í samskiptum við aðra til að umræður fari af stað.",
+  "empty_column.public": "Það er ekkert hér! Skrifaðu eitthvað opinberlega, eða fylgstu með notendum á öðrum netþjónum til að fylla upp í þetta",
+  "error.unexpected_crash.explanation": "Vegna villu í kóðanum okkar eða samhæfnivandamála í vafra er ekki hægt að birta þessa síðu svo vel sé.",
+  "error.unexpected_crash.next_steps": "Prófaðu að endurlesa síðuna. Ef það hjálpar ekki til, má samt vera að þú getir notað Mastodon í gegnum annan vafra eða forrit.",
+  "errors.unexpected_crash.copy_stacktrace": "Afrita rakningarupplýsingar (stacktrace) á klippispjald",
+  "errors.unexpected_crash.report_issue": "Tilkynna vandamál",
+  "follow_request.authorize": "Heimila",
+  "follow_request.reject": "Hafna",
+  "getting_started.developers": "Forritarar",
+  "getting_started.directory": "Notandasniðamappa",
+  "getting_started.documentation": "Hjálparskjöl",
+  "getting_started.heading": "Komast í gang",
+  "getting_started.invite": "Bjóða fólki",
+  "getting_started.open_source_notice": "Mastodon er opinn og frjáls hugbúnaður. Þú getur lagt þitt af mörkum eða tilkynnt um vandamál á GitHub á slóðinni {github}.",
+  "getting_started.security": "Stillingar notandaaðgangs",
+  "getting_started.terms": "Þjónustuskilmálar",
+  "hashtag.column_header.tag_mode.all": "og {additional}",
+  "hashtag.column_header.tag_mode.any": "eða {additional}",
+  "hashtag.column_header.tag_mode.none": "án {additional}",
+  "hashtag.column_settings.select.no_options_message": "Engar tillögur fundust",
+  "hashtag.column_settings.select.placeholder": "Settu inn myllumerki…",
+  "hashtag.column_settings.tag_mode.all": "Allt þetta",
+  "hashtag.column_settings.tag_mode.any": "Hvað sem er af þessu",
+  "hashtag.column_settings.tag_mode.none": "Ekkert af þessu",
+  "hashtag.column_settings.tag_toggle": "Taka með viðbótarmerki fyrir þennan dálk",
+  "home.column_settings.basic": "Einfalt",
+  "home.column_settings.show_reblogs": "Sýna endurbirtingar",
+  "home.column_settings.show_replies": "Birta svör",
+  "intervals.full.days": "{number, plural, one {# dagur} other {# dagar}}",
+  "intervals.full.hours": "{number, plural, one {# klukkustund} other {# klukkustundir}}",
+  "intervals.full.minutes": "{number, plural, one {# mínúta} other {# mínútur}}",
+  "introduction.federation.action": "Næsta",
+  "introduction.federation.federated.headline": "Sameiginlegt",
+  "introduction.federation.federated.text": "Opinberar færslur frá öðrum vefþjónum í skýjasambandinu birtast á sameiginlegu tímalínunni.",
+  "introduction.federation.home.headline": "Heim",
+  "introduction.federation.home.text": "Færslur frá fólki sem þú fylgist með birtast í heimastreyminu þínu. Þú getur fylgst með hverjum sem er á hvaða vefþjóni sem er!",
+  "introduction.federation.local.headline": "Staðvært",
+  "introduction.federation.local.text": "Opinberar færslur frá fólki á sama vefþjóni og þú birtast á staðværu tímalínunni.",
+  "introduction.interactions.action": "Ljúka leiðarvísinum!",
+  "introduction.interactions.favourite.headline": "Eftirlæti",
+  "introduction.interactions.favourite.text": "Þú getur vistað tíst til að eiga í handraðanum og látið höfundinn vita að þér hafi líkað það með því að setja það í eftirlætin þín.",
+  "introduction.interactions.reblog.headline": "Endurbirting",
+  "introduction.interactions.reblog.text": "Þú getur deilt tístum frá öðru fólki til þeirra sem fylgjast með þér með því að endurbirta færslurnar.",
+  "introduction.interactions.reply.headline": "Svara",
+  "introduction.interactions.reply.text": "Þú getur svarað tístum frá öðru fólki og eins sjálfum þér, sem mun binda tístin saman í samtal.",
+  "introduction.welcome.action": "Drífum okkur!",
+  "introduction.welcome.headline": "Fyrstu skrefin",
+  "introduction.welcome.text": "Velkomin í skýjasambandið (fediverse)! Eftir augnablik geturðu farið að útvarpa skilaboðum og spjalla við vini þína úti um víða veröld, allt í gegnum ýmsar gerðir vefþjóna sem samt geta talað saman. En þessi vefþjónn, {domain}, er sérstakur — hann hýsir notandasniðið þitt, þannig að þú skalt muna hvað hann heitir.",
+  "keyboard_shortcuts.back": "að fara til baka",
+  "keyboard_shortcuts.blocked": "að opna lista yfir útilokaða notendur",
+  "keyboard_shortcuts.boost": "að endurbirta",
+  "keyboard_shortcuts.column": "að setja virkni á stöðufærslu í einum af dálkunum",
+  "keyboard_shortcuts.compose": "að setja virkni á textainnsetningarreit",
+  "keyboard_shortcuts.description": "Lýsing",
+  "keyboard_shortcuts.direct": "að opna dálk með beinum skilaboðum",
+  "keyboard_shortcuts.down": "að fara neðar í listanum",
+  "keyboard_shortcuts.enter": "að opna stöðufærslu",
+  "keyboard_shortcuts.favourite": "að setja í eftirlæti",
+  "keyboard_shortcuts.favourites": "að opna eftirlætislista",
+  "keyboard_shortcuts.federated": "að opna sameiginlega tímalínu",
+  "keyboard_shortcuts.heading": "Flýtileiðir á lyklaborði",
+  "keyboard_shortcuts.home": "að opna heimatímalínu",
+  "keyboard_shortcuts.hotkey": "Flýtilykill",
+  "keyboard_shortcuts.legend": "að birta þessa skýringu",
+  "keyboard_shortcuts.local": "að opna staðværa tímalínu",
+  "keyboard_shortcuts.mention": "að minnast á höfund",
+  "keyboard_shortcuts.muted": "að opna lista yfir þaggaða notendur",
+  "keyboard_shortcuts.my_profile": "að opna notandasniðið þitt",
+  "keyboard_shortcuts.notifications": "að opna tilkynningadálk",
+  "keyboard_shortcuts.open_media": "til að opna margmiðlunargögn",
+  "keyboard_shortcuts.pinned": "að opna lista yfir föst tíst",
+  "keyboard_shortcuts.profile": "að opna notandasnið höfundar",
+  "keyboard_shortcuts.reply": "að svara",
+  "keyboard_shortcuts.requests": "að opna lista yfir fylgjendabeiðnir",
+  "keyboard_shortcuts.search": "að setja virkni í leit",
+  "keyboard_shortcuts.start": "að opna \"komast í gang\" dálk",
+  "keyboard_shortcuts.toggle_hidden": "að birta/fela texta á bak við aðvörun vegna efnis",
+  "keyboard_shortcuts.toggle_sensitivity": "að birta/fela myndir",
+  "keyboard_shortcuts.toot": "að byrja glænýtt tíst",
+  "keyboard_shortcuts.unfocus": "að taka virkni úr textainnsetningarreit eða leit",
+  "keyboard_shortcuts.up": "að fara ofar í listanum",
+  "lightbox.close": "Loka",
+  "lightbox.next": "Næsta",
+  "lightbox.previous": "Fyrra",
+  "lightbox.view_context": "Skoða samhengi",
+  "lists.account.add": "Bæta á lista",
+  "lists.account.remove": "Fjarlægja af lista",
+  "lists.delete": "Eyða lista",
+  "lists.edit": "Breyta lista",
+  "lists.edit.submit": "Breyta titli",
+  "lists.new.create": "Bæta við lista",
+  "lists.new.title_placeholder": "Titill á nýjum lista",
+  "lists.search": "Leita meðal þeirra sem þú fylgist með",
+  "lists.subheading": "Listarnir þínir",
+  "load_pending": "{count, plural, one {# nýtt atriði} other {# ný atriði}}",
+  "loading_indicator.label": "Hleð inn...",
+  "media_gallery.toggle_visible": "Víxla sýnileika",
+  "missing_indicator.label": "Fannst ekki",
+  "missing_indicator.sublabel": "Tilfangið fannst ekki",
+  "mute_modal.hide_notifications": "Fela tilkynningar frá þessum notanda?",
+  "navigation_bar.apps": "Farsímaforrit",
+  "navigation_bar.blocks": "Útilokaðir notendur",
+  "navigation_bar.bookmarks": "Bókamerki",
+  "navigation_bar.community_timeline": "Staðvær tímalína",
+  "navigation_bar.compose": "Semja nýtt tíst",
+  "navigation_bar.direct": "Bein skilaboð",
+  "navigation_bar.discover": "Uppgötva",
+  "navigation_bar.domain_blocks": "Falin lén",
+  "navigation_bar.edit_profile": "Breyta notandasniði",
+  "navigation_bar.favourites": "Eftirlæti",
+  "navigation_bar.filters": "Þögguð orð",
+  "navigation_bar.follow_requests": "Beiðnir um að fylgjast með",
+  "navigation_bar.follows_and_followers": "Fylgist með og fylgjendur",
+  "navigation_bar.info": "Um þennan vefþjón",
+  "navigation_bar.keyboard_shortcuts": "Flýtilyklar",
+  "navigation_bar.lists": "Listar",
+  "navigation_bar.logout": "Útskráning",
+  "navigation_bar.mutes": "Þaggaðir notendur",
+  "navigation_bar.personal": "Einka",
+  "navigation_bar.pins": "Föst tíst",
+  "navigation_bar.preferences": "Kjörstillingar",
+  "navigation_bar.public_timeline": "Sameiginleg tímalína",
+  "navigation_bar.security": "Öryggi",
+  "notification.favourite": "{name} setti stöðufærslu þína í eftirlæti",
+  "notification.follow": "{name} fylgist með þér",
+  "notification.follow_request": "{name} hefur beðið um að fylgjast með þér",
+  "notification.mention": "{name} minntist á þig",
+  "notification.own_poll": "Könnuninni þinni er lokið",
+  "notification.poll": "Könnun sem þú tókst þátt í er lokið",
+  "notification.reblog": "{name} endurbirti stöðufærsluna þína",
+  "notifications.clear": "Hreinsa tilkynningar",
+  "notifications.clear_confirmation": "Ertu viss um að þú viljir endanlega eyða öllum tilkynningunum þínum?",
+  "notifications.column_settings.alert": "Tilkynningar á skjáborði",
+  "notifications.column_settings.favourite": "Eftirlæti:",
+  "notifications.column_settings.filter_bar.advanced": "Birta alla flokka",
+  "notifications.column_settings.filter_bar.category": "Skyndisíustika",
+  "notifications.column_settings.filter_bar.show": "Sýna",
+  "notifications.column_settings.follow": "Nýir fylgjendur:",
+  "notifications.column_settings.follow_request": "Nýjar beiðnir um að fylgjast með:",
+  "notifications.column_settings.mention": "Tilvísanir:",
+  "notifications.column_settings.poll": "Niðurstöður könnunar:",
+  "notifications.column_settings.push": "Ýti-tilkynningar",
+  "notifications.column_settings.reblog": "Endurbirtingar:",
+  "notifications.column_settings.show": "Sýna í dálki",
+  "notifications.column_settings.sound": "Spila hljóð",
+  "notifications.filter.all": "Allt",
+  "notifications.filter.boosts": "Endurbirtingar",
+  "notifications.filter.favourites": "Eftirlæti",
+  "notifications.filter.follows": "Fylgist með",
+  "notifications.filter.mentions": "Tilvísanir",
+  "notifications.filter.polls": "Niðurstöður könnunar",
+  "notifications.group": "{count} tilkynningar",
+  "poll.closed": "Lokað",
+  "poll.refresh": "Endurlesa",
+  "poll.total_people": "{count, plural, one {# aðili} other {# aðilar}}",
+  "poll.total_votes": "{count, plural, one {# atkvæði} other {# atkvæði}}",
+  "poll.vote": "Greiða atkvæði",
+  "poll.voted": "Þú kaust þetta svar",
+  "poll_button.add_poll": "Bæta við könnun",
+  "poll_button.remove_poll": "Fjarlægja könnun",
+  "privacy.change": "Aðlaga gagnaleynd stöðufærslu",
+  "privacy.direct.long": "Senda einungis á notendur sem minnst er á",
+  "privacy.direct.short": "Beint",
+  "privacy.private.long": "Senda einungis á fylgjendur",
+  "privacy.private.short": "Einungis fylgjendur",
+  "privacy.public.long": "Senda á opinberar tímalínur",
+  "privacy.public.short": "Opinbert",
+  "privacy.unlisted.long": "Ekki senda á opinberar tímalínur",
+  "privacy.unlisted.short": "Óskráð",
+  "refresh": "Endurlesa",
+  "regeneration_indicator.label": "Hleð inn…",
+  "regeneration_indicator.sublabel": "Verið er að útbúa heimastreymið þitt!",
+  "relative_time.days": "{number}d",
+  "relative_time.hours": "{number}kl.",
+  "relative_time.just_now": "núna",
+  "relative_time.minutes": "{number}mín",
+  "relative_time.seconds": "{number}sek",
+  "reply_indicator.cancel": "Hætta við",
+  "report.forward": "Áframsenda til {target}",
+  "report.forward_hint": "Notandaaðgangurinn er af öðrum vefþjóni. Á einnig að senda nafnlaust afrit af kærunni þangað?",
+  "report.hint": "Kæran verður send á umsjónarmenn vefþjónsins þíns. Þú getur gefið skýringu hér fyrir neðan á því af hverju þú ert að kæra þennan notandaaðgang:",
+  "report.placeholder": "Viðbótarathugasemdir",
+  "report.submit": "Senda inn",
+  "report.target": "Kæri {target}",
+  "search.placeholder": "Leita",
+  "search_popout.search_format": "Snið ítarlegrar leitar",
+  "search_popout.tips.full_text": "Einfaldur texti skilar stöðufæ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": "stöðufæ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_results.hashtags": "Myllumerki",
+  "search_results.statuses": "Tíst",
+  "search_results.statuses_fts_disabled": "Að leita í efni tísta er ekki virk á þessum Mastodon-þjóni.",
+  "search_results.total": "{count, number} {count, plural, one {niðurstaða} other {niðurstöður}}",
+  "status.admin_account": "Opna umsjónarviðmót fyrir @{name}",
+  "status.admin_status": "Opna þessa stöðufærslu í umsjónarviðmótinu",
+  "status.block": "Útiloka @{name}",
+  "status.bookmark": "Bókamerki",
+  "status.cancel_reblog_private": "Taka úr endurbirtingu",
+  "status.cannot_reblog": "Þessa færslu er ekki hægt að endurbirta",
+  "status.copy": "Afrita tengil í stöðufærslu",
+  "status.delete": "Eyða",
+  "status.detailed_status": "Nákvæm spjallþráðasýn",
+  "status.direct": "Bein skilaboð @{name}",
+  "status.embed": "Ívefja",
+  "status.favourite": "Eftirlæti",
+  "status.filtered": "Síað",
+  "status.load_more": "Hlaða inn meiru",
+  "status.media_hidden": "Mynd er falin",
+  "status.mention": "Minnast á @{name}",
+  "status.more": "Meira",
+  "status.mute": "Þagga niður í @{name}",
+  "status.mute_conversation": "Þagga niður í samtali",
+  "status.open": "Útliða þessa stöðu",
+  "status.pin": "Festa á notandasnið",
+  "status.pinned": "Fast tíst",
+  "status.read_more": "Lesa meira",
+  "status.reblog": "Endurbirting",
+  "status.reblog_private": "Endurbirta til upphaflegra lesenda",
+  "status.reblogged_by": "{name} endurbirti",
+  "status.reblogs.empty": "Enginn hefur ennþá endurbirt þetta tíst. Þegar einhverjir gera það, munu þeir birtast hér.",
+  "status.redraft": "Eyða og enduvinna drög",
+  "status.remove_bookmark": "Fjarlægja bókamerki",
+  "status.reply": "Svara",
+  "status.replyAll": "Svara þræði",
+  "status.report": "Kæra @{name}",
+  "status.sensitive_warning": "Viðkvæmt efni",
+  "status.share": "Deila",
+  "status.show_less": "Sýna minna",
+  "status.show_less_all": "Sýna minna fyrir allt",
+  "status.show_more": "Sýna meira",
+  "status.show_more_all": "Sýna meira fyrir allt",
+  "status.show_thread": "Birta þráð",
+  "status.uncached_media_warning": "Ekki tiltækt",
+  "status.unmute_conversation": "Hætta að þagga niður í samtali",
+  "status.unpin": "Losa af notandasniði",
+  "suggestions.dismiss": "Hafna tillögu",
+  "suggestions.header": "Þú gætir haft áhuga á…",
+  "tabs_bar.federated_timeline": "Sameiginlegt",
+  "tabs_bar.home": "Heim",
+  "tabs_bar.local_timeline": "Staðvært",
+  "tabs_bar.notifications": "Tilkynningar",
+  "tabs_bar.search": "Leita",
+  "time_remaining.days": "{number, plural, one {# dagur} other {# dagar}} eftir",
+  "time_remaining.hours": "{number, plural, one {# klukkustund} other {# klukkustundir}} eftir",
+  "time_remaining.minutes": "{number, plural, one {# mínúta} other {# mínútur}} eftir",
+  "time_remaining.moments": "Tími eftir",
+  "time_remaining.seconds": "{number, plural, one {# sekúnda} other {# sekúndur}} eftir",
+  "trends.count_by_accounts": "{count} {rawCount, plural, one {aðili} other {aðilar}} að tala",
+  "trends.trending_now": "Í umræðunni núna",
+  "ui.beforeunload": "Drögin tapast ef þú ferð út úr Mastodon.",
+  "upload_area.title": "Dragðu-og-slepptu hér til að senda inn",
+  "upload_button.label": "Bæta við ({formats}) myndskrá",
+  "upload_error.limit": "Fór yfir takmörk á innsendingum skráa.",
+  "upload_error.poll": "Innsending skráa er ekki leyfð í könnunum.",
+  "upload_form.audio_description": "Lýstu þessu fyrir heyrnarskerta",
+  "upload_form.description": "Lýstu þessu fyrir sjónskerta",
+  "upload_form.edit": "Breyta",
+  "upload_form.undo": "Eyða",
+  "upload_form.video_description": "Lýstu þessu fyrir fólk sem heyrir illa eða er með skerta sjón",
+  "upload_modal.analyzing_picture": "Greini mynd…",
+  "upload_modal.apply": "Virkja",
+  "upload_modal.description_placeholder": "Öllum dýrunum í skóginum þætti bezt að vera vinir",
+  "upload_modal.detect_text": "Skynja texta úr mynd",
+  "upload_modal.edit_media": "Breyta myndskrá",
+  "upload_modal.hint": "Smelltu eða dragðu til hringinn á forskoðuninni til að velja miðpunktinn sem verður alltaf sýnilegastur á öllum smámyndum.",
+  "upload_modal.preview_label": "Forskoðun ({ratio})",
+  "upload_progress.label": "Er að senda inn...",
+  "video.close": "Loka myndskeiði",
+  "video.download": "Sækja skrá",
+  "video.exit_fullscreen": "Hætta í skjáfylli",
+  "video.expand": "Stækka myndskeið",
+  "video.fullscreen": "Skjáfylli",
+  "video.hide": "Fela myndskeið",
+  "video.mute": "Þagga hljóð",
+  "video.pause": "Gera hlé",
+  "video.play": "Spila",
+  "video.unmute": "Kveikja á hljóði"
+}
diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json
index 757882523..1b31f90f6 100644
--- a/app/javascript/mastodon/locales/it.json
+++ b/app/javascript/mastodon/locales/it.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Aggiungi o togli dalle liste",
   "account.badges.bot": "Bot",
+  "account.badges.group": "Gruppo",
   "account.block": "Blocca @{name}",
   "account.block_domain": "Nascondi tutto da {domain}",
   "account.blocked": "Bloccato",
@@ -39,9 +40,10 @@
   "account.unmute": "Non silenziare @{name}",
   "account.unmute_notifications": "Non silenziare più le notifiche da @{name}",
   "alert.rate_limited.message": "Riprova dopo {retry_time, time, medium}.",
-  "alert.rate_limited.title": "Rate limited",
+  "alert.rate_limited.title": "Numero massimo di richieste superato",
   "alert.unexpected.message": "Si è verificato un errore inatteso.",
   "alert.unexpected.title": "Oops!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} per settimana",
   "boost_modal.combo": "Puoi premere {combo} per saltare questo passaggio la prossima volta",
   "bundle_column_error.body": "E' avvenuto un errore durante il caricamento di questo componente.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "C'è stato un errore mentre questo componente veniva caricato.",
   "bundle_modal_error.retry": "Riprova",
   "column.blocks": "Utenti bloccati",
+  "column.bookmarks": "Segnalibri",
   "column.community": "Timeline locale",
   "column.direct": "Messaggi diretti",
   "column.directory": "Sfoglia profili",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Durata del sondaggio",
   "compose_form.poll.option_placeholder": "Scelta {number}",
   "compose_form.poll.remove_option": "Rimuovi questa scelta",
+  "compose_form.poll.switch_to_multiple": "Modifica sondaggio per consentire scelte multiple",
+  "compose_form.poll.switch_to_single": "Modifica sondaggio per consentire una singola scelta",
   "compose_form.publish": "Toot",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Segna media come sensibile",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Non ci sono toot qui!",
   "empty_column.account_unavailable": "Profilo non disponibile",
   "empty_column.blocks": "Non hai ancora bloccato nessun utente.",
+  "empty_column.bookmarked_statuses": "Non hai ancora nessun toot tra i segnalibri. Quando ne aggiungerai qualcuno, comparirà qui.",
   "empty_column.community": "La timeline locale è vuota. Condividi qualcosa pubblicamente per dare inizio alla festa!",
   "empty_column.direct": "Non hai ancora nessun messaggio privato. Quando ne manderai o riceverai qualcuno, apparirà qui.",
   "empty_column.domain_blocks": "Non vi sono domini nascosti.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "per aprire l'elenco degli utenti silenziati",
   "keyboard_shortcuts.my_profile": "per aprire il tuo profilo",
   "keyboard_shortcuts.notifications": "per aprire la colonna delle notifiche",
+  "keyboard_shortcuts.open_media": "per aprire media",
   "keyboard_shortcuts.pinned": "per aprire l'elenco dei toot fissati in cima",
   "keyboard_shortcuts.profile": "per aprire il profilo dell'autore",
   "keyboard_shortcuts.reply": "per rispondere",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Nascondere le notifiche da quest'utente?",
   "navigation_bar.apps": "App per dispositivi mobili",
   "navigation_bar.blocks": "Utenti bloccati",
+  "navigation_bar.bookmarks": "Segnalibri",
   "navigation_bar.community_timeline": "Timeline locale",
   "navigation_bar.compose": "Componi nuovo toot",
   "navigation_bar.direct": "Messaggi diretti",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Sicurezza",
   "notification.favourite": "{name} ha apprezzato il tuo post",
   "notification.follow": "{name} ha iniziato a seguirti",
+  "notification.follow_request": "{name} ha chiesto di seguirti",
   "notification.mention": "{name} ti ha menzionato",
+  "notification.own_poll": "Il tuo sondaggio è terminato",
   "notification.poll": "Un sondaggio in cui hai votato è terminato",
   "notification.reblog": "{name} ha condiviso il tuo post",
   "notifications.clear": "Cancella notifiche",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Filtro rapido",
   "notifications.column_settings.filter_bar.show": "Mostra",
   "notifications.column_settings.follow": "Nuovi seguaci:",
+  "notifications.column_settings.follow_request": "Nuove richieste di essere seguito:",
   "notifications.column_settings.mention": "Menzioni:",
   "notifications.column_settings.poll": "Risultati del sondaggio:",
   "notifications.column_settings.push": "Notifiche push",
@@ -344,6 +355,7 @@
   "status.admin_account": "Apri interfaccia di moderazione per @{name}",
   "status.admin_status": "Apri questo status nell'interfaccia di moderazione",
   "status.block": "Blocca @{name}",
+  "status.bookmark": "Aggiungi segnalibro",
   "status.cancel_reblog_private": "Annulla condivisione",
   "status.cannot_reblog": "Questo post non può essere condiviso",
   "status.copy": "Copia link allo status",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} ha condiviso",
   "status.reblogs.empty": "Nessuno ha ancora condiviso questo toot. Quando qualcuno lo farà, comparirà qui.",
   "status.redraft": "Cancella e riscrivi",
+  "status.remove_bookmark": "Elimina segnalibro",
   "status.reply": "Rispondi",
   "status.replyAll": "Rispondi alla conversazione",
   "status.report": "Segnala @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Aggiungi file multimediale",
   "upload_error.limit": "Limite al caricamento di file superato.",
   "upload_error.poll": "Caricamento file non consentito nei sondaggi.",
+  "upload_form.audio_description": "Descrizione per persone con difetti uditivi",
   "upload_form.description": "Descrizione per utenti con disabilità visive",
   "upload_form.edit": "Modifica",
   "upload_form.undo": "Cancella",
+  "upload_form.video_description": "Descrizione per persone con difetti uditivi o visivi",
   "upload_modal.analyzing_picture": "Analisi immagine…",
   "upload_modal.apply": "Applica",
   "upload_modal.description_placeholder": "Ma la volpe col suo balzo ha raggiunto il quieto Fido",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Anteprima ({ratio})",
   "upload_progress.label": "Sto caricando...",
   "video.close": "Chiudi video",
+  "video.download": "Scarica file",
   "video.exit_fullscreen": "Esci da modalità a schermo intero",
   "video.expand": "Espandi video",
   "video.fullscreen": "Schermo intero",
diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json
index fdcbe53a2..67cee9276 100644
--- a/app/javascript/mastodon/locales/ja.json
+++ b/app/javascript/mastodon/locales/ja.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "リストから追加または外す",
   "account.badges.bot": "Bot",
+  "account.badges.group": "Group",
   "account.block": "@{name}さんをブロック",
   "account.block_domain": "{domain}全体を非表示",
   "account.blocked": "ブロック済み",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "制限に達しました",
   "alert.unexpected.message": "不明なエラーが発生しました。",
   "alert.unexpected.title": "エラー!",
+  "announcement.announcement": "お知らせ",
   "autosuggest_hashtag.per_week": "{count} 回 / 週",
   "boost_modal.combo": "次からは{combo}を押せばスキップできます",
   "bundle_column_error.body": "コンポーネントの読み込み中に問題が発生しました。",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "コンポーネントの読み込み中に問題が発生しました。",
   "bundle_modal_error.retry": "再試行",
   "column.blocks": "ブロックしたユーザー",
+  "column.bookmarks": "ブックマーク",
   "column.community": "ローカルタイムライン",
   "column.direct": "ダイレクトメッセージ",
   "column.directory": "ディレクトリ",
@@ -86,6 +89,8 @@
   "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_loud": "{publish}!",
   "compose_form.sensitive.hide": "メディアを閲覧注意にする",
@@ -107,7 +112,7 @@
   "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": "本当にこのトゥートを削除して下書きに戻しますか? このトゥートへのお気に入り登録やブーストは失われ、返信は孤立することになります。",
@@ -142,6 +147,7 @@
   "empty_column.account_timeline": "トゥートがありません!",
   "empty_column.account_unavailable": "プロフィールは利用できません",
   "empty_column.blocks": "まだ誰もブロックしていません。",
+  "empty_column.bookmarked_statuses": "まだ何もブックマーク登録していません。ブックマーク登録するとここに表示されます。",
   "empty_column.community": "ローカルタイムラインはまだ使われていません。何か書いてみましょう!",
   "empty_column.direct": "ダイレクトメッセージはまだありません。ダイレクトメッセージをやりとりすると、ここに表示されます。",
   "empty_column.domain_blocks": "非表示にしているドメインはありません。",
@@ -223,6 +229,7 @@
   "keyboard_shortcuts.muted": "ミュートしたユーザーのリストを開く",
   "keyboard_shortcuts.my_profile": "自分のプロフィールを開く",
   "keyboard_shortcuts.notifications": "通知カラムを開く",
+  "keyboard_shortcuts.open_media": "メディアを開く",
   "keyboard_shortcuts.pinned": "固定したトゥートのリストを開く",
   "keyboard_shortcuts.profile": "プロフィールを開く",
   "keyboard_shortcuts.reply": "返信",
@@ -255,6 +262,7 @@
   "mute_modal.hide_notifications": "このユーザーからの通知を隠しますか?",
   "navigation_bar.apps": "アプリ",
   "navigation_bar.blocks": "ブロックしたユーザー",
+  "navigation_bar.bookmarks": "ブックマーク",
   "navigation_bar.community_timeline": "ローカルタイムライン",
   "navigation_bar.compose": "トゥートの新規作成",
   "navigation_bar.direct": "ダイレクトメッセージ",
@@ -278,7 +286,9 @@
   "navigation_bar.security": "セキュリティ",
   "notification.favourite": "{name}さんがあなたのトゥートをお気に入りに登録しました",
   "notification.follow": "{name}さんにフォローされました",
+  "notification.follow_request": "{name} さんがあなたにフォローリクエストしました",
   "notification.mention": "{name}さんがあなたに返信しました",
+  "notification.own_poll": "アンケートが終了しました",
   "notification.poll": "アンケートが終了しました",
   "notification.reblog": "{name}さんがあなたのトゥートをブーストしました",
   "notifications.clear": "通知を消去",
@@ -289,6 +299,7 @@
   "notifications.column_settings.filter_bar.category": "クイックフィルターバー",
   "notifications.column_settings.filter_bar.show": "表示",
   "notifications.column_settings.follow": "新しいフォロワー:",
+  "notifications.column_settings.follow_request": "新しいフォローリクエスト:",
   "notifications.column_settings.mention": "返信:",
   "notifications.column_settings.poll": "アンケート結果:",
   "notifications.column_settings.push": "プッシュ通知",
@@ -349,6 +360,7 @@
   "status.admin_account": "@{name} のモデレーション画面を開く",
   "status.admin_status": "このトゥートをモデレーション画面で開く",
   "status.block": "@{name}さんをブロック",
+  "status.bookmark": "ブックマーク",
   "status.cancel_reblog_private": "ブースト解除",
   "status.cannot_reblog": "この投稿はブーストできません",
   "status.copy": "トゥートへのリンクをコピー",
@@ -373,6 +385,7 @@
   "status.reblogged_by": "{name}さんがブースト",
   "status.reblogs.empty": "まだ誰もブーストしていません。ブーストされるとここに表示されます。",
   "status.redraft": "削除して下書きに戻す",
+  "status.remove_bookmark": "ブックマークを削除",
   "status.reply": "返信",
   "status.replyAll": "全員に返信",
   "status.report": "@{name}さんを通報",
@@ -405,9 +418,11 @@
   "upload_button.label": "メディアを追加 ({formats})",
   "upload_error.limit": "アップロードできる上限を超えています。",
   "upload_error.poll": "アンケートではファイルをアップロードできません。",
-  "upload_form.description": "視覚障害者のための説明",
+  "upload_form.audio_description": "聴取が難しいユーザーへの説明",
+  "upload_form.description": "閲覧が難しいユーザーへの説明",
   "upload_form.edit": "編集",
   "upload_form.undo": "削除",
+  "upload_form.video_description": "視聴が難しいユーザーへの説明",
   "upload_modal.analyzing_picture": "画像を解析中…",
   "upload_modal.apply": "適用",
   "upload_modal.description_placeholder": "あのイーハトーヴォのすきとおった風",
@@ -417,6 +432,7 @@
   "upload_modal.preview_label": "プレビュー ({ratio})",
   "upload_progress.label": "アップロード中...",
   "video.close": "動画を閉じる",
+  "video.download": "ダウンロード",
   "video.exit_fullscreen": "全画面を終了する",
   "video.expand": "動画を拡大する",
   "video.fullscreen": "全画面",
diff --git a/app/javascript/mastodon/locales/ka.json b/app/javascript/mastodon/locales/ka.json
index 35789374a..1222702a4 100644
--- a/app/javascript/mastodon/locales/ka.json
+++ b/app/javascript/mastodon/locales/ka.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Add or Remove from lists",
   "account.badges.bot": "ბოტი",
+  "account.badges.group": "Group",
   "account.block": "დაბლოკე @{name}",
   "account.block_domain": "დაიმალოს ყველაფერი დომენიდან {domain}",
   "account.blocked": "დაიბლოკა",
@@ -42,7 +43,8 @@
   "alert.rate_limited.title": "Rate limited",
   "alert.unexpected.message": "წარმოიშვა მოულოდნელი შეცდომა.",
   "alert.unexpected.title": "უპს!",
-  "autosuggest_hashtag.per_week": "{count} per week",
+  "announcement.announcement": "Announcement",
+  "autosuggest_hashtag.per_week": "კვირაში {count}",
   "boost_modal.combo": "შეგიძლიათ დააჭიროთ {combo}-ს რათა შემდეგ ჯერზე გამოტოვოთ ეს",
   "bundle_column_error.body": "ამ კომპონენტის ჩატვირთვისას რაღაც აირია.",
   "bundle_column_error.retry": "სცადეთ კიდევ ერთხელ",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "ამ კომპონენტის ჩატვირთვისას რაღაც აირია.",
   "bundle_modal_error.retry": "სცადეთ კიდევ ერთხელ",
   "column.blocks": "დაბლოკილი მომხმარებლები",
+  "column.bookmarks": "Bookmarks",
   "column.community": "ლოკალური თაიმლაინი",
   "column.direct": "პირდაპირი წერილები",
   "column.directory": "Browse profiles",
@@ -82,6 +85,8 @@
   "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": "ტუტი",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Mark media as sensitive",
@@ -138,6 +143,7 @@
   "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": "ლოკალური თაიმლაინი ცარიელია. დაწერეთ რაიმე ღიად ან ქენით რაიმე სხვა!",
   "empty_column.direct": "ჯერ პირდაპირი წერილები არ გაქვთ. როდესაც მიიღებთ ან გააგზავნით, გამოჩნდება აქ.",
   "empty_column.domain_blocks": "There are no hidden domains yet.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "to open muted users list",
   "keyboard_shortcuts.my_profile": "to open your profile",
   "keyboard_shortcuts.notifications": "to open notifications column",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "to open pinned toots list",
   "keyboard_shortcuts.profile": "ავტორის პროფილის გასახსნელად",
   "keyboard_shortcuts.reply": "პასუხისთვის",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "დავმალოთ შეტყობინებები ამ მომხმარებლისგან?",
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "დაბლოკილი მომხმარებლები",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "ლოკალური თაიმლაინი",
   "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "პირდაპირი წერილები",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "უსაფრთხოება",
   "notification.favourite": "{name}-მა თქვენი სტატუსი აქცია ფავორიტად",
   "notification.follow": "{name} გამოგყვათ",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name}-მა გასახელათ",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "A poll you have voted in has ended",
   "notification.reblog": "{name}-მა დაბუსტა თქვენი სტატუსი",
   "notifications.clear": "შეტყობინებების გასუფთავება",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Quick filter bar",
   "notifications.column_settings.filter_bar.show": "Show",
   "notifications.column_settings.follow": "ახალი მიმდევრები:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "ხსენებები:",
   "notifications.column_settings.poll": "Poll results:",
   "notifications.column_settings.push": "ფუშ შეტყობინებები",
@@ -344,6 +355,7 @@
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_status": "Open this status in the moderation interface",
   "status.block": "დაბლოკე @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "ბუსტის მოშორება",
   "status.cannot_reblog": "ეს პოსტი ვერ დაიბუსტება",
   "status.copy": "Copy link to status",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} დაიბუსტა",
   "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
   "status.redraft": "გაუქმდეს და გადანაწილდეს",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "პასუხი",
   "status.replyAll": "უპასუხე თემას",
   "status.report": "დაარეპორტე @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "მედიის დამატება",
   "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": "აღწერილობა ვიზუალურად უფასურისთვის",
   "upload_form.edit": "Edit",
   "upload_form.undo": "გაუქმება",
+  "upload_form.video_description": "Describe for people with hearing loss or visual impairment",
   "upload_modal.analyzing_picture": "Analyzing picture…",
   "upload_modal.apply": "Apply",
   "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "იტვირთება...",
   "video.close": "ვიდეოს დახურვა",
+  "video.download": "Download file",
   "video.exit_fullscreen": "სრულ ეკრანზე ჩვენების გათიშვა",
   "video.expand": "ვიდეოს გაფართოება",
   "video.fullscreen": "ჩვენება სრულ ეკრანზე",
diff --git a/app/javascript/mastodon/locales/kab.json b/app/javascript/mastodon/locales/kab.json
new file mode 100644
index 000000000..ec77fa9e4
--- /dev/null
+++ b/app/javascript/mastodon/locales/kab.json
@@ -0,0 +1,439 @@
+{
+  "account.add_or_remove_from_list": "Rnu neγ kkes seg tebdarin",
+  "account.badges.bot": "Aṛubut",
+  "account.badges.group": "Agraw",
+  "account.block": "Seḥbes @{name}",
+  "account.block_domain": "Ffer kra i d-yekkan seg {domain}",
+  "account.blocked": "Yettuseḥbes",
+  "account.cancel_follow_request": "Sefsex asuter n weḍfaṛ",
+  "account.direct": "Izen usrid i @{name}",
+  "account.domain_blocked": "Taγult yeffren",
+  "account.edit_profile": "Ẓreg amaγnu",
+  "account.endorse": "Welleh fell-as deg umaγnu-inek",
+  "account.follow": "Ḍfeṛ",
+  "account.followers": "Imeḍfaṛen",
+  "account.followers.empty": "Ar tura, ulac yiwen i yeṭṭafaṛen amseqdac-agi.",
+  "account.follows": "Aḍfaṛen",
+  "account.follows.empty": "Ar tura, amseqdac-agi ur yeṭṭafaṛ yiwen.",
+  "account.follows_you": "Yeṭṭafaṛ-ik",
+  "account.hide_reblogs": "Ffer ayen i ibeṭṭu @{name}",
+  "account.last_status": "Armud aneggaru",
+  "account.link_verified_on": "Taγara n useγwen-a tettwasenqed ass n {date}",
+  "account.locked_info": "Amiḍan-agi uslig isekweṛ. D bab-is kan i izemren ad yeǧǧ, s ufus-is, win ara t-iḍefṛen.",
+  "account.media": "Allal n teywalt",
+  "account.mention": "Bder-d @{name}",
+  "account.moved_to": "{name} ibeddel γer:",
+  "account.mute": "Sgugem @{name}",
+  "account.mute_notifications": "Susem ilγa sγur @{name}",
+  "account.muted": "Yettwasgugem",
+  "account.never_active": "Werǧin",
+  "account.posts": "Tiberraḥin",
+  "account.posts_with_replies": "Tibarraḥin d tririyin",
+  "account.report": "Sewɛed @{name}",
+  "account.requested": "Di laɛḍil ad yettwaqbel. Ssit iwakken ad yefsex usuter n weḍfar",
+  "account.share": "Bḍu amaγnu n @{name}",
+  "account.show_reblogs": "Sken-d inebḍa n @{name}",
+  "account.unblock": "Serreḥ i @{name}",
+  "account.unblock_domain": "Kkes tuffra i {domain}",
+  "account.unendorse": "Ur ttwellih ara fell-as deg umaγnu-inek",
+  "account.unfollow": "Ur ṭṭafaṛ ara",
+  "account.unmute": "Kkes asgugem γef @{name}",
+  "account.unmute_notifications": "Serreḥ ilγa sγur @{name}",
+  "alert.rate_limited.message": "Ma ulac aγilif ɛreḍ tikelt-nniḍen mbeɛd {retry_time, time, medium}.",
+  "alert.rate_limited.title": "Aktum s talast",
+  "alert.unexpected.message": "Tella-d tuccḍa i ɣef ur nedmi ara.",
+  "alert.unexpected.title": "Ayhuh!",
+  "announcement.announcement": "Announcement",
+  "autosuggest_hashtag.per_week": "{count} i yimalas",
+  "boost_modal.combo": "Tzemreḍ ad tetekkiḍ γef {combo} akken ad tessurfeḍ aya tikelt-nniḍen",
+  "bundle_column_error.body": "Tella-d kra n tuccḍa mi d-yettali ugbur-agi.",
+  "bundle_column_error.retry": "Ɛreḍ tikelt-nniḍen",
+  "bundle_column_error.title": "Tuccḍa deg uẓeṭṭa",
+  "bundle_modal_error.close": "Mdel",
+  "bundle_modal_error.message": "Tella-d kra n tuccḍa mi d-yettali ugbur-agi.",
+  "bundle_modal_error.retry": "Ɛreḍ tikelt-nniḍen",
+  "column.blocks": "Imiḍanen yettusḥebsen",
+  "column.bookmarks": "Ticraḍ",
+  "column.community": "Tasuddemt tadigant",
+  "column.direct": "Iznan usriden",
+  "column.directory": "Qelleb deg imaγnuten",
+  "column.domain_blocks": "Tiγula yettwaffren",
+  "column.favourites": "Ismenyifen",
+  "column.follow_requests": "Isuturen n teḍfeṛt",
+  "column.home": "Agejdan",
+  "column.lists": "Tibdarin",
+  "column.mutes": "Imiḍanen yettwasgugmen",
+  "column.notifications": "Tilγa",
+  "column.pins": "Tiberraḥin yettwasenṭḍen",
+  "column.public": "Tasuddemt tamatut",
+  "column_back_button.label": "Tuγalin",
+  "column_header.hide_settings": "Ffer iγewwaṛen",
+  "column_header.moveLeft_settings": "Err ajgu γer tama tazelmaḍt",
+  "column_header.moveRight_settings": "Err ajgu γer tama tayfust",
+  "column_header.pin": "Senteḍ",
+  "column_header.show_settings": "Sken iγewwaṛen",
+  "column_header.unpin": "Kkes asenteḍ",
+  "column_subheading.settings": "Iγewwaṛen",
+  "community.column_settings.media_only": "Allal n teywalt kan",
+  "compose_form.direct_message_warning": "Taberraḥt-a ad d-tettwasken kan i yimseqdacen i d-yettwabedren.",
+  "compose_form.direct_message_warning_learn_more": "Issin ugar",
+  "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
+  "compose_form.lock_disclaimer": "Amiḍan-ik ur yelli ara {locked}. Menwala yezmer ad k-yeḍfeṛ akken ad iẓer acu tbeṭṭuḍ akked yimeḍfaṛen-ik.",
+  "compose_form.lock_disclaimer.lock": "yettwacekkel",
+  "compose_form.placeholder": "D acu i itezzin deg uqaṛṛu-ik?",
+  "compose_form.poll.add_option": "Rnu afran",
+  "compose_form.poll.duration": "Tanzagt n tefrant",
+  "compose_form.poll.option_placeholder": "Afran {number}",
+  "compose_form.poll.remove_option": "Sfeḍ afran-agi",
+  "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": "Taberraḥt",
+  "compose_form.publish_loud": "{publish}!",
+  "compose_form.sensitive.hide": "Creḍ allal n teywalt d anafri",
+  "compose_form.sensitive.marked": "Allal n teywalt yettwacreḍ d anafri",
+  "compose_form.sensitive.unmarked": "Allal n teywalt ur yettwacreḍ ara d anafri",
+  "compose_form.spoiler.marked": "Aḍris yeffer deffir n walγu",
+  "compose_form.spoiler.unmarked": "Aḍris ur yettwaffer ara",
+  "compose_form.spoiler_placeholder": "Aru alγu-inek da",
+  "confirmation_modal.cancel": "Sefsex",
+  "confirmations.block.block_and_report": "Sewḥel & sewɛed",
+  "confirmations.block.confirm": "Sewḥel",
+  "confirmations.block.message": "Tebγiḍ s tidet ad tesḥebseḍ {name}?",
+  "confirmations.delete.confirm": "Kkes",
+  "confirmations.delete.message": "Tebγiḍ s tidet ad tekkseḍ tasuffeγt-agi?",
+  "confirmations.delete_list.confirm": "Kkes",
+  "confirmations.delete_list.message": "Tebγiḍ s tidet ad tekkseḍ tabdert-agi i lebda?",
+  "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.logout.confirm": "Ffeγ",
+  "confirmations.logout.message": "D tidet tebγiḍ ad teffγeḍ?",
+  "confirmations.mute.confirm": "Sgugem",
+  "confirmations.mute.explanation": "Aya ad yeffer iznan-is d wid i deg d-yettwabder neγ d-tettwabder, maca xas akka yezmer neγ tezmer awali n yiznan-inek d uḍfaṛ-ik.",
+  "confirmations.mute.message": "Tetḥeqqeḍ belli tebγiḍ asɛuggen n {name}?",
+  "confirmations.redraft.confirm": "Sfeḍ & Ɛiwed tira",
+  "confirmations.redraft.message": "Tetḥeqqeḍ belli tebγiḍ asfaḍ n waddad-agi iwakken ad s-tɛiwdeḍ tira? Ismenyifen d beḍḍuwat ad ṛuḥen, ma d tiririyin-is ad uγalent d tigujilin.",
+  "confirmations.reply.confirm": "Err",
+  "confirmations.reply.message": "Tiririt akka tura ad k-degger izen-agi i tettaruḍ. Tebγiḍ ad tkemmleḍ?",
+  "confirmations.unfollow.confirm": "Ur ḍḍafaṛ ara",
+  "confirmations.unfollow.message": "Tetḥeqqeḍ belli tebγiḍ ur teḍḍafaṛeḍ ara {name}?",
+  "conversation.delete": "Sfeḍ adiwenni",
+  "conversation.mark_as_read": "Creḍ yettwaγṛa",
+  "conversation.open": "Sken adiwenni",
+  "conversation.with": "Akked {names}",
+  "directory.federated": "Seg fedivers yettwasnen",
+  "directory.local": "Seg {domain} kan",
+  "directory.new_arrivals": "Inebgawen imaynuten",
+  "directory.recently_active": "Yermed xas melmi kan",
+  "embed.instructions": "Ẓẓu addad-agi deg usmel-inek s wenγal n tangalt yellan sdaw-agi.",
+  "embed.preview": "Akka ara d-iban:",
+  "emoji_button.activity": "Aqeddic",
+  "emoji_button.custom": "Udmawan",
+  "emoji_button.flags": "Innayen",
+  "emoji_button.food": "Tegwella & Tissit",
+  "emoji_button.label": "Sekcem imuji",
+  "emoji_button.nature": "Agama",
+  "emoji_button.not_found": "Ulac izamulen n yiḥulfan  !! (╯°□°)╯︵ ┻━┻",
+  "emoji_button.objects": "Tiγawsiwin",
+  "emoji_button.people": "Medden",
+  "emoji_button.recent": "Wid yettuseqdacen s waṭas",
+  "emoji_button.search": "Nadi…",
+  "emoji_button.search_results": "Igmaḍ u unadi",
+  "emoji_button.symbols": "Izamulen",
+  "emoji_button.travel": "Imeḍqan d Yinigen",
+  "empty_column.account_timeline": "Ulac tiberraḥin dagi!",
+  "empty_column.account_unavailable": "Ur nufi ara amaγnu-a",
+  "empty_column.blocks": "Ur tesḥebseḍ ula yiwen n umseqdac ar tura.",
+  "empty_column.bookmarked_statuses": "Ulac tiberraḥin 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": "Ulac γur-k ula yiwen n yizen usrid. Ad d-yettwasken da, ticki tuzneḍ neγ teṭṭfeḍ-d yiwen.",
+  "empty_column.domain_blocks": "Ulac kra n taγult yettwaffren ar tura.",
+  "empty_column.favourited_statuses": "Ulac ula yiwet n tberraḥt deg yismenyifen-ik ar tura. Ticki Tella-d yiwet, ad d-ban da.",
+  "empty_column.favourites": "Ula yiwen ur yerri taberraḥt-agi deg yismenyifen-is. Melmi i d-yella waya, ad d-yettwasken da.",
+  "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.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.public_timeline": "tasuddemt tazayezt n yisallen",
+  "empty_column.list": "Ar tura ur yelli kra deg tebdert-a. Ad d-yettwasken da ticki iɛeggalen n tebdert-a suffγen-d kra.",
+  "empty_column.lists": "Ulac γur-k kra n tebdert yakan. Ad d-tettwasken da ticki tesluleḍ-d yiwet.",
+  "empty_column.mutes": "Ulac γur-k imseqdacen i yettwasgugmen.",
+  "empty_column.notifications": "Ulac γur-k tilγa. Sedmer akked yemdanen-nniḍen akken ad tebduḍ adiwenni.",
+  "empty_column.public": "Ulac kra da! Aru kra, neγ ḍfeṛ imdanen i yellan deg yiqeddacen-nniḍen akken ad d-teččar tsuddemt tazayezt",
+  "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.next_steps": "Smiren asebter-a, ma ur yekkis ara wugur, ẓer d akken tzemreḍ ad tesqedceḍ Masṭudun deg yiminig-nniḍen neγ deg usnas anaṣli.",
+  "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard",
+  "errors.unexpected_crash.report_issue": "Mmel ugur",
+  "follow_request.authorize": "Ssireg",
+  "follow_request.reject": "Agi",
+  "getting_started.developers": "Ineflayen",
+  "getting_started.directory": "Imaγnuten",
+  "getting_started.documentation": "Amnir",
+  "getting_started.heading": "Bdu",
+  "getting_started.invite": "Snebgi-d imdanen",
+  "getting_started.open_source_notice": "Mastudun d aseγzan s uγbalu yeldin. Tzemreḍ ad tɛiwneḍ neγ ad temmleḍ uguren deg GitHub {github}.",
+  "getting_started.security": "Iγewwaṛen n umiḍan",
+  "getting_started.terms": "Tiwetlin n useqdec",
+  "hashtag.column_header.tag_mode.all": "d {additional}",
+  "hashtag.column_header.tag_mode.any": "neγ {additional}",
+  "hashtag.column_header.tag_mode.none": "war {additional}",
+  "hashtag.column_settings.select.no_options_message": "Ulac isumar",
+  "hashtag.column_settings.select.placeholder": "Rnu-d ihacṭagen…",
+  "hashtag.column_settings.tag_mode.all": "Kra yellan",
+  "hashtag.column_settings.tag_mode.any": "Yiwen seg-sen",
+  "hashtag.column_settings.tag_mode.none": "Yiwen ala seg-sen",
+  "hashtag.column_settings.tag_toggle": "Glu-d s yihacṭagen imerna i ujgu-agi",
+  "home.column_settings.basic": "Igejdanen",
+  "home.column_settings.show_reblogs": "Sken-d beṭṭu",
+  "home.column_settings.show_replies": "Sken-d tiririyin",
+  "intervals.full.days": "{number, plural, one {# n wass} other {# n wussan}}",
+  "intervals.full.hours": "{number, plural, one {# n usarag} other {# n yesragen}}",
+  "intervals.full.minutes": "{number, plural, one {# n tesdat} other {# n tesdatin}}",
+  "introduction.federation.action": "Uḍfiṛ",
+  "introduction.federation.federated.headline": "Amatu",
+  "introduction.federation.federated.text": "Iznan izuyaz i d-yekkan seg yiqeddacen-nniḍen n fediverse ad banen deg tsuddemt tazayezt tamatut n yisallen.",
+  "introduction.federation.home.headline": "Agejdan",
+  "introduction.federation.home.text": "Iznan n yemdanen i teṭṭafaṛeḍ ad banen deg tsuddemt n umagger. Tzemreḍ ad tḍefṛeḍ win tebγiḍ deg uqeddac i tebγiḍ!",
+  "introduction.federation.local.headline": "Adigan",
+  "introduction.federation.local.text": "Iznan izuyaz n yemdanen i yellan deg yiwen uqeddac akked kečč ad d-banen deg tsuddemt tazayezt tadigant.",
+  "introduction.interactions.action": "Fakk tameskant!",
+  "introduction.interactions.favourite.headline": "Ismenyifen",
+  "introduction.interactions.favourite.text": "Tzemreḍ ad teǧǧeḍ kra n tberraḥt i ticki, daγen ad tiniḍ i bab-is d akken taɛǧeb-ik, s tmerna-ines γer yismenyifen-ik.",
+  "introduction.interactions.reblog.headline": "Bḍu tikelt-nniḍen",
+  "introduction.interactions.reblog.text": "Tzemreḍ ad tebḍuḍ akked yimeḍfaṛen-ik tiberraḥin n yemdanen-nniḍen s beṭṭu-nsent tikelt-nniḍen.",
+  "introduction.interactions.reply.headline": "Err",
+  "introduction.interactions.reply.text": "Tzemreḍ ad terreḍ γef tberraḥin-ik d tid n medden-nniḍen, d acu ara tent-id-iɛeqden ta deffir ta deg udiwenni.",
+  "introduction.welcome.action": "Bdu!",
+  "introduction.welcome.headline": "Isurifen imenza",
+  "introduction.welcome.text": "Anṣuf γer fediverse! Deg kra n yimiren, ad tizmireḍ ad tzzuzreḍ iznan neɣ ad tmeslayeḍ i yemddukkal deg waṭas n yiqeddacen. Maca aqeddac-agi, {domain}, mačči am wiyaḍ - deg-s i yella umaγnu-ik, ihi cfu γef yisem-is.",
+  "keyboard_shortcuts.back": "uγal ar deffir",
+  "keyboard_shortcuts.blocked": "akken ad teldiḍ tabdert n yimseqdacen yettwasḥebsen",
+  "keyboard_shortcuts.boost": "i beṭṭu tikelt-nniḍen",
+  "keyboard_shortcuts.column": "to focus a status in one of the columns",
+  "keyboard_shortcuts.compose": "to focus the compose textarea",
+  "keyboard_shortcuts.description": "Aglam",
+  "keyboard_shortcuts.direct": "akken ad teldiḍ ajgu n yiznan usriden",
+  "keyboard_shortcuts.down": "i kennu γer wadda n tebdert",
+  "keyboard_shortcuts.enter": "i tildin n tsuffeγt",
+  "keyboard_shortcuts.favourite": "akken ad ternuḍ γer yismenyifen",
+  "keyboard_shortcuts.favourites": "i tildin n tebdert n yismenyifen",
+  "keyboard_shortcuts.federated": "i tildin n tsuddemt tamatut n yisallen",
+  "keyboard_shortcuts.heading": "Inegzumen n unasiw",
+  "keyboard_shortcuts.home": "i tildin n tsuddemt tagejdant n yisallen",
+  "keyboard_shortcuts.hotkey": "Inegzumen",
+  "keyboard_shortcuts.legend": "to display this legend",
+  "keyboard_shortcuts.local": "i tildin n tsuddemt tadigant n yisallen",
+  "keyboard_shortcuts.mention": "akken ad d-bedreḍ ameskar",
+  "keyboard_shortcuts.muted": "akken ad teldiḍ tabdert n yimseqdacen yettwasgugmen",
+  "keyboard_shortcuts.my_profile": "akken ad d-teldiḍ amaγnu-ik",
+  "keyboard_shortcuts.notifications": "akken ad d-teldiḍ ajgu n tilγa",
+  "keyboard_shortcuts.open_media": "to open media",
+  "keyboard_shortcuts.pinned": "i tildin n tebdert n tberraḥin yettwasentḍen",
+  "keyboard_shortcuts.profile": "akken ad d-teldiḍ amaγnu n umeskar",
+  "keyboard_shortcuts.reply": "i tririt",
+  "keyboard_shortcuts.requests": "akken ad d-teldiḍ tabdert n yisuturen n teḍfeṛt",
+  "keyboard_shortcuts.search": "to focus search",
+  "keyboard_shortcuts.start": "akken ad d-teldiḍ ajgu n \"bdu\"",
+  "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW",
+  "keyboard_shortcuts.toggle_sensitivity": "i teskent/tuffra n yimidyaten",
+  "keyboard_shortcuts.toot": "i beddu n tberraḥt tamaynut",
+  "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
+  "keyboard_shortcuts.up": "i tulin γer ufella n tebdert",
+  "lightbox.close": "Mdel",
+  "lightbox.next": "Γer zdat",
+  "lightbox.previous": "Γer deffir",
+  "lightbox.view_context": "Ẓer amnaḍ",
+  "lists.account.add": "Rnu γer tabdart",
+  "lists.account.remove": "Kkes seg tebdart",
+  "lists.delete": "Kkes tabdert",
+  "lists.edit": "Ẓreg tabdert",
+  "lists.edit.submit": "Beddel azwel",
+  "lists.new.create": "Rnu tabdart",
+  "lists.new.title_placeholder": "Azwel n tebdert tamaynut",
+  "lists.search": "Nadi gar yemdanen i teṭṭafaṛeḍ",
+  "lists.subheading": "Tibdarin-ik·im",
+  "load_pending": "{count, plural, one {# n uferdis amaynut} other {# n yiferdisen imaynuten}}",
+  "loading_indicator.label": "Asali...",
+  "media_gallery.toggle_visible": "Sken / Ffer",
+  "missing_indicator.label": "Ulac-it",
+  "missing_indicator.sublabel": "Ur nufi ara aγbalu-a",
+  "mute_modal.hide_notifications": "Tebγiḍ ad teffreḍ talγutin n umseqdac-a?",
+  "navigation_bar.apps": "Isnasen izirazen",
+  "navigation_bar.blocks": "Imseqdacen yettusḥebsen",
+  "navigation_bar.bookmarks": "Ticraḍ",
+  "navigation_bar.community_timeline": "Tasuddemt tadigant",
+  "navigation_bar.compose": "Aru taberraḥt tamaynut",
+  "navigation_bar.direct": "Iznan usridden",
+  "navigation_bar.discover": "Ẓer",
+  "navigation_bar.domain_blocks": "Tiγula yeffren",
+  "navigation_bar.edit_profile": "Ẓreg amaγnu",
+  "navigation_bar.favourites": "Ismenyifen",
+  "navigation_bar.filters": "Awalen i yettwasgugmen",
+  "navigation_bar.follow_requests": "Isuturen n teḍfeṛt",
+  "navigation_bar.follows_and_followers": "Imeḍfaṛen akked wid i teṭṭafaṛeḍ",
+  "navigation_bar.info": "Γef uqeddac-a",
+  "navigation_bar.keyboard_shortcuts": "Inegzumen n unasiw",
+  "navigation_bar.lists": "Tibdarin",
+  "navigation_bar.logout": "Ffeγ",
+  "navigation_bar.mutes": "Iseqdacen yettwasusmen",
+  "navigation_bar.personal": "Udmawan",
+  "navigation_bar.pins": "Tiberraḥin yettwasentḍen",
+  "navigation_bar.preferences": "Imenyafen",
+  "navigation_bar.public_timeline": "Tasuddemt tazayezt tamatut",
+  "navigation_bar.security": "Taγellist",
+  "notification.favourite": "{name} yesmenyef tasuffeγt-ik",
+  "notification.follow": "{name} yeṭṭafaṛ-ik",
+  "notification.follow_request": "{name} yessuter-d ad k-yeḍfeṛ",
+  "notification.mention": "{name} yebder-ik-id",
+  "notification.own_poll": "Your poll has ended",
+  "notification.poll": "A poll you have voted in has ended",
+  "notification.reblog": "{name} yebḍa taberraḥ-ik i tikelt-nniḍen",
+  "notifications.clear": "Sfeḍ tilγa",
+  "notifications.clear_confirmation": "Tebγiḍ s tidet ad tekkseḍ akk tilγa-ik i lebda?",
+  "notifications.column_settings.alert": "Tilγa n tnarit",
+  "notifications.column_settings.favourite": "Ismenyifen:",
+  "notifications.column_settings.filter_bar.advanced": "Sken-d meṛṛa tiggayin",
+  "notifications.column_settings.filter_bar.category": "Iri n usizdeg uzrib",
+  "notifications.column_settings.filter_bar.show": "Sken",
+  "notifications.column_settings.follow": "Imeḍfaṛen imaynuten:",
+  "notifications.column_settings.follow_request": "Isuturen imaynuten n teḍfeṛt:",
+  "notifications.column_settings.mention": "Abdar:",
+  "notifications.column_settings.poll": "Igemmaḍ n usenqed:",
+  "notifications.column_settings.push": "Tilγa yettudemmren",
+  "notifications.column_settings.reblog": "Boosts:",
+  "notifications.column_settings.show": "Sken-d tilγa deg ujgu",
+  "notifications.column_settings.sound": "Rmed imesli",
+  "notifications.filter.all": "Akk",
+  "notifications.filter.boosts": "Boosts",
+  "notifications.filter.favourites": "Ismenyifen",
+  "notifications.filter.follows": "Yeṭafaṛ",
+  "notifications.filter.mentions": "Abdar",
+  "notifications.filter.polls": "Igemmaḍ n usenqed",
+  "notifications.group": "{count} n tilγa",
+  "poll.closed": "Ifukk",
+  "poll.refresh": "Smiren",
+  "poll.total_people": "{count, plural, one {# n wemdan} other {# n yemdanen}}",
+  "poll.total_votes": "{count, plural, one {# n udγaṛ} other {# n yedγaṛen}}",
+  "poll.vote": "Dγeṛ",
+  "poll.voted": "Tdeγṛeḍ γef tririt-agi",
+  "poll_button.add_poll": "Rnu asenqed",
+  "poll_button.remove_poll": "Kkes asenqed",
+  "privacy.change": "Adjust status privacy",
+  "privacy.direct.long": "Bḍu gar yimseqdacen i tbedreḍ kan",
+  "privacy.direct.short": "Usrid",
+  "privacy.private.long": "Bḍu i yimeḍfaṛen-ik kan",
+  "privacy.private.short": "Imeḍfaṛen kan",
+  "privacy.public.long": "Bḍu deg tsuddemt tazayezt",
+  "privacy.public.short": "Azayez",
+  "privacy.unlisted.long": "Ur beṭṭu ara deg tsuddemt tazayezt",
+  "privacy.unlisted.short": "War tabdert",
+  "refresh": "Smiren",
+  "regeneration_indicator.label": "Asali…",
+  "regeneration_indicator.sublabel": "Tasuddemt tagejdant ara d-tettwaheggay!",
+  "relative_time.days": "{number}u",
+  "relative_time.hours": "{number}h",
+  "relative_time.just_now": "tura",
+  "relative_time.minutes": "{number}t",
+  "relative_time.seconds": "{number}s",
+  "reply_indicator.cancel": "Sefsex",
+  "report.forward": "Forward to {target}",
+  "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
+  "report.hint": "The report will be sent to your server moderators. You can provide an explanation of why you are reporting this account below:",
+  "report.placeholder": "Iwenniten-nniḍen",
+  "report.submit": "Azen",
+  "report.target": "Mmel {target}",
+  "search.placeholder": "Nadi",
+  "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": "status",
+  "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
+  "search_popout.tips.user": "amseqdac",
+  "search_results.accounts": "Medden",
+  "search_results.hashtags": "Ihacṭagen",
+  "search_results.statuses": "Tibeṛṛaniyin",
+  "search_results.statuses_fts_disabled": "Anadi n tberraḥin s ugbur-nsent ur yermid ara deg uqeddac-agi n Mastudun.",
+  "search_results.total": "{count, number} {count, plural, one {n ugemmuḍ} other {n yigemmuḍen}}",
+  "status.admin_account": "Open moderation interface for @{name}",
+  "status.admin_status": "Open this status in the moderation interface",
+  "status.block": "Seḥbes @{name}",
+  "status.bookmark": "Creḍ",
+  "status.cancel_reblog_private": "Sefsex beṭṭu",
+  "status.cannot_reblog": "Tasuffeγt-a ur tezmir ara ad tettwabḍu tikelt-nniḍen",
+  "status.copy": "Nγel assaγ γer tasuffeγt",
+  "status.delete": "Kkes",
+  "status.detailed_status": "Detailed conversation view",
+  "status.direct": "Izen usrid i @{name}",
+  "status.embed": "Embed",
+  "status.favourite": "Rnu γer yismenyifen",
+  "status.filtered": "Yettwasizdeg",
+  "status.load_more": "Sali ugar",
+  "status.media_hidden": "Media hidden",
+  "status.mention": "Bder-d @{name}",
+  "status.more": "Ugar",
+  "status.mute": "Sussem @{name}",
+  "status.mute_conversation": "Mute conversation",
+  "status.open": "Semγeṛ tasuffeγt-agi",
+  "status.pin": "Senteḍ-itt deg umaγnu",
+  "status.pinned": "Tiberraḥin yettwasentḍen",
+  "status.read_more": "Issin ugar",
+  "status.reblog": "Bḍu",
+  "status.reblog_private": "Boost to original audience",
+  "status.reblogged_by": "{name} boosted",
+  "status.reblogs.empty": "Ula yiwen ur yebḍi taberraḥt-agi ar tura. Ticki yebḍa-tt yiwen, ad d-iban da.",
+  "status.redraft": "Sfeḍ tɛiwdeḍ tira",
+  "status.remove_bookmark": "Kkes tacreḍt",
+  "status.reply": "Err",
+  "status.replyAll": "Reply to thread",
+  "status.report": "Cetki γef @{name}",
+  "status.sensitive_warning": "Agbur amḥulfu",
+  "status.share": "Bḍu",
+  "status.show_less": "Sken-d drus",
+  "status.show_less_all": "Semẓi akk tisuffγin",
+  "status.show_more": "Sken-ed ugar",
+  "status.show_more_all": "Ẓerr ugar lebda",
+  "status.show_thread": "Show thread",
+  "status.uncached_media_warning": "Ulac-it",
+  "status.unmute_conversation": "Kkes asgugem n udiwenni",
+  "status.unpin": "Kkes asenteḍ seg umaγnu",
+  "suggestions.dismiss": "Dismiss suggestion",
+  "suggestions.header": "Ahat ad tcelgeḍ deg…",
+  "tabs_bar.federated_timeline": "Amatu",
+  "tabs_bar.home": "Agejdan",
+  "tabs_bar.local_timeline": "Adigan",
+  "tabs_bar.notifications": "Tilγa",
+  "tabs_bar.search": "Nadi",
+  "time_remaining.days": "Mazal {number, plural, one {# n wass} other {# n wussan}}",
+  "time_remaining.hours": "Mazal {number, plural, one {# n usrag} other {# n yesragen}}",
+  "time_remaining.minutes": "Mazal {number, plural, one {# n tesdat} other {# n tesdatin}}",
+  "time_remaining.moments": "Moments remaining",
+  "time_remaining.seconds": "Mazal {number, plural, one {# n tasint} other {# n tsinin}}",
+  "trends.count_by_accounts": "{count} {rawCount, plural, one {n umdan} other {n yemdanen}} i yettmeslayen",
+  "trends.trending_now": "Trending now",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
+  "upload_area.title": "Drag & drop to upload",
+  "upload_button.label": "Add media ({formats})",
+  "upload_error.limit": "File upload limit exceeded.",
+  "upload_error.poll": "File upload not allowed with polls.",
+  "upload_form.audio_description": "Glem-d i yemdanen i yesɛan ugur deg tmesliwt",
+  "upload_form.description": "Glem-d i yemdaneni yesɛan ugur deg yiẓri",
+  "upload_form.edit": "Ẓreg",
+  "upload_form.undo": "Kkes",
+  "upload_form.video_description": "Glem-d i yemdanen i yesɛan ugur deg tmesliwt neγ deg yiẓri",
+  "upload_modal.analyzing_picture": "Tasleḍt n tugna tetteddu…",
+  "upload_modal.apply": "Snes",
+  "upload_modal.description_placeholder": "Aberraγ arurad ineggez nnig n uqjun amuṭṭis",
+  "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.preview_label": "Preview ({ratio})",
+  "upload_progress.label": "Asali iteddu...",
+  "video.close": "Mdel tabidyutt",
+  "video.download": "Sidered afaylu",
+  "video.exit_fullscreen": "Ffeγ seg ugdil aččuran",
+  "video.expand": "Semγeṛ tavidyut",
+  "video.fullscreen": "Agdil aččuran",
+  "video.hide": "Ffer tabidyutt",
+  "video.mute": "Gzem imesli",
+  "video.pause": "Sgunfu",
+  "video.play": "Seddu",
+  "video.unmute": "Rmed imesli"
+}
diff --git a/app/javascript/mastodon/locales/kk.json b/app/javascript/mastodon/locales/kk.json
index ba13c7ddf..b307a3161 100644
--- a/app/javascript/mastodon/locales/kk.json
+++ b/app/javascript/mastodon/locales/kk.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Тізімге қосу немесе жою",
   "account.badges.bot": "Бот",
+  "account.badges.group": "Group",
   "account.block": "Бұғаттау @{name}",
   "account.block_domain": "Домендегі барлығын бұғатта {domain}",
   "account.blocked": "Бұғатталды",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Бағалау шектеулі",
   "alert.unexpected.message": "Бір нәрсе дұрыс болмады.",
   "alert.unexpected.title": "Өй!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} аптасына",
   "boost_modal.combo": "Келесіде өткізіп жіберу үшін басыңыз {combo}",
   "bundle_column_error.body": "Бұл компонентті жүктеген кезде бір қате пайда болды.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Бұл компонентті жүктеген кезде бір қате пайда болды.",
   "bundle_modal_error.retry": "Қайтадан көріңіз",
   "column.blocks": "Бұғатталғандар",
+  "column.bookmarks": "Бетбелгілер",
   "column.community": "Жергілікті желі",
   "column.direct": "Жеке хаттар",
   "column.directory": "Профильдерді аралау",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Сауалнама мерзімі",
   "compose_form.poll.option_placeholder": "Жауап {number}",
   "compose_form.poll.remove_option": "Бұл жауапты өшір",
+  "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": "Түрт",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Сезімтал ретінде белгіле",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Жазба жоқ ешқандай!",
   "empty_column.account_unavailable": "Профиль қолжетімді емес",
   "empty_column.blocks": "Ешкімді бұғаттамағансыз.",
+  "empty_column.bookmarked_statuses": "Ешқандай жазба Бетбелгілер тізіміне қосылмапты. Қосылғаннан кейін осында жинала бастайды.",
   "empty_column.community": "Жергілікті желі бос. Сіз бастап жазыңыз!",
   "empty_column.direct": "Әзірше дым хат жоқ. Өзіңіз жазып көріңіз алдымен.",
   "empty_column.domain_blocks": "Бұғатталған домен жоқ.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "үнсіздер тізімін ашу",
   "keyboard_shortcuts.my_profile": "профиліңізді ашу",
   "keyboard_shortcuts.notifications": "ескертпелер бағанын ашу",
+  "keyboard_shortcuts.open_media": "медианы ашу үшін",
   "keyboard_shortcuts.pinned": "жабыстырылған жазбаларды көру",
   "keyboard_shortcuts.profile": "автор профилін қарау",
   "keyboard_shortcuts.reply": "жауап жазу",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Бұл қолданушы ескертпелерін жасырамыз ба?",
   "navigation_bar.apps": "Мобиль қосымшалар",
   "navigation_bar.blocks": "Бұғатталғандар",
+  "navigation_bar.bookmarks": "Бетбелгілер",
   "navigation_bar.community_timeline": "Жергілікті желі",
   "navigation_bar.compose": "Жаңа жазба бастау",
   "navigation_bar.direct": "Жеке хаттар",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Қауіпсіздік",
   "notification.favourite": "{name} жазбаңызды таңдаулыға қосты",
   "notification.follow": "{name} сізге жазылды",
+  "notification.follow_request": "{name} сізге жазылғысы келеді",
   "notification.mention": "{name} сізді атап өтті",
+  "notification.own_poll": "Сауалнама аяқталды",
   "notification.poll": "Бұл сауалнаманың мерзімі аяқталыпты",
   "notification.reblog": "{name} жазбаңызды бөлісті",
   "notifications.clear": "Ескертпелерді тазарт",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Жедел сүзгі",
   "notifications.column_settings.filter_bar.show": "Көрсету",
   "notifications.column_settings.follow": "Жаңа оқырмандар:",
+  "notifications.column_settings.follow_request": "Жазылуға жаңа сұранымдар:",
   "notifications.column_settings.mention": "Аталымдар:",
   "notifications.column_settings.poll": "Нәтижелері:",
   "notifications.column_settings.push": "Push ескертпелер",
@@ -344,6 +355,7 @@
   "status.admin_account": "@{name} үшін модерация интерфейсін аш",
   "status.admin_status": "Бұл жазбаны модерация интерфейсінде аш",
   "status.block": "Бұғаттау @{name}",
+  "status.bookmark": "Бетбелгі",
   "status.cancel_reblog_private": "Бөліспеу",
   "status.cannot_reblog": "Бұл жазба бөлісілмейді",
   "status.copy": "Жазба сілтемесін көшір",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} бөлісті",
   "status.reblogs.empty": "Бұл жазбаны әлі ешкім бөліспеді. Біреу бөліскен кезде осында көрінеді.",
   "status.redraft": "Өшіру & қайта қарастыру",
+  "status.remove_bookmark": "Бетбелгілерден алып тастау",
   "status.reply": "Жауап",
   "status.replyAll": "Тақырыпқа жауап",
   "status.report": "Шағым @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Медиа қосу (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_error.limit": "Файл жүктеу лимитінен асып кеттіңіз.",
   "upload_error.poll": "Сауалнамамен бірге файл жүктеуге болмайды.",
+  "upload_form.audio_description": "Есту қабілеті нашар адамдарға сипаттама беріңіз",
   "upload_form.description": "Көру қабілеті нашар адамдар үшін сипаттаңыз",
   "upload_form.edit": "Түзету",
   "upload_form.undo": "Өшіру",
+  "upload_form.video_description": "Есту немесе көру қабілеті нашар адамдарға сипаттама беріңіз",
   "upload_modal.analyzing_picture": "Суретті анализ жасау…",
   "upload_modal.apply": "Қолдану",
   "upload_modal.description_placeholder": "Щучинск съезіндегі өрт пе? Вагон-үй, аэромобиль һәм ұшақ фюзеляжы цехінен ғой",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Превью ({ratio})",
   "upload_progress.label": "Жүктеп жатыр...",
   "video.close": "Видеоны жабу",
+  "video.download": "Файлды түсіру",
   "video.exit_fullscreen": "Толық экраннан шық",
   "video.expand": "Видеоны аш",
   "video.fullscreen": "Толық экран",
diff --git a/app/javascript/mastodon/locales/kn.json b/app/javascript/mastodon/locales/kn.json
index 39ca86a0c..278f6b14c 100644
--- a/app/javascript/mastodon/locales/kn.json
+++ b/app/javascript/mastodon/locales/kn.json
@@ -1,6 +1,7 @@
 {
   "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": "Hide everything from {domain}",
   "account.blocked": "Blocked",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Rate limited",
   "alert.unexpected.message": "An unexpected error occurred.",
   "alert.unexpected.title": "Oops!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} per week",
   "boost_modal.combo": "You can press {combo} to skip this next time",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Something went wrong while loading this component.",
   "bundle_modal_error.retry": "Try again",
   "column.blocks": "Blocked users",
+  "column.bookmarks": "Bookmarks",
   "column.community": "Local timeline",
   "column.direct": "Direct messages",
   "column.directory": "Browse profiles",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Poll duration",
   "compose_form.poll.option_placeholder": "Choice {number}",
   "compose_form.poll.remove_option": "Remove this choice",
+  "compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices",
+  "compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
   "compose_form.publish": "Toot",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Mark media as sensitive",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
   "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no hidden domains yet.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "to open muted users list",
   "keyboard_shortcuts.my_profile": "to open your profile",
   "keyboard_shortcuts.notifications": "to open notifications column",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "to open pinned toots list",
   "keyboard_shortcuts.profile": "to open author's profile",
   "keyboard_shortcuts.reply": "to reply",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blocked users",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Security",
   "notification.favourite": "{name} favourited your status",
   "notification.follow": "{name} followed you",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} mentioned you",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "A poll you have voted in has ended",
   "notification.reblog": "{name} boosted your status",
   "notifications.clear": "Clear notifications",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Quick filter bar",
   "notifications.column_settings.filter_bar.show": "Show",
   "notifications.column_settings.follow": "New followers:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "Mentions:",
   "notifications.column_settings.poll": "Poll results:",
   "notifications.column_settings.push": "Push notifications",
@@ -344,6 +355,7 @@
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_status": "Open this status in the moderation interface",
   "status.block": "Block @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.copy": "Copy link to status",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} boosted",
   "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
   "status.redraft": "Delete & re-draft",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "Reply",
   "status.replyAll": "Reply to thread",
   "status.report": "Report @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Add media ({formats})",
   "upload_error.limit": "File upload limit exceeded.",
   "upload_error.poll": "File upload not allowed with polls.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "Describe for the visually impaired",
   "upload_form.edit": "Edit",
   "upload_form.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.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "Uploading…",
   "video.close": "Close video",
+  "video.download": "Download file",
   "video.exit_fullscreen": "Exit full screen",
   "video.expand": "Expand video",
   "video.fullscreen": "Full screen",
diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json
index e7c59d68f..5a3b409c0 100644
--- a/app/javascript/mastodon/locales/ko.json
+++ b/app/javascript/mastodon/locales/ko.json
@@ -1,19 +1,20 @@
 {
   "account.add_or_remove_from_list": "리스트에 추가 혹은 삭제",
   "account.badges.bot": "봇",
+  "account.badges.group": "그룹",
   "account.block": "@{name}을 차단",
   "account.block_domain": "{domain} 전체를 숨김",
-  "account.blocked": "차단 됨",
+  "account.blocked": "차단됨",
   "account.cancel_follow_request": "팔로우 요청 취소",
-  "account.direct": "@{name}으로부터의 다이렉트 메시지",
+  "account.direct": "@{name}의 다이렉트 메시지",
   "account.domain_blocked": "도메인 숨겨짐",
   "account.edit_profile": "프로필 편집",
-  "account.endorse": "프로필에 나타내기",
+  "account.endorse": "프로필에 보이기",
   "account.follow": "팔로우",
   "account.followers": "팔로워",
-  "account.followers.empty": "아직 아무도 이 유저를 팔로우 하고 있지 않습니다.",
+  "account.followers.empty": "아직 아무도 이 유저를 팔로우하고 있지 않습니다.",
   "account.follows": "팔로우",
-  "account.follows.empty": "이 유저는 아직 아무도 팔로우 하고 있지 않습니다.",
+  "account.follows.empty": "이 유저는 아직 아무도 팔로우하고 있지 않습니다.",
   "account.follows_you": "날 팔로우합니다",
   "account.hide_reblogs": "@{name}의 부스트를 숨기기",
   "account.last_status": "마지막 활동",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "빈도 제한",
   "alert.unexpected.message": "예측하지 못한 에러가 발생했습니다.",
   "alert.unexpected.title": "앗!",
+  "announcement.announcement": "공지사항",
   "autosuggest_hashtag.per_week": "주간 {count}회",
   "boost_modal.combo": "{combo}를 누르면 다음부터 이 과정을 건너뛸 수 있습니다",
   "bundle_column_error.body": "컴포넌트를 불러오는 과정에서 문제가 발생했습니다.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "컴포넌트를 불러오는 과정에서 문제가 발생했습니다.",
   "bundle_modal_error.retry": "다시 시도",
   "column.blocks": "차단 중인 사용자",
+  "column.bookmarks": "갈무리",
   "column.community": "로컬 타임라인",
   "column.direct": "다이렉트 메시지",
   "column.directory": "프로필 둘러보기",
@@ -82,6 +85,8 @@
   "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_loud": "{publish}!",
   "compose_form.sensitive.hide": "미디어를 민감함으로 설정하기",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "여긴 툿이 없어요!",
   "empty_column.account_unavailable": "프로필 사용 불가",
   "empty_column.blocks": "아직 아무도 차단하지 않았습니다.",
+  "empty_column.bookmarked_statuses": "아직 갈무리한 툿이 없습니다. 툿을 갈무리하면 여기에 나타납니다.",
   "empty_column.community": "로컬 타임라인에 아무 것도 없습니다. 아무거나 적어 보세요!",
   "empty_column.direct": "아직 다이렉트 메시지가 없습니다. 다이렉트 메시지를 보내거나 받은 경우, 여기에 표시 됩니다.",
   "empty_column.domain_blocks": "아직 숨겨진 도메인이 없습니다.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "뮤트 된 유저 리스트 열기",
   "keyboard_shortcuts.my_profile": "내 프로필 열기",
   "keyboard_shortcuts.notifications": "알림 컬럼 열기",
+  "keyboard_shortcuts.open_media": "미디어 열기",
   "keyboard_shortcuts.pinned": "고정 툿 리스트 열기",
   "keyboard_shortcuts.profile": "프로필 열기",
   "keyboard_shortcuts.reply": "답장",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "이 사용자로부터의 알림을 숨기시겠습니까?",
   "navigation_bar.apps": "모바일 앱",
   "navigation_bar.blocks": "차단한 사용자",
+  "navigation_bar.bookmarks": "갈무리",
   "navigation_bar.community_timeline": "로컬 타임라인",
   "navigation_bar.compose": "새 툿 작성",
   "navigation_bar.direct": "다이렉트 메시지",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "보안",
   "notification.favourite": "{name}님이 즐겨찾기 했습니다",
   "notification.follow": "{name}님이 나를 팔로우 했습니다",
+  "notification.follow_request": "{name}님이 팔로우 요청을 보냈습니다",
   "notification.mention": "{name}님이 답글을 보냈습니다",
+  "notification.own_poll": "내 투표가 끝났습니다",
   "notification.poll": "당신이 참여 한 투표가 종료되었습니다",
   "notification.reblog": "{name}님이 부스트 했습니다",
   "notifications.clear": "알림 지우기",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "퀵 필터 바",
   "notifications.column_settings.filter_bar.show": "표시",
   "notifications.column_settings.follow": "새 팔로워:",
+  "notifications.column_settings.follow_request": "새 팔로우 요청:",
   "notifications.column_settings.mention": "답글:",
   "notifications.column_settings.poll": "투표 결과:",
   "notifications.column_settings.push": "푸시 알림",
@@ -341,9 +352,10 @@
   "search_results.statuses": "툿",
   "search_results.statuses_fts_disabled": "이 마스토돈 서버에선 툿의 내용을 통한 검색이 활성화 되어 있지 않습니다.",
   "search_results.total": "{count, number}건의 결과",
-  "status.admin_account": "@{name}에 대한 모더레이션 인터페이스 열기",
-  "status.admin_status": "모더레이션 인터페이스에서 이 게시물 열기",
+  "status.admin_account": "@{name}에 대한 중재 화면 열기",
+  "status.admin_status": "중재 화면에서 이 게시물 열기",
   "status.block": "@{name} 차단",
+  "status.bookmark": "갈무리",
   "status.cancel_reblog_private": "부스트 취소",
   "status.cannot_reblog": "이 포스트는 부스트 할 수 없습니다",
   "status.copy": "게시물 링크 복사",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name}님이 부스트 했습니다",
   "status.reblogs.empty": "아직 아무도 이 툿을 부스트하지 않았습니다. 부스트 한 사람들이 여기에 표시 됩니다.",
   "status.redraft": "지우고 다시 쓰기",
+  "status.remove_bookmark": "갈무리 삭제",
   "status.reply": "답장",
   "status.replyAll": "전원에게 답장",
   "status.report": "신고",
@@ -400,9 +413,11 @@
   "upload_button.label": "미디어 추가 (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_error.limit": "파일 업로드 제한에 도달했습니다.",
   "upload_error.poll": "파일 업로드는 투표와 함께 첨부할 수 없습니다.",
+  "upload_form.audio_description": "청각 장애인을 위한 설명",
   "upload_form.description": "시각장애인을 위한 설명",
   "upload_form.edit": "편집",
   "upload_form.undo": "삭제",
+  "upload_form.video_description": "청각, 시각 장애인을 위한 설명",
   "upload_modal.analyzing_picture": "이미지 분석 중…",
   "upload_modal.apply": "적용",
   "upload_modal.description_placeholder": "다람쥐 헌 쳇바퀴 타고파",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "미리보기 ({ratio})",
   "upload_progress.label": "업로드 중...",
   "video.close": "동영상 닫기",
+  "video.download": "파일 다운로드",
   "video.exit_fullscreen": "전체화면 나가기",
   "video.expand": "동영상 확장",
   "video.fullscreen": "전체화면",
diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json
index 39ca86a0c..278f6b14c 100644
--- a/app/javascript/mastodon/locales/lt.json
+++ b/app/javascript/mastodon/locales/lt.json
@@ -1,6 +1,7 @@
 {
   "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": "Hide everything from {domain}",
   "account.blocked": "Blocked",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Rate limited",
   "alert.unexpected.message": "An unexpected error occurred.",
   "alert.unexpected.title": "Oops!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} per week",
   "boost_modal.combo": "You can press {combo} to skip this next time",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Something went wrong while loading this component.",
   "bundle_modal_error.retry": "Try again",
   "column.blocks": "Blocked users",
+  "column.bookmarks": "Bookmarks",
   "column.community": "Local timeline",
   "column.direct": "Direct messages",
   "column.directory": "Browse profiles",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Poll duration",
   "compose_form.poll.option_placeholder": "Choice {number}",
   "compose_form.poll.remove_option": "Remove this choice",
+  "compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices",
+  "compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
   "compose_form.publish": "Toot",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Mark media as sensitive",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
   "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no hidden domains yet.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "to open muted users list",
   "keyboard_shortcuts.my_profile": "to open your profile",
   "keyboard_shortcuts.notifications": "to open notifications column",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "to open pinned toots list",
   "keyboard_shortcuts.profile": "to open author's profile",
   "keyboard_shortcuts.reply": "to reply",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blocked users",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Security",
   "notification.favourite": "{name} favourited your status",
   "notification.follow": "{name} followed you",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} mentioned you",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "A poll you have voted in has ended",
   "notification.reblog": "{name} boosted your status",
   "notifications.clear": "Clear notifications",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Quick filter bar",
   "notifications.column_settings.filter_bar.show": "Show",
   "notifications.column_settings.follow": "New followers:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "Mentions:",
   "notifications.column_settings.poll": "Poll results:",
   "notifications.column_settings.push": "Push notifications",
@@ -344,6 +355,7 @@
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_status": "Open this status in the moderation interface",
   "status.block": "Block @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.copy": "Copy link to status",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} boosted",
   "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
   "status.redraft": "Delete & re-draft",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "Reply",
   "status.replyAll": "Reply to thread",
   "status.report": "Report @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Add media ({formats})",
   "upload_error.limit": "File upload limit exceeded.",
   "upload_error.poll": "File upload not allowed with polls.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "Describe for the visually impaired",
   "upload_form.edit": "Edit",
   "upload_form.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.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "Uploading…",
   "video.close": "Close video",
+  "video.download": "Download file",
   "video.exit_fullscreen": "Exit full screen",
   "video.expand": "Expand video",
   "video.fullscreen": "Full screen",
diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json
index 1c0e35501..b2f8fedbf 100644
--- a/app/javascript/mastodon/locales/lv.json
+++ b/app/javascript/mastodon/locales/lv.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Pievienot vai noņemt no saraksta",
   "account.badges.bot": "Bots",
+  "account.badges.group": "Group",
   "account.block": "Bloķēt @{name}",
   "account.block_domain": "Slēpt visu no {domain}",
   "account.blocked": "Bloķēts",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Rate limited",
   "alert.unexpected.message": "Negaidīta kļūda.",
   "alert.unexpected.title": "Ups!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} per week",
   "boost_modal.combo": "Nospied {combo} lai izlaistu šo nākamreiz",
   "bundle_column_error.body": "Kaut kas nogāja greizi ielādējot šo komponenti.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Kaut kas nogāja greizi ielādējot šo komponenti.",
   "bundle_modal_error.retry": "Mēģini vēlreiz",
   "column.blocks": "Bloķētie lietotāji",
+  "column.bookmarks": "Bookmarks",
   "column.community": "Lokālā laika līnija",
   "column.direct": "Privātās ziņas",
   "column.directory": "Browse profiles",
@@ -82,6 +85,8 @@
   "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": "Publicēt",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Mark media as sensitive",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Šeit ziņojumu nav!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "Tu neesi vēl nevienu bloķējis.",
+  "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "Lokālā laika līnija ir tukša. :/ Ieraksti kaut ko lai sākas rosība!",
   "empty_column.direct": "Tev nav privāto ziņu. Tiklīdz saņemsi tās šeit parādīsies.",
   "empty_column.domain_blocks": "Slēpto domēnu vēl nav.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "to open muted users list",
   "keyboard_shortcuts.my_profile": "to open your profile",
   "keyboard_shortcuts.notifications": "to open notifications column",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "to open pinned toots list",
   "keyboard_shortcuts.profile": "to open author's profile",
   "keyboard_shortcuts.reply": "to reply",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blocked users",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Security",
   "notification.favourite": "{name} favourited your status",
   "notification.follow": "{name} followed you",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} mentioned you",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "A poll you have voted in has ended",
   "notification.reblog": "{name} boosted your status",
   "notifications.clear": "Clear notifications",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Quick filter bar",
   "notifications.column_settings.filter_bar.show": "Show",
   "notifications.column_settings.follow": "New followers:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "Mentions:",
   "notifications.column_settings.poll": "Poll results:",
   "notifications.column_settings.push": "Push notifications",
@@ -344,6 +355,7 @@
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_status": "Open this status in the moderation interface",
   "status.block": "Block @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.copy": "Copy link to status",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} boosted",
   "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
   "status.redraft": "Delete & re-draft",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "Reply",
   "status.replyAll": "Reply to thread",
   "status.report": "Report @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Add media ({formats})",
   "upload_error.limit": "File upload limit exceeded.",
   "upload_error.poll": "File upload not allowed with polls.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "Describe for the visually impaired",
   "upload_form.edit": "Edit",
   "upload_form.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.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "Uploading…",
   "video.close": "Close video",
+  "video.download": "Download file",
   "video.exit_fullscreen": "Exit full screen",
   "video.expand": "Expand video",
   "video.fullscreen": "Full screen",
diff --git a/app/javascript/mastodon/locales/mk.json b/app/javascript/mastodon/locales/mk.json
index 5189e8ea8..90c8d2418 100644
--- a/app/javascript/mastodon/locales/mk.json
+++ b/app/javascript/mastodon/locales/mk.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Додади или одстрани од листа",
   "account.badges.bot": "Бот",
+  "account.badges.group": "Group",
   "account.block": "Блокирај @{name}",
   "account.block_domain": "Сокријај се од {domain}",
   "account.blocked": "Блокиран",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Rate limited",
   "alert.unexpected.message": "Неочекувана грешка.",
   "alert.unexpected.title": "Упс!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} неделно",
   "boost_modal.combo": "Кликни {combo} за да го прескокниш ова нареден пат",
   "bundle_column_error.body": "Се случи проблем при вчитувањето.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Настана грешка при прикажувањето на оваа веб-страница.",
   "bundle_modal_error.retry": "Обидете се повторно",
   "column.blocks": "Блокирани корисници",
+  "column.bookmarks": "Bookmarks",
   "column.community": "Локална временска зона",
   "column.direct": "Директна порака",
   "column.directory": "Види профили",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Времетрање на анкета",
   "compose_form.poll.option_placeholder": "Избери {number}",
   "compose_form.poll.remove_option": "Избриши избор",
+  "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": "Тутови",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Обележи медиа како сензитивна",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Недостапен профил",
   "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": "Немате директни пораки. Кога ќе пратите или примите, ќе се појават тука.",
   "empty_column.domain_blocks": "Немате сокриени домеини уште.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "to open muted users list",
   "keyboard_shortcuts.my_profile": "to open your profile",
   "keyboard_shortcuts.notifications": "to open notifications column",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "to open pinned toots list",
   "keyboard_shortcuts.profile": "to open author's profile",
   "keyboard_shortcuts.reply": "одговори",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blocked users",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Безбедност",
   "notification.favourite": "{name} favourited your status",
   "notification.follow": "{name} followed you",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} mentioned you",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "A poll you have voted in has ended",
   "notification.reblog": "{name} boosted your status",
   "notifications.clear": "Clear notifications",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Quick filter bar",
   "notifications.column_settings.filter_bar.show": "Show",
   "notifications.column_settings.follow": "New followers:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "Mentions:",
   "notifications.column_settings.poll": "Резултати од анкета:",
   "notifications.column_settings.push": "Пуш нотификации",
@@ -344,6 +355,7 @@
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_status": "Open this status in the moderation interface",
   "status.block": "Block @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.copy": "Copy link to status",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} boosted",
   "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
   "status.redraft": "Delete & re-draft",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "Reply",
   "status.replyAll": "Reply to thread",
   "status.report": "Report @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Add media ({formats})",
   "upload_error.limit": "File upload limit exceeded.",
   "upload_error.poll": "File upload not allowed with polls.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "Describe for the visually impaired",
   "upload_form.edit": "Edit",
   "upload_form.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.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "Uploading…",
   "video.close": "Close video",
+  "video.download": "Download file",
   "video.exit_fullscreen": "Exit full screen",
   "video.expand": "Expand video",
   "video.fullscreen": "Full screen",
diff --git a/app/javascript/mastodon/locales/ml.json b/app/javascript/mastodon/locales/ml.json
index f331590de..6a042e8c4 100644
--- a/app/javascript/mastodon/locales/ml.json
+++ b/app/javascript/mastodon/locales/ml.json
@@ -1,16 +1,17 @@
 {
-  "account.add_or_remove_from_list": "Add or Remove from lists",
+  "account.add_or_remove_from_list": "പട്ടികയിൽ ചേർക്കുകയോ അല്ലെങ്കിൽ മാറ്റുകയോ ചെയ്യുക",
   "account.badges.bot": "റോബോട്ട്",
+  "account.badges.group": "Group",
   "account.block": "@{name} നെ ബ്ലോക്ക് ചെയ്യുക",
-  "account.block_domain": "{domain} ഇൽ നിന്നുള്ള എല്ലാം മറയ്കുക",
+  "account.block_domain": "{domain} ൽ നിന്നുള്ള എല്ലാം മറയ്കുക",
   "account.blocked": "തടഞ്ഞു",
-  "account.cancel_follow_request": "Cancel follow request",
-  "account.direct": "നേരിട്ട് മെസേജ് അയക്കുക{name}",
-  "account.domain_blocked": "Domain hidden",
+  "account.cancel_follow_request": "പിന്തുടരാനുള്ള അപേക്ഷ നിരസിക്കുക",
+  "account.direct": "@{name} ന് നേരിട്ട് മെസേജ് അയക്കുക",
+  "account.domain_blocked": "മേഖല മറയ്ക്കപ്പെട്ടിരിക്കുന്നു",
   "account.edit_profile": "പ്രൊഫൈൽ തിരുത്തുക",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "പ്രൊഫൈലിൽ പ്രകടമാക്കുക",
   "account.follow": "പിന്തുടരുക",
-  "account.followers": "നിങ്ങളെ പിന്തുടരുന്നവർ",
+  "account.followers": "പിന്തുടരുന്നവർ",
   "account.followers.empty": "ഈ ഉപയോക്താവിനെ ആരും ഇതുവരെ പിന്തുടരുന്നില്ല.",
   "account.follows": "പിന്തുടരുന്നു",
   "account.follows.empty": "ഈ ഉപയോക്താവ് ആരേയും ഇതുവരെ പിന്തുടരുന്നില്ല.",
@@ -18,131 +19,136 @@
   "account.hide_reblogs": "@{name} ബൂസ്റ്റ് ചെയ്തവ മറയ്കുക",
   "account.last_status": "അവസാനം കണ്ടത്",
   "account.link_verified_on": "ഈ ലിങ്കിന്റെ ഉടമസ്തത {date} ഇൽ ഉറപ്പാക്കിയതാണ്",
-  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
+  "account.locked_info": "ഈ അംഗത്വത്തിന്റെ സ്വകാര്യതാ നിലപാട് അനുസരിച്ച് പിന്തുടരുന്നവരെ തിരഞ്ഞെടുക്കാനുള്ള വിവേചനാധികാരം ഉടമസ്ഥനിൽ നിഷിപ്തമായിരിക്കുന്നു.",
   "account.media": "മീഡിയ",
-  "account.mention": "Mention @{name}",
-  "account.moved_to": "{name} has moved to:",
+  "account.mention": "@{name} സൂചിപ്പിക്കുക",
+  "account.moved_to": "{name} ഇതിലേക്ക് മാറിയിരിക്കുന്നു:",
   "account.mute": "നിശ്ശബ്ദമാക്കുക @{name}",
-  "account.mute_notifications": "Mute notifications from @{name}",
+  "account.mute_notifications": "@{name} യിൽ നിന്നുള്ള അറിയിപ്പുകൾ നിശബ്ദമാക്കുക",
   "account.muted": "നിശ്ശബ്ദമാക്കിയിരിക്കുന്നു",
-  "account.never_active": "ഒരിക്കലും വേണ്ട",
+  "account.never_active": "ഒരിക്കലും ഇല്ല",
   "account.posts": "ടൂട്ടുകൾ",
   "account.posts_with_replies": "ടൂട്ടുകളും മറുപടികളും",
   "account.report": "റിപ്പോർട്ട് ചെയ്യുക @{name}",
-  "account.requested": "Awaiting approval",
-  "account.share": "Share @{name}'s profile",
-  "account.show_reblogs": "Show boosts from @{name}",
+  "account.requested": "അനുവാദത്തിനായി കാത്തിരിക്കുന്നു. പിന്തുടരാനുള്ള അപേക്ഷ റദ്ദാക്കുവാൻ ഞെക്കുക",
+  "account.share": "@{name} ന്റെ പ്രൊഫൈൽ പങ്കുവെക്കുക",
+  "account.show_reblogs": "@{name} ൽ നിന്നുള്ള ബൂസ്റ്റുകൾ കാണിക്കുക",
   "account.unblock": "ബ്ലോക്ക് മാറ്റുക @{name}",
-  "account.unblock_domain": "Unhide {domain}",
-  "account.unendorse": "Don't feature on profile",
+  "account.unblock_domain": "{domain} വെളിപ്പെടുത്തുക",
+  "account.unendorse": "പ്രൊഫൈലിൽ പ്രകടമാക്കാതിരിക്കുക",
   "account.unfollow": "പിന്തുടരുന്നത് നിര്‍ത്തുക",
   "account.unmute": "നിശ്ശബ്ദമാക്കുന്നത് നിർത്തുക @{name}",
-  "account.unmute_notifications": "Unmute notifications from @{name}",
-  "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.",
-  "alert.rate_limited.title": "Rate limited",
+  "account.unmute_notifications": "@{name} യിൽ നിന്നുള്ള അറിയിപ്പുകൾ പ്രസിദ്ധപ്പെടുത്തുക",
+  "alert.rate_limited.message": "{retry_time, time, medium} നു ശേഷം വീണ്ടും ശ്രമിക്കുക.",
+  "alert.rate_limited.title": "തോത് പരിമിതപ്പെടുത്തിയിരിക്കുന്നു",
   "alert.unexpected.message": "അപ്രതീക്ഷിതമായി എന്തോ സംഭവിച്ചു.",
   "alert.unexpected.title": "ശ്ശോ!",
-  "autosuggest_hashtag.per_week": "{count} per week",
-  "boost_modal.combo": "You can press {combo} to skip this next time",
-  "bundle_column_error.body": "Something went wrong while loading this component.",
+  "announcement.announcement": "Announcement",
+  "autosuggest_hashtag.per_week": "ആഴ്ച തോറും {count}",
+  "boost_modal.combo": "അടുത്ത തവണ ഇത് ഒഴിവാക്കുവാൻ {combo} ഞെക്കാവുന്നതാണ്",
+  "bundle_column_error.body": "ഈ ഘടകം പ്രദശിപ്പിക്കുമ്പോൾ എന്തോ കുഴപ്പം സംഭവിച്ചു.",
   "bundle_column_error.retry": "വീണ്ടും ശ്രമിക്കുക",
-  "bundle_column_error.title": "Network error",
+  "bundle_column_error.title": "ശൃംഖലയിലെ പിഴവ്",
   "bundle_modal_error.close": "അടയ്ക്കുക",
   "bundle_modal_error.message": "ഈ വെബ്പേജ് പ്രദർശിപ്പിക്കുമ്പോൾ എന്തോ കുഴപ്പം സംഭവിച്ചു.",
   "bundle_modal_error.retry": "വീണ്ടും ശ്രമിക്കുക",
-  "column.blocks": "Blocked users",
-  "column.community": "Local timeline",
-  "column.direct": "Direct messages",
-  "column.directory": "Browse profiles",
-  "column.domain_blocks": "Hidden domains",
-  "column.favourites": "Favourites",
-  "column.follow_requests": "Follow requests",
-  "column.home": "Home",
-  "column.lists": "Lists",
-  "column.mutes": "Muted users",
-  "column.notifications": "Notifications",
-  "column.pins": "Pinned toot",
-  "column.public": "Federated timeline",
-  "column_back_button.label": "Back",
-  "column_header.hide_settings": "Hide settings",
-  "column_header.moveLeft_settings": "Move column to the left",
-  "column_header.moveRight_settings": "Move column to the right",
-  "column_header.pin": "Pin",
-  "column_header.show_settings": "Show settings",
-  "column_header.unpin": "Unpin",
-  "column_subheading.settings": "Settings",
-  "community.column_settings.media_only": "Media only",
-  "compose_form.direct_message_warning": "This toot will only be sent to all the mentioned users.",
-  "compose_form.direct_message_warning_learn_more": "Learn more",
-  "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
+  "column.blocks": "തടയപ്പെട്ട ഉപയോക്താക്കൾ",
+  "column.bookmarks": "Bookmarks",
+  "column.community": "പ്രാദേശികമായ സമയരേഖ",
+  "column.direct": "നേരിട്ടുള്ള സന്ദേശങ്ങൾ",
+  "column.directory": "പ്രൊഫൈലുകൾ മറിച്ചുനോക്കുക",
+  "column.domain_blocks": "മറയ്ക്കപ്പെട്ട മേഖലകൾ",
+  "column.favourites": "പ്രിയപ്പെട്ടവ",
+  "column.follow_requests": "പിന്തുടരാനുള്ള അഭ്യർത്ഥനകൾ",
+  "column.home": "ഭവനം",
+  "column.lists": "പട്ടികകൾ",
+  "column.mutes": "നിശബ്ദമാക്കപ്പെട്ട ഉപയോക്താക്കൾ",
+  "column.notifications": "അറിയിപ്പുകൾ",
+  "column.pins": "ഉറപ്പിച്ചു നിറുത്തിയിരിക്കുന്ന ടൂട്ടുകൾ",
+  "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.media_only": "മാധ്യമങ്ങൾ മാത്രം",
+  "compose_form.direct_message_warning": "പരാമർശിക്കപ്പെട്ടിരിക്കുന്ന ഉപയോഗ്താക്കൾക്കെ ഈ ടൂട്ട് അയക്കപ്പെടുകയുള്ളു.",
+  "compose_form.direct_message_warning_learn_more": "കൂടുതൽ പഠിക്കുക",
+  "compose_form.hashtag_warning": "ഈ ടൂട്ട് പട്ടികയിൽ ഇല്ലാത്തതിനാൽ ഒരു ചർച്ചാവിഷയത്തിന്റെ പട്ടികയിലും പെടുകയില്ല. പരസ്യമായ ടൂട്ടുകൾ മാത്രമേ ചർച്ചാവിഷയം അടിസ്ഥാനമാക്കി തിരയുവാൻ സാധിക്കുകയുള്ളു.",
   "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
   "compose_form.lock_disclaimer.lock": "locked",
-  "compose_form.placeholder": "What is on your mind?",
+  "compose_form.placeholder": "നിങ്ങളുടെ മനസ്സിൽ എന്താണ്?",
   "compose_form.poll.add_option": "Add a choice",
-  "compose_form.poll.duration": "Poll duration",
+  "compose_form.poll.duration": "തിരഞ്ഞെടുപ്പിന്റെ സമയദൈർഖ്യം",
   "compose_form.poll.option_placeholder": "Choice {number}",
   "compose_form.poll.remove_option": "Remove this choice",
-  "compose_form.publish": "Toot",
+  "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": "ടൂട്ട്",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Mark media as sensitive",
   "compose_form.sensitive.marked": "Media is marked as sensitive",
   "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
-  "compose_form.spoiler.marked": "Text is hidden behind warning",
-  "compose_form.spoiler.unmarked": "Text is not hidden",
-  "compose_form.spoiler_placeholder": "Write your warning here",
-  "confirmation_modal.cancel": "Cancel",
+  "compose_form.spoiler.marked": "എഴുത്ത് മുന്നറിയിപ്പിനാൽ മറച്ചിരിക്കുന്നു",
+  "compose_form.spoiler.unmarked": "എഴുത്ത് മറയ്ക്കപ്പെട്ടിട്ടില്ല",
+  "compose_form.spoiler_placeholder": "നിങ്ങളുടെ മുന്നറിയിപ്പ് ഇവിടെ എഴുതുക",
+  "confirmation_modal.cancel": "റദ്ദാക്കുക",
   "confirmations.block.block_and_report": "Block & Report",
-  "confirmations.block.confirm": "Block",
+  "confirmations.block.confirm": "തടയുക",
   "confirmations.block.message": "Are you sure you want to block {name}?",
-  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.confirm": "മായ്ക്കുക",
   "confirmations.delete.message": "Are you sure you want to delete this status?",
-  "confirmations.delete_list.confirm": "Delete",
+  "confirmations.delete_list.confirm": "മായ്ക്കുക",
   "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?",
   "confirmations.domain_block.confirm": "Hide entire domain",
   "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.",
-  "confirmations.logout.confirm": "Log out",
+  "confirmations.logout.confirm": "പുറത്തുകടക്കുക",
   "confirmations.logout.message": "Are you sure you want to log out?",
-  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.confirm": "നിശ്ശബ്ദമാക്കുക",
   "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.",
   "confirmations.mute.message": "Are you sure you want to mute {name}?",
-  "confirmations.redraft.confirm": "Delete & redraft",
-  "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.",
-  "confirmations.reply.confirm": "Reply",
-  "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
-  "confirmations.unfollow.confirm": "Unfollow",
-  "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
-  "conversation.delete": "Delete conversation",
-  "conversation.mark_as_read": "Mark as read",
-  "conversation.open": "View conversation",
-  "conversation.with": "With {names}",
-  "directory.federated": "From known fediverse",
-  "directory.local": "From {domain} only",
-  "directory.new_arrivals": "New arrivals",
-  "directory.recently_active": "Recently active",
+  "confirmations.redraft.confirm": "മായിച്ച് മാറ്റങ്ങൾ വരുത്തി വീണ്ടും എഴുതുക",
+  "confirmations.redraft.message": "നിങ്ങൾ ഉറപ്പായും ഈ കുറിപ്പ് മായ്ച്ച് മാറ്റങ്ങൾ വരുത്തി വീണ്ടും എഴുതുവാൻ താല്പര്യപ്പെടുന്നുവോ? അങ്ങനെ ചെയ്യുന്ന പക്ഷം ഇതിനു ലഭിച്ചിരിക്കുന്ന പ്രിയപ്പെടലുകളും ബൂസ്റ്റുകളും ആദ്യമുണ്ടായിരുന്ന കുറിപ്പിന് ലഭിച്ചിരുന്ന മറുപടികൾ ഒറ്റപ്പെടുകയും ചെയ്യും.",
+  "confirmations.reply.confirm": "മറുപടി",
+  "confirmations.reply.message": "ഇപ്പോൾ മറുപടി കൊടുക്കുന്നത് നിങ്ങൾ എഴുതിക്കൊണ്ടിരിക്കുന്ന സന്ദേശത്തിന് മുകളിൽ എഴുതാൻ കാരണമാകും. തീർച്ചയായും മുൻപോട്ട് പോകാൻ തീരുമാനിച്ചുവോ?",
+  "confirmations.unfollow.confirm": "പിന്തുടരുന്നത് നിര്‍ത്തുക",
+  "confirmations.unfollow.message": "നിങ്ങൾ {name} യെ പിന്തുടരുന്നത് നിർത്തുവാൻ തീർച്ചയായും തീരുമാനിച്ചുവോ?",
+  "conversation.delete": "സംഭാഷണം മായിക്കുക",
+  "conversation.mark_as_read": "വായിച്ചതായി അടയാളപ്പെടുത്തുക",
+  "conversation.open": "സംഭാഷണം കാണുക",
+  "conversation.with": "{names} കൂടെ",
+  "directory.federated": "അറിയപ്പെടുന്ന ഫെഡിവേഴ്‌സ്ൽ നിന്ന്",
+  "directory.local": "{domain} ൽ നിന്ന് മാത്രം",
+  "directory.new_arrivals": "പുതിയ വരവുകൾ",
+  "directory.recently_active": "അടുത്തിടെയായി സജീവമായ",
   "embed.instructions": "Embed this status on your website by copying the code below.",
-  "embed.preview": "Here is what it will look like:",
-  "emoji_button.activity": "Activity",
-  "emoji_button.custom": "Custom",
-  "emoji_button.flags": "Flags",
-  "emoji_button.food": "Food & Drink",
-  "emoji_button.label": "Insert emoji",
-  "emoji_button.nature": "Nature",
+  "embed.preview": "ഇത് ഇങ്ങനെ കാണപ്പെടും:",
+  "emoji_button.activity": "പ്രവര്‍ത്തനം",
+  "emoji_button.custom": "സ്വന്തമായ ഭേദഗതി",
+  "emoji_button.flags": "കൊടികൾ",
+  "emoji_button.food": "ഭക്ഷണവും പാനീയവും",
+  "emoji_button.label": "ഇമോജി ചേർക്കുക",
+  "emoji_button.nature": "പ്രകൃതി",
   "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
-  "emoji_button.objects": "Objects",
-  "emoji_button.people": "People",
-  "emoji_button.recent": "Frequently used",
-  "emoji_button.search": "Search...",
-  "emoji_button.search_results": "Search results",
-  "emoji_button.symbols": "Symbols",
-  "emoji_button.travel": "Travel & Places",
-  "empty_column.account_timeline": "No toots here!",
-  "empty_column.account_unavailable": "Profile unavailable",
-  "empty_column.blocks": "You haven't blocked any users yet.",
-  "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
-  "empty_column.domain_blocks": "There are no hidden domains yet.",
-  "empty_column.favourited_statuses": "You don't have any favourite toots yet. When you favourite one, it will show up here.",
-  "empty_column.favourites": "No one has favourited this toot yet. When someone does, they will show up here.",
+  "emoji_button.objects": "വസ്തുക്കൾ",
+  "emoji_button.people": "ആളുകൾ",
+  "emoji_button.recent": "അടിക്കടി ഉപയോഗിക്കുന്നവ",
+  "emoji_button.search": "തിരയുക...",
+  "emoji_button.search_results": "തിരച്ചിൽ ഫലങ്ങൾ",
+  "emoji_button.symbols": "ചിഹ്നങ്ങൾ",
+  "emoji_button.travel": "യാത്രയും സ്ഥലങ്ങളും",
+  "empty_column.account_timeline": "ഇവിടെ ടൂട്ടുകൾ ഇല്ല!",
+  "empty_column.account_unavailable": "പ്രൊഫൈൽ ലഭ്യമല്ല",
+  "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": "നിങ്ങൾക്ക് ഇതുവരെ നേരിട്ടുള്ള സന്ദേശങ്ങൾ ഒന്നുമില്ല. നിങ്ങൾ അങ്ങനെ ഒന്ന് അയക്കുകയോ, നിങ്ങൾക്ക് ലഭിക്കുകയോ ചെയ്യുന്നപക്ഷം അതിവിടെ കാണപ്പെടുന്നതാണ്.",
+  "empty_column.domain_blocks": "മറയ്ക്കപ്പെട്ടിരിക്കുന്ന മേഖലകൾ ഇതുവരെ ഇല്ല.",
+  "empty_column.favourited_statuses": "നിങ്ങൾക്ക് ഇത് വരെ ഒരു പ്രിയപ്പെട്ട ടൂട്ടും ഇല്ല. നിങ്ങൾ അങ്ങനെ ഒന്ന് പ്രിയപ്പെടുന്ന പക്ഷം അതിവിടെ കാണപ്പെടുന്നതാണ്.",
+  "empty_column.favourites": "ഇതുവരെ ആരും ഈ ടൂട്ട് പ്രിയപ്പെട്ടതായി അടയാളപ്പെടുത്തിയിട്ടില്ല. ആരെങ്കിലും അങ്ങനെ ചെയ്യുന്നപക്ഷം അതിവിടെ കാണപ്പെടുന്നതാണ്.",
   "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
@@ -150,67 +156,67 @@
   "empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.",
   "empty_column.lists": "You don't have any lists yet. When you create one, it will show up here.",
   "empty_column.mutes": "You haven't muted any users yet.",
-  "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
-  "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other servers to fill it up",
+  "empty_column.notifications": "നിങ്ങൾക്ക് ഇതുവരെ ഒരു അറിയിപ്പുകളും ഇല്ല. മറ്റുള്ളവരുമായി ഇടപെട്ട് സംഭാഷണത്തിന് തുടക്കം കുറിക്കു.",
+  "empty_column.public": "ഇവിടെ ഒന്നുമില്ലല്ലോ! ഇവിടെ നിറയ്ക്കാൻ എന്തെങ്കിലും പരസ്യമായി എഴുതുകയോ മറ്റ് ഉപഭോക്താക്കളെ പിന്തുടരുകയോ ചെയ്യുക",
   "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.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.",
   "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard",
-  "errors.unexpected_crash.report_issue": "Report issue",
-  "follow_request.authorize": "Authorize",
-  "follow_request.reject": "Reject",
-  "getting_started.developers": "Developers",
-  "getting_started.directory": "Profile directory",
-  "getting_started.documentation": "Documentation",
-  "getting_started.heading": "Getting started",
-  "getting_started.invite": "Invite people",
-  "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}.",
-  "getting_started.security": "Security",
-  "getting_started.terms": "Terms of service",
-  "hashtag.column_header.tag_mode.all": "and {additional}",
-  "hashtag.column_header.tag_mode.any": "or {additional}",
-  "hashtag.column_header.tag_mode.none": "without {additional}",
-  "hashtag.column_settings.select.no_options_message": "No suggestions found",
-  "hashtag.column_settings.select.placeholder": "Enter hashtags…",
-  "hashtag.column_settings.tag_mode.all": "All of these",
-  "hashtag.column_settings.tag_mode.any": "Any of these",
-  "hashtag.column_settings.tag_mode.none": "None of these",
-  "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
-  "home.column_settings.basic": "Basic",
-  "home.column_settings.show_reblogs": "Show boosts",
-  "home.column_settings.show_replies": "Show replies",
+  "errors.unexpected_crash.report_issue": "പ്രശ്നം അറിയിക്കുക",
+  "follow_request.authorize": "ചുമതലപ്പെടുത്തുക",
+  "follow_request.reject": "നിരസിക്കുക",
+  "getting_started.developers": "വികസിപ്പിക്കുന്നവർ",
+  "getting_started.directory": "രൂപരേഖ നാമഗൃഹസൂചി",
+  "getting_started.documentation": "രേഖാ സമാഹരണം",
+  "getting_started.heading": "തുടക്കം കുറിക്കുക",
+  "getting_started.invite": "ആളുകളെ ക്ഷണിക്കുക",
+  "getting_started.open_source_notice": "മാസ്റ്റഡോൺ ഒരു സ്വതന്ത്ര സോഫ്ട്‍വെയർ ആണ്. നിങ്ങൾക്ക് {github} GitHub ൽ സംഭാവന ചെയ്യുകയോ പ്രശ്നങ്ങൾ അറിയിക്കുകയോ ചെയ്യാം.",
+  "getting_started.security": "അംഗത്വ ക്രമീകരണങ്ങൾ",
+  "getting_started.terms": "സേവന വ്യവസ്ഥകൾ",
+  "hashtag.column_header.tag_mode.all": "{additional} ഉം കൂടെ",
+  "hashtag.column_header.tag_mode.any": "അല്ലെങ്കിൽ {additional}",
+  "hashtag.column_header.tag_mode.none": "{additional} ഇല്ലാതെ",
+  "hashtag.column_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": "ഈ എഴുത്തുപംക്തിക്ക് കൂടുതൽ ഉപനാമങ്ങൾ ചേർക്കുക",
+  "home.column_settings.basic": "അടിസ്ഥാനം",
+  "home.column_settings.show_reblogs": "ബൂസ്റ്റുകൾ കാണിക്കുക",
+  "home.column_settings.show_replies": "മറുപടികൾ കാണിക്കുക",
   "intervals.full.days": "{number, plural, one {# day} other {# days}}",
   "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
   "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}",
-  "introduction.federation.action": "Next",
-  "introduction.federation.federated.headline": "Federated",
+  "introduction.federation.action": "അടുത്തത്",
+  "introduction.federation.federated.headline": "സംയുക്തമാക്കിയ",
   "introduction.federation.federated.text": "Public posts from other servers of the fediverse will appear in the federated timeline.",
-  "introduction.federation.home.headline": "Home",
+  "introduction.federation.home.headline": "ഭവനം",
   "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!",
-  "introduction.federation.local.headline": "Local",
+  "introduction.federation.local.headline": "പ്രാദേശികം",
   "introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
-  "introduction.interactions.action": "Finish toot-orial!",
-  "introduction.interactions.favourite.headline": "Favourite",
+  "introduction.interactions.action": "പഠനസഹായി പൂർത്തീകരിക്കുക!",
+  "introduction.interactions.favourite.headline": "പ്രിയപ്പെട്ടത്",
   "introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
-  "introduction.interactions.reblog.headline": "Boost",
+  "introduction.interactions.reblog.headline": "ബൂസ്റ്റ് ചെയ്യുക",
   "introduction.interactions.reblog.text": "You can share other people's toots with your followers by boosting them.",
-  "introduction.interactions.reply.headline": "Reply",
+  "introduction.interactions.reply.headline": "മറുപടി",
   "introduction.interactions.reply.text": "You can reply to other people's and your own toots, which will chain them together in a conversation.",
-  "introduction.welcome.action": "Let's go!",
-  "introduction.welcome.headline": "First steps",
+  "introduction.welcome.action": "നമുക്ക് തുടങ്ങാം!",
+  "introduction.welcome.headline": "ആദ്യ ചുവടുവെപ്പുകൾ",
   "introduction.welcome.text": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.",
-  "keyboard_shortcuts.back": "to navigate back",
+  "keyboard_shortcuts.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.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.heading": "കീബോർഡ് എളുപ്പവഴികൾ",
   "keyboard_shortcuts.home": "to open home timeline",
   "keyboard_shortcuts.hotkey": "Hotkey",
   "keyboard_shortcuts.legend": "to display this legend",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "to open muted users list",
   "keyboard_shortcuts.my_profile": "to open your profile",
   "keyboard_shortcuts.notifications": "to open notifications column",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "to open pinned toots list",
   "keyboard_shortcuts.profile": "to open author's profile",
   "keyboard_shortcuts.reply": "to reply",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blocked users",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Security",
   "notification.favourite": "{name} favourited your status",
   "notification.follow": "{name} followed you",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} mentioned you",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "A poll you have voted in has ended",
   "notification.reblog": "{name} boosted your status",
   "notifications.clear": "Clear notifications",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Quick filter bar",
   "notifications.column_settings.filter_bar.show": "Show",
   "notifications.column_settings.follow": "New followers:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "Mentions:",
   "notifications.column_settings.poll": "Poll results:",
   "notifications.column_settings.push": "Push notifications",
@@ -344,6 +355,7 @@
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_status": "Open this status in the moderation interface",
   "status.block": "Block @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.copy": "Copy link to status",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} boosted",
   "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
   "status.redraft": "Delete & re-draft",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "Reply",
   "status.replyAll": "Reply to thread",
   "status.report": "Report @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Add media ({formats})",
   "upload_error.limit": "File upload limit exceeded.",
   "upload_error.poll": "File upload not allowed with polls.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "Describe for the visually impaired",
   "upload_form.edit": "Edit",
   "upload_form.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.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "Uploading…",
   "video.close": "Close video",
+  "video.download": "Download file",
   "video.exit_fullscreen": "Exit full screen",
   "video.expand": "Expand video",
   "video.fullscreen": "Full screen",
diff --git a/app/javascript/mastodon/locales/mr.json b/app/javascript/mastodon/locales/mr.json
index 8af8e3493..f265042f2 100644
--- a/app/javascript/mastodon/locales/mr.json
+++ b/app/javascript/mastodon/locales/mr.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "यादीत घाला किंवा यादीतून काढून टाका",
   "account.badges.bot": "स्वयंचलित खाते",
+  "account.badges.group": "Group",
   "account.block": "@{name} यांना ब्लॉक करा",
   "account.block_domain": "{domain} पासून सर्व लपवा",
   "account.blocked": "ब्लॉक केले आहे",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Rate limited",
   "alert.unexpected.message": "An unexpected error occurred.",
   "alert.unexpected.title": "अरेरे!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} प्रतिसप्ताह",
   "boost_modal.combo": "You can press {combo} to skip this next time",
   "bundle_column_error.body": "हा घटक लोड करतांना काहीतरी चुकले आहे.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "हा घटक लोड करतांना काहीतरी चुकले आहे.",
   "bundle_modal_error.retry": "पुन्हा प्रयत्न करा",
   "column.blocks": "ब्लॉक केलेले खातेधारक",
+  "column.bookmarks": "Bookmarks",
   "column.community": "Local timeline",
   "column.direct": "थेट संदेश",
   "column.directory": "Browse profiles",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Poll duration",
   "compose_form.poll.option_placeholder": "निवड {number}",
   "compose_form.poll.remove_option": "हा पर्याय काढा",
+  "compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices",
+  "compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
   "compose_form.publish": "Toot",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Mark media as sensitive",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
   "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no hidden domains yet.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "to open muted users list",
   "keyboard_shortcuts.my_profile": "to open your profile",
   "keyboard_shortcuts.notifications": "to open notifications column",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "to open pinned toots list",
   "keyboard_shortcuts.profile": "to open author's profile",
   "keyboard_shortcuts.reply": "to reply",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blocked users",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Security",
   "notification.favourite": "{name} favourited your status",
   "notification.follow": "{name} followed you",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} mentioned you",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "A poll you have voted in has ended",
   "notification.reblog": "{name} boosted your status",
   "notifications.clear": "Clear notifications",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Quick filter bar",
   "notifications.column_settings.filter_bar.show": "Show",
   "notifications.column_settings.follow": "New followers:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "Mentions:",
   "notifications.column_settings.poll": "Poll results:",
   "notifications.column_settings.push": "Push notifications",
@@ -344,6 +355,7 @@
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_status": "Open this status in the moderation interface",
   "status.block": "Block @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.copy": "Copy link to status",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} boosted",
   "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
   "status.redraft": "Delete & re-draft",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "Reply",
   "status.replyAll": "Reply to thread",
   "status.report": "Report @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Add media ({formats})",
   "upload_error.limit": "File upload limit exceeded.",
   "upload_error.poll": "File upload not allowed with polls.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "Describe for the visually impaired",
   "upload_form.edit": "Edit",
   "upload_form.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.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "Uploading…",
   "video.close": "Close video",
+  "video.download": "Download file",
   "video.exit_fullscreen": "Exit full screen",
   "video.expand": "Expand video",
   "video.fullscreen": "Full screen",
diff --git a/app/javascript/mastodon/locales/ms.json b/app/javascript/mastodon/locales/ms.json
index 6881d3be3..3bd6e145e 100644
--- a/app/javascript/mastodon/locales/ms.json
+++ b/app/javascript/mastodon/locales/ms.json
@@ -1,6 +1,7 @@
 {
   "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": "Hide everything from {domain}",
   "account.blocked": "Blocked",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Rate limited",
   "alert.unexpected.message": "An unexpected error occurred.",
   "alert.unexpected.title": "Oops!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} per week",
   "boost_modal.combo": "You can press {combo} to skip this next time",
   "bundle_column_error.body": "Something went wrong while loading this component.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Something went wrong while loading this component.",
   "bundle_modal_error.retry": "Try again",
   "column.blocks": "Blocked users",
+  "column.bookmarks": "Bookmarks",
   "column.community": "Local timeline",
   "column.direct": "Direct messages",
   "column.directory": "Browse profiles",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Poll duration",
   "compose_form.poll.option_placeholder": "Choice {number}",
   "compose_form.poll.remove_option": "Remove this choice",
+  "compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices",
+  "compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
   "compose_form.publish": "Toot",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Mark media as sensitive",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
   "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no hidden domains yet.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "to open muted users list",
   "keyboard_shortcuts.my_profile": "to open your profile",
   "keyboard_shortcuts.notifications": "to open notifications column",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "to open pinned toots list",
   "keyboard_shortcuts.profile": "to open author's profile",
   "keyboard_shortcuts.reply": "to reply",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blocked users",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Security",
   "notification.favourite": "{name} favourited your status",
   "notification.follow": "{name} followed you",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} mentioned you",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "A poll you have voted in has ended",
   "notification.reblog": "{name} boosted your status",
   "notifications.clear": "Clear notifications",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Quick filter bar",
   "notifications.column_settings.filter_bar.show": "Show",
   "notifications.column_settings.follow": "New followers:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "Mentions:",
   "notifications.column_settings.poll": "Poll results:",
   "notifications.column_settings.push": "Push notifications",
@@ -344,6 +355,7 @@
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_status": "Open this status in the moderation interface",
   "status.block": "Block @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.copy": "Copy link to status",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} boosted",
   "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
   "status.redraft": "Delete & re-draft",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "Reply",
   "status.replyAll": "Reply to thread",
   "status.report": "Report @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Add media ({formats})",
   "upload_error.limit": "File upload limit exceeded.",
   "upload_error.poll": "File upload not allowed with polls.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "Describe for the visually impaired",
   "upload_form.edit": "Edit",
   "upload_form.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.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "Uploading…",
   "video.close": "Close video",
+  "video.download": "Download file",
   "video.exit_fullscreen": "Exit full screen",
   "video.expand": "Expand video",
   "video.fullscreen": "Full screen",
diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json
index 74aaad970..d570f3612 100644
--- a/app/javascript/mastodon/locales/nl.json
+++ b/app/javascript/mastodon/locales/nl.json
@@ -1,11 +1,12 @@
 {
   "account.add_or_remove_from_list": "Toevoegen of verwijderen vanuit lijsten",
   "account.badges.bot": "Bot",
+  "account.badges.group": "Group",
   "account.block": "Blokkeer @{name}",
   "account.block_domain": "Verberg alles van {domain}",
   "account.blocked": "Geblokkeerd",
   "account.cancel_follow_request": "Volgverzoek annuleren",
-  "account.direct": "Direct Message @{name}",
+  "account.direct": "Direct bericht @{name}",
   "account.domain_blocked": "Domein verborgen",
   "account.edit_profile": "Profiel bewerken",
   "account.endorse": "Op profiel weergeven",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Beperkt te gebruiken",
   "alert.unexpected.message": "Er deed zich een onverwachte fout voor",
   "alert.unexpected.title": "Oeps!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} per week",
   "boost_modal.combo": "Je kunt {combo} klikken om dit de volgende keer over te slaan",
   "bundle_column_error.body": "Tijdens het laden van dit onderdeel is er iets fout gegaan.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Tijdens het laden van dit onderdeel is er iets fout gegaan.",
   "bundle_modal_error.retry": "Opnieuw proberen",
   "column.blocks": "Geblokkeerde gebruikers",
+  "column.bookmarks": "Bladwijzers",
   "column.community": "Lokale tijdlijn",
   "column.direct": "Directe berichten",
   "column.directory": "Gebruikersgids",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Duur van de poll",
   "compose_form.poll.option_placeholder": "Keuze {number}",
   "compose_form.poll.remove_option": "Deze keuze verwijderen",
+  "compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices",
+  "compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
   "compose_form.publish": "Toot",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Media als gevoelig markeren",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Hier zijn geen toots!",
   "empty_column.account_unavailable": "Profiel is niet beschikbaar",
   "empty_column.blocks": "Jij hebt nog geen enkele gebruiker geblokkeerd.",
+  "empty_column.bookmarked_statuses": "Jij hebt nog geen toots 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. Toot iets in het openbaar om de bal aan het rollen te krijgen!",
   "empty_column.direct": "Je hebt nog geen directe berichten. Wanneer je er een verzend of ontvangt, zijn deze hier te zien.",
   "empty_column.domain_blocks": "Er zijn nog geen genegeerde servers.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "om de door jou genegeerde gebruikers te tonen",
   "keyboard_shortcuts.my_profile": "om jouw profiel te tonen",
   "keyboard_shortcuts.notifications": "om jouw meldingen te tonen",
+  "keyboard_shortcuts.open_media": "om media te openen",
   "keyboard_shortcuts.pinned": "om jouw vastgezette toots te tonen",
   "keyboard_shortcuts.profile": "om het gebruikersprofiel te openen",
   "keyboard_shortcuts.reply": "om te reageren",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Verberg meldingen van deze persoon?",
   "navigation_bar.apps": "Mobiele apps",
   "navigation_bar.blocks": "Geblokkeerde gebruikers",
+  "navigation_bar.bookmarks": "Bladwijzers",
   "navigation_bar.community_timeline": "Lokale tijdlijn",
   "navigation_bar.compose": "Nieuw toot schrijven",
   "navigation_bar.direct": "Directe berichten",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Beveiliging",
   "notification.favourite": "{name} voegde jouw toot als favoriet toe",
   "notification.follow": "{name} volgt jou nu",
+  "notification.follow_request": "{name} wil jou graag volgen",
   "notification.mention": "{name} vermeldde jou",
+  "notification.own_poll": "Jouw poll is beëindigd",
   "notification.poll": "Een poll waaraan jij hebt meegedaan is beëindigd",
   "notification.reblog": "{name} boostte jouw toot",
   "notifications.clear": "Meldingen verwijderen",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Snelle filterbalk",
   "notifications.column_settings.filter_bar.show": "Tonen",
   "notifications.column_settings.follow": "Nieuwe volgers:",
+  "notifications.column_settings.follow_request": "Nieuw volgverzoek:",
   "notifications.column_settings.mention": "Vermeldingen:",
   "notifications.column_settings.poll": "Pollresultaten:",
   "notifications.column_settings.push": "Pushmeldingen",
@@ -344,12 +355,13 @@
   "status.admin_account": "Moderatie-omgeving van @{name} openen",
   "status.admin_status": "Deze toot in de moderatie-omgeving openen",
   "status.block": "Blokkeer @{name}",
+  "status.bookmark": "Bladwijzer toevoegen",
   "status.cancel_reblog_private": "Niet langer boosten",
   "status.cannot_reblog": "Deze toot kan niet geboost worden",
   "status.copy": "Link naar toot kopiëren",
   "status.delete": "Verwijderen",
   "status.detailed_status": "Uitgebreide gespreksweergave",
-  "status.direct": "Directe toot @{name}",
+  "status.direct": "Direct bericht @{name}",
   "status.embed": "Insluiten",
   "status.favourite": "Favoriet",
   "status.filtered": "Gefilterd",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} boostte",
   "status.reblogs.empty": "Niemand heeft deze toot nog geboost. Wanneer iemand dit doet, valt dat hier te zien.",
   "status.redraft": "Verwijderen en herschrijven",
+  "status.remove_bookmark": "Bladwijzer verwijderen",
   "status.reply": "Reageren",
   "status.replyAll": "Reageer op iedereen",
   "status.report": "Rapporteer @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Media toevoegen ({formats})",
   "upload_error.limit": "Uploadlimiet van bestand overschreden.",
   "upload_error.poll": "Het uploaden van bestanden is in polls niet toegestaan.",
+  "upload_form.audio_description": "Omschrijf dit voor mensen met een auditieve beperking",
   "upload_form.description": "Omschrijf dit voor mensen met een visuele beperking",
   "upload_form.edit": "Bewerken",
   "upload_form.undo": "Verwijderen",
+  "upload_form.video_description": "Omschrijf dit voor mensen met een auditieve of visuele beperking",
   "upload_modal.analyzing_picture": "Afbeelding analyseren…",
   "upload_modal.apply": "Toepassen",
   "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Voorvertoning ({ratio})",
   "upload_progress.label": "Uploaden...",
   "video.close": "Video sluiten",
+  "video.download": "Bestand downloaden",
   "video.exit_fullscreen": "Volledig scherm sluiten",
   "video.expand": "Video groter maken",
   "video.fullscreen": "Volledig scherm",
diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json
index 039381cf0..c04b30e04 100644
--- a/app/javascript/mastodon/locales/nn.json
+++ b/app/javascript/mastodon/locales/nn.json
@@ -1,376 +1,389 @@
 {
-  "account.add_or_remove_from_list": "Legg til eller ta vekk fra liste",
+  "account.add_or_remove_from_list": "Legg til eller tak vekk frå listene",
   "account.badges.bot": "Robot",
-  "account.block": "Blokkér @{name}",
-  "account.block_domain": "Gøyme alt innhald for domenet {domain}",
+  "account.badges.group": "Gruppe",
+  "account.block": "Blokker @{name}",
+  "account.block_domain": "Skjul alt frå {domain}",
   "account.blocked": "Blokkert",
-  "account.cancel_follow_request": "Avslutt føljar-førespurnad",
-  "account.direct": "Direkte meld @{name}",
+  "account.cancel_follow_request": "Fjern fylgjeførespurnad",
+  "account.direct": "Send melding til @{name}",
   "account.domain_blocked": "Domenet er gøymt",
   "account.edit_profile": "Rediger profil",
-  "account.endorse": "Framhev på profilen din",
-  "account.follow": "Følj",
-  "account.followers": "Føljare",
-  "account.followers.empty": "Er ikkje nokon som føljar denne brukaren ennå.",
-  "account.follows": "Føljingar",
-  "account.follows.empty": "Denne brukaren foljer ikkje nokon ennå.",
-  "account.follows_you": "Føljar deg",
-  "account.hide_reblogs": "Gøym fremhevingar for @{name}",
+  "account.endorse": "Framhev på profil",
+  "account.follow": "Fylg",
+  "account.followers": "Fylgjarar",
+  "account.followers.empty": "Ingen fylgjer denne brukaren enno.",
+  "account.follows": "Fylgjer",
+  "account.follows.empty": "Denne brukaren fylgjer ikkje nokon enno.",
+  "account.follows_you": "Fylgjer deg",
+  "account.hide_reblogs": "Gøym fremhevingar frå @{name}",
   "account.last_status": "Sist aktiv",
-  "account.link_verified_on": "Eigerskap for denne linken er sist sjekket den {date}",
-  "account.locked_info": "Brukarens privat-status er satt til lukka. Eigaren må manuelt døme kvem som kan følje honom.",
+  "account.link_verified_on": "Eigarskap for denne lenkja vart sist sjekka {date}",
+  "account.locked_info": "Denne kontoen er privat. Eigaren kan sjølv velja kven som kan fylgja han.",
   "account.media": "Media",
-  "account.mention": "Nemne @{name}",
+  "account.mention": "Nemn @{name}",
   "account.moved_to": "{name} har flytta til:",
   "account.mute": "Målbind @{name}",
-  "account.mute_notifications": "Målbind varslingar ifrå @{name}",
-  "account.muted": "Målbindt",
+  "account.mute_notifications": "Målbind varsel frå @{name}",
+  "account.muted": "Målbunden",
   "account.never_active": "Aldri",
-  "account.posts": "Tutar",
-  "account.posts_with_replies": "Tutar og svar",
+  "account.posts": "Tut",
+  "account.posts_with_replies": "Tut og svar",
   "account.report": "Rapporter @{name}",
-  "account.requested": "Venter på samtykke. Klikk for å avbryte føljar-førespurnad",
+  "account.requested": "Ventar på samtykke. Klikk for å avbryta fylgjeførespurnaden",
   "account.share": "Del @{name} sin profil",
-  "account.show_reblogs": "Sjå framhevingar ifrå @{name}",
-  "account.unblock": "Avblokker @{name}",
+  "account.show_reblogs": "Vis framhevingar frå @{name}",
+  "account.unblock": "Slutt å blokera @{name}",
   "account.unblock_domain": "Vis {domain}",
   "account.unendorse": "Ikkje framhev på profil",
-  "account.unfollow": "Avfølja",
+  "account.unfollow": "Slutt å fylgja",
   "account.unmute": "Av-demp @{name}",
-  "account.unmute_notifications": "Av-demp notifikasjoner ifrå @{name}",
-  "alert.rate_limited.message": "Ver vennlig og prøv igjen {retry_time, time, medium}.",
-  "alert.rate_limited.title": "Bregrensa rate",
-  "alert.unexpected.message": "Eit uforventa problem har hendt.",
-  "alert.unexpected.title": "Oops!",
+  "account.unmute_notifications": "Vis varsel frå @{name}",
+  "alert.rate_limited.message": "Ver venleg å prøva igjen etter {retry_time, time, medium}.",
+  "alert.rate_limited.title": "Begrensa rate",
+  "alert.unexpected.message": "Eit uventa problem oppstod.",
+  "alert.unexpected.title": "Oi sann!",
+  "announcement.announcement": "Kunngjøring",
   "autosuggest_hashtag.per_week": "{count} per veke",
-  "boost_modal.combo": "Du kan trykke {combo} for å hoppe over dette neste gong",
-  "bundle_column_error.body": "Noko gikk gale mens komponent ble nedlasta.",
+  "boost_modal.combo": "Du kan trykkja {combo} for å hoppa over dette neste gong",
+  "bundle_column_error.body": "Noko gjekk gale mens denne komponenten vart lasta ned.",
   "bundle_column_error.retry": "Prøv igjen",
-  "bundle_column_error.title": "Tenarmaskin feil",
+  "bundle_column_error.title": "Nettverksfeil",
   "bundle_modal_error.close": "Lukk",
-  "bundle_modal_error.message": "Noko gikk gale mens komponent var i ferd med å bli nedlasta.",
+  "bundle_modal_error.message": "Noko gjekk gale under lastinga av denne komponenten.",
   "bundle_modal_error.retry": "Prøv igjen",
-  "column.blocks": "Blokka brukare",
-  "column.community": "Lokal samtid",
-  "column.direct": "Direkte meldingar",
-  "column.directory": "Sjå gjennom profiler",
-  "column.domain_blocks": "Gøymte domener",
+  "column.blocks": "Blokkerte brukarar",
+  "column.bookmarks": "Bokmerke",
+  "column.community": "Lokal tidsline",
+  "column.direct": "Meldingar",
+  "column.directory": "Sjå gjennom profilar",
+  "column.domain_blocks": "Gøymde domene",
   "column.favourites": "Favorittar",
-  "column.follow_requests": "Føljarførespurnad",
+  "column.follow_requests": "Fylgjeførespurnadar",
   "column.home": "Heim",
   "column.lists": "Lister",
-  "column.mutes": "Målbindte brukare",
-  "column.notifications": "Varslingar",
-  "column.pins": "Festa tuter",
-  "column.public": "Federert samtid",
+  "column.mutes": "Målbundne brukarar",
+  "column.notifications": "Varsel",
+  "column.pins": "Festa tut",
+  "column.public": "Samla tidsline",
   "column_back_button.label": "Tilbake",
-  "column_header.hide_settings": "Skjul innstillingar",
-  "column_header.moveLeft_settings": "Flytt feltet til venstre",
-  "column_header.moveRight_settings": "Flytt feltet til høgre",
+  "column_header.hide_settings": "Gøym innstillingar",
+  "column_header.moveLeft_settings": "Flytt kolonne til venstre",
+  "column_header.moveRight_settings": "Flytt kolonne til høgre",
   "column_header.pin": "Fest",
   "column_header.show_settings": "Vis innstillingar",
   "column_header.unpin": "Løys",
   "column_subheading.settings": "Innstillingar",
-  "community.column_settings.media_only": "Kun medie",
-  "compose_form.direct_message_warning": "Denne tuten vil kun verte synleg for nemnde brukarar.",
+  "community.column_settings.media_only": "Berre media",
+  "compose_form.direct_message_warning": "Dette tutet vert berre synleg for nemnde brukarar.",
   "compose_form.direct_message_warning_learn_more": "Lær meir",
-  "compose_form.hashtag_warning": "Denne tuten vill ikkje bli lista under nokon knagg ettersom den ikkje er opplista. Berre offentlege tutar kan ble søkt på ved emneknagg.",
-  "compose_form.lock_disclaimer": "Din brukar er ikkje {locked}. Alle kan følje deg for å sjå føljar-modus poster.",
+  "compose_form.hashtag_warning": "Dette tutet vert ikkje oppført under nokon emneknagg sidan det ikkje er oppført. Berre offentlege tut kan verta søkt etter med emneknagg.",
+  "compose_form.lock_disclaimer": "Kontoen din er ikkje {locked}. Kven som helst kan fylgja deg for å sjå innlegga dine som berre visast til fylgjarar.",
   "compose_form.lock_disclaimer.lock": "låst",
-  "compose_form.placeholder": "Kva har du på hjartet?",
-  "compose_form.poll.add_option": "Legg til eit punkt",
-  "compose_form.poll.duration": "Varigheit for spørring",
+  "compose_form.placeholder": "Kva har du på hjarta?",
+  "compose_form.poll.add_option": "Legg til eit val",
+  "compose_form.poll.duration": "Varigskap for røysting",
   "compose_form.poll.option_placeholder": "Val {number}",
-  "compose_form.poll.remove_option": "Ta burt dette valet",
+  "compose_form.poll.remove_option": "Ta vekk dette valet",
+  "compose_form.poll.switch_to_multiple": "Endre avstemning til å tillate flere valg",
+  "compose_form.poll.switch_to_single": "Endre avstemning til å tillate ett valg",
   "compose_form.publish": "Tut",
   "compose_form.publish_loud": "{publish}!",
-  "compose_form.sensitive.hide": "Merk media som sensitivt",
-  "compose_form.sensitive.marked": "Media er markert som sensitivt",
-  "compose_form.sensitive.unmarked": "Media er ikkje merka som sensitivt",
-  "compose_form.spoiler.marked": "Tekst er gøymt bak ein advarsel",
-  "compose_form.spoiler.unmarked": "Tekst er ikkje gøymt",
-  "compose_form.spoiler_placeholder": "Skriv varselen din her",
-  "confirmation_modal.cancel": "Avbrot",
-  "confirmations.block.block_and_report": "Blokk & rapportér",
-  "confirmations.block.confirm": "Blokkér",
-  "confirmations.block.message": "Er du sikker på at du vill blokke {name}?",
+  "compose_form.sensitive.hide": "Merk medium som sensitivt",
+  "compose_form.sensitive.marked": "Medium er markert som sensitivt",
+  "compose_form.sensitive.unmarked": "Medium er ikkje merka som sensitivt",
+  "compose_form.spoiler.marked": "Tekst er gøymd bak åtvaring",
+  "compose_form.spoiler.unmarked": "Tekst er ikkje gøymd",
+  "compose_form.spoiler_placeholder": "Skriv åtvaringa di her",
+  "confirmation_modal.cancel": "Avbryt",
+  "confirmations.block.block_and_report": "Blokker & rapporter",
+  "confirmations.block.confirm": "Blokker",
+  "confirmations.block.message": "Er du sikker på at du vil blokkera {name}?",
   "confirmations.delete.confirm": "Slett",
-  "confirmations.delete.message": "Er du sikker på at du vill slette denne statusen?",
+  "confirmations.delete.message": "Er du sikker på at du vil sletta denne statusen?",
   "confirmations.delete_list.confirm": "Slett",
-  "confirmations.delete_list.message": "Er du sikker på at du vill slette denne listen for alltid?",
+  "confirmations.delete_list.message": "Er du sikker på at du vil sletta denne lista for alltid?",
   "confirmations.domain_block.confirm": "Gøym heile domenet",
-  "confirmations.domain_block.message": "Er du ordentleg, ordentleg sikker på at du vill blokkere heile {domain}? I dei tilfeller er det bedre med ein målretta blokkering eller demping av individuelle brukare.",
+  "confirmations.domain_block.message": "Er du heilt, heilt sikker på at du vil blokkera 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å det domenet i nokon fødererte tidsliner eller i varsla dine. Fylgjarane dine frå det domenet vert fjerna.",
   "confirmations.logout.confirm": "Logg ut",
-  "confirmations.logout.message": "Er du sikker på at du vill logge ut?",
+  "confirmations.logout.message": "Er du sikker på at du vil logga ut?",
   "confirmations.mute.confirm": "Målbind",
-  "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": "Er du sikker på at d vill målbinde {name}?",
-  "confirmations.redraft.confirm": "Slett & gjennopprett",
-  "confirmations.redraft.message": "Er du sikker på at du vill slette statusen og gjennoprette den? Favoritter og framhevinger vill bli borte, og svar til den originale posten vill bli einstøing.",
+  "confirmations.mute.explanation": "Dette gøymer innlegg frå dei og innlegg som nemner dei, men tillèt dei framleis å sjå dine innlegg og fylgja deg.",
+  "confirmations.mute.message": "Er du sikker på at du vil målbinda {name}?",
+  "confirmations.redraft.confirm": "Slett & skriv på nytt",
+  "confirmations.redraft.message": "Er du sikker på at du vil sletta denne statusen og skriva han på nytt? Då misser du favorittar og framhevingar, og svar til det opphavlege innlegget vert einstøingar.",
   "confirmations.reply.confirm": "Svar",
-  "confirmations.reply.message": "Å svare nå vill overskrive meldingen du er i ferd med å skrive. Er du sikker på at du vill gå fram?",
-  "confirmations.unfollow.confirm": "Avfølj",
-  "confirmations.unfollow.message": "Er du sikker på at du vill avfølje {name}?",
+  "confirmations.reply.message": "Å svara no vil overskriva meldinga du skriv no. Er du sikker på at du vil halda fram?",
+  "confirmations.unfollow.confirm": "Slutt å fylgja",
+  "confirmations.unfollow.message": "Er du sikker på at du vil slutta å fylgja {name}?",
   "conversation.delete": "Slett samtale",
-  "conversation.mark_as_read": "Merk som lest",
+  "conversation.mark_as_read": "Merk som lese",
   "conversation.open": "Sjå samtale",
   "conversation.with": "Med {names}",
-  "directory.federated": "Fra kjent fedivers",
-  "directory.local": "Fra berre {domain} domenet",
+  "directory.federated": "Frå kjent fedivers",
+  "directory.local": "Berre frå {domain}",
   "directory.new_arrivals": "Nyankommne",
-  "directory.recently_active": "Nylig aktiv",
-  "embed.instructions": "Embed this status on your website by copying the code below.",
-  "embed.preview": "Dette er korleis den vil sjå ut:",
+  "directory.recently_active": "Nyleg aktive",
+  "embed.instructions": "Bygg inn denne statusen på nettsida di ved å kopiera koden under.",
+  "embed.preview": "Slik bid det å sjå ut:",
   "emoji_button.activity": "Aktivitet",
-  "emoji_button.custom": "Custom",
+  "emoji_button.custom": "Eige",
   "emoji_button.flags": "Flagg",
   "emoji_button.food": "Mat & drikke",
-  "emoji_button.label": "Legg til smilefjes",
+  "emoji_button.label": "Legg til emoji",
   "emoji_button.nature": "Natur",
   "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
-  "emoji_button.objects": "Objektar",
+  "emoji_button.objects": "Objekt",
   "emoji_button.people": "Folk",
   "emoji_button.recent": "Ofte brukt",
   "emoji_button.search": "Søk...",
-  "emoji_button.search_results": "Søke resultater",
-  "emoji_button.symbols": "Symbolar",
-  "emoji_button.travel": "Reise & steder",
-  "empty_column.account_timeline": "Ikkje nokon tutar her!",
+  "emoji_button.search_results": "Søkeresultat",
+  "emoji_button.symbols": "Symbol",
+  "emoji_button.travel": "Reise & stader",
+  "empty_column.account_timeline": "Ingen tut her!",
   "empty_column.account_unavailable": "Profil ikkje tilgjengelig",
-  "empty_column.blocks": "Du har ikkje blokkért nokon brukarar ennå.",
+  "empty_column.blocks": "Du har ikkje blokkert nokon brukarar enno.",
+  "empty_column.bookmarked_statuses": "Du har ikkje nokon bokmerkte tut enno. Når du bokmerkjer eit, dukkar det opp her.",
   "empty_column.community": "Den lokale samtiden er tom. Skriv noko offentleg å få ballen til å rulle!",
-  "empty_column.direct": "Du har ikkje nokon direkte meldingar ennå. Når du sendar eller får ein, så vill den ende opp her.",
-  "empty_column.domain_blocks": "Der er ikkje nokon gøymte domener enno.",
-  "empty_column.favourited_statuses": "Du har ikkje favorisert nokon tutar enno. Når du favoriserer noko, så vill det ende opp her.",
-  "empty_column.favourites": "Ikkje nokon har favorisert denne tuten enno. Når nokon gjer det, så vill den ende opp her.",
-  "empty_column.follow_requests": "Du har ikkje nokon føljar førespurnad enno. Når du får ein, så vill den sjåast her.",
-  "empty_column.hashtag": "Det er ikkje noko i denne emneknaggen her enno.",
-  "empty_column.home": "Din heime-tidslinja er tom! Dra til {public} eller søk for å starte å møte andre brukare.",
-  "empty_column.home.public_timeline": "Den offentlege tidslinja",
-  "empty_column.list": "Det er ikkje noko i denne lista enno. Når medlemmar av denne lista poster statuser, så vill dei sjåast her.",
-  "empty_column.lists": "Du har ikkje nokon liste enno. Når du lagar ein, så vill den ende up her.",
-  "empty_column.mutes": "Du har ikkje dempet nokon brukare enno.",
-  "empty_column.notifications": "Der er ikkje nokon varsler her enno. Kommuniser med andre for å starte samtalen.",
-  "empty_column.public": "Det er ikkje noko her! Skriv noko offentleg, eller manuelt følje brukare fra andre tenarmaskiner for å fylle det opp",
-  "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.next_steps": "Prøv å laste på nytt. Om det ikkje hjelper så kan du fortsatt bruke mastodon gjennom ein anna nettlesar eller ein applikasjon.",
-  "errors.unexpected_crash.copy_stacktrace": "Kopiér stacktrace til klippebord",
-  "errors.unexpected_crash.report_issue": "Rapportér problem",
+  "empty_column.direct": "Du har ingen direktemeldingar ennå. Når du sender eller får ein vil den dukke opp her.",
+  "empty_column.domain_blocks": "Det er ingen gøymde domene ennå.",
+  "empty_column.favourited_statuses": "Du har ingen favoritt-tut ennå. Når du merkjer ein 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_requests": "Du har ingen følgjeførespurnadar ennå. Når du får ein, så vil den dukke opp her.",
+  "empty_column.hashtag": "Det er ingenting i denne emneknaggen ennå.",
+  "empty_column.home": "Heime-tidslinja di er tom! Besøk {public} eller søk for å starte og å møte andre brukarar.",
+  "empty_column.home.public_timeline": "den offentlege tidslina",
+  "empty_column.list": "Det er ingenting i denne lista enno. Når medlemer av denne lista legg ut nye statusar, så dukkar dei opp her.",
+  "empty_column.lists": "Du har ingen lister enno. Når du lagar ei, så dukkar ho opp her.",
+  "empty_column.mutes": "Du har ikkje målbunde nokon brukarar enno.",
+  "empty_column.notifications": "Du har ingen varsel ennå. Kommuniser med andre for å starte samtalen.",
+  "empty_column.public": "Det er ingenting her! Skriv noko offentleg, eller følg brukarar frå andre tenarar manuelt for å fylle det opp",
+  "error.unexpected_crash.explanation": "På grunn av ein feil i vår kode eller eit nettlesarkompatibilitetsproblem, kunne ikkje denne sida verte vist korrekt.",
+  "error.unexpected_crash.next_steps": "Prøv å lasta inn sida på nytt. Om det ikkje hjelper så kan du framleis nytta Mastodon i ein annan nettlesar eller app.",
+  "errors.unexpected_crash.copy_stacktrace": "Kopier stacktrace til utklippstavla",
+  "errors.unexpected_crash.report_issue": "Rapporter problem",
   "follow_request.authorize": "Autoriser",
-  "follow_request.reject": "Reject",
-  "getting_started.developers": "Utviklare",
-  "getting_started.directory": "Profile directory",
+  "follow_request.reject": "Avvis",
+  "getting_started.developers": "Utviklarar",
+  "getting_started.directory": "Profilkatalog",
   "getting_started.documentation": "Dokumentasjon",
-  "getting_started.heading": "Komme i gong",
-  "getting_started.invite": "Inviter folk",
-  "getting_started.open_source_notice": "Mastodon har åpen kilde kode. Du kan hjelpe til med problemar på GitHub gjennom {github}.",
-  "getting_started.security": "Brukar instillingar",
-  "getting_started.terms": "Brukarvillkår",
+  "getting_started.heading": "Kom i gang",
+  "getting_started.invite": "Byd folk inn",
+  "getting_started.open_source_notice": "Mastodon er fri programvare. Du kan bidraga eller rapportera problem med GitHub på {github}.",
+  "getting_started.security": "Kontoinnstillingar",
+  "getting_started.terms": "Brukarvilkår",
   "hashtag.column_header.tag_mode.all": "og {additional}",
   "hashtag.column_header.tag_mode.any": "eller {additional}",
-  "hashtag.column_header.tag_mode.none": "uten {additional}",
-  "hashtag.column_settings.select.no_options_message": "Ingen forslag funnet",
-  "hashtag.column_settings.select.placeholder": "Legg til emneknagger…",
+  "hashtag.column_header.tag_mode.none": "utan {additional}",
+  "hashtag.column_settings.select.no_options_message": "Fann ingen forslag",
+  "hashtag.column_settings.select.placeholder": "Legg til emneknaggar…",
   "hashtag.column_settings.tag_mode.all": "Alle disse",
-  "hashtag.column_settings.tag_mode.any": "Kvem som helst av disse",
+  "hashtag.column_settings.tag_mode.any": "Kva som helst av desse",
   "hashtag.column_settings.tag_mode.none": "Ikkje nokon av disse",
-  "hashtag.column_settings.tag_toggle": "Inkludér ekstra emneknagger til denne kolonnen",
-  "home.column_settings.basic": "Enkel",
-  "home.column_settings.show_reblogs": "Vis fremhevingar",
+  "hashtag.column_settings.tag_toggle": "Inkluder ekstra emneknaggar for denne kolonna",
+  "home.column_settings.basic": "Enkelt",
+  "home.column_settings.show_reblogs": "Vis framhevingar",
   "home.column_settings.show_replies": "Vis svar",
-  "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}}",
+  "intervals.full.days": "{number, plural, one {# dag} other {# dagar}}",
+  "intervals.full.hours": "{number, plural, one {# time} other {# timar}}",
+  "intervals.full.minutes": "{number, plural, one {# minutt} other {# minutt}}",
   "introduction.federation.action": "Neste",
-  "introduction.federation.federated.headline": "Federert",
-  "introduction.federation.federated.text": "Offentlege poster fra tenarmaskiner i fediverset kjem fram i den federerte tidslinja.",
+  "introduction.federation.federated.headline": "Føderert",
+  "introduction.federation.federated.text": "Offentlege innlegg frå andre tenarar i fødiverset dukkar opp i den fødererte tidslina.",
   "introduction.federation.home.headline": "Heim",
-  "introduction.federation.home.text": "Poster frå folk du kjenner kjem i heime tidslinja. Du kan følje kvem som helst ifrå alle tenarmaskiner!",
+  "introduction.federation.home.text": "Innlegg frå folk du fylgjer dukkar opp i heimetidslina di. Du kan fylgja kven som helst frå kva tenar som helst!",
   "introduction.federation.local.headline": "Lokal",
-  "introduction.federation.local.text": "Offentlege poster frå folk på samme tenarmaskin som deg vill du finne i den lokale tidslinja.",
+  "introduction.federation.local.text": "Offentlege innlegg frå folk på same tenar som deg, dukkar opp i den lokale tidslina.",
   "introduction.interactions.action": "Fullfør omvisning!",
-  "introduction.interactions.favourite.headline": "Lik",
-  "introduction.interactions.favourite.text": "Du kan lagre ein tut til seinere, og la forfatteren vite at ut likte den ved å trykke likar på tuten.",
-  "introduction.interactions.reblog.headline": "Fremhev",
-  "introduction.interactions.reblog.text": "Du kan dela folks tutar ved å \"booste\" døm.",
+  "introduction.interactions.favourite.headline": "Merk som favoritt",
+  "introduction.interactions.favourite.text": "Du kan lagra eit tut til seinare og lata forfattaren vita at du likte det med å merkja det som favoritt.",
+  "introduction.interactions.reblog.headline": "Framhev",
+  "introduction.interactions.reblog.text": "Du kan dela andre folk sine tut med å framheva dei.",
   "introduction.interactions.reply.headline": "Svar",
-  "introduction.interactions.reply.text": "Du kan svare på andre folk sine tuter, om så vill tutane lenkes i hop i ein samtale.",
-  "introduction.welcome.action": "Ta åt!",
-  "introduction.welcome.headline": "Første steg",
-  "introduction.welcome.text": "Velkommen til fediverset! Om ein kort tid vill det være mogleg å skrive til verden og venner gjennom ein stor variasjon av tenarmaskiner. Men denne tenarmaskinen, {domain}, er spesiell - den holder din profil, så ikkje gløym namnet dens.",
-  "keyboard_shortcuts.back": "for å navigere tilbake",
-  "keyboard_shortcuts.blocked": "for å åpne lista for blokka brukare",
-  "keyboard_shortcuts.boost": "for å dele",
-  "keyboard_shortcuts.column": "for å fokusere på ein status i ein av kollonene",
-  "keyboard_shortcuts.compose": "for å fokusera på status-skrivaren",
-  "keyboard_shortcuts.description": "Beskrivelse",
-  "keyboard_shortcuts.direct": "forå åpne direkte meldingar i ein kollone",
-  "keyboard_shortcuts.down": "for å bevege seg opp og ned",
-  "keyboard_shortcuts.enter": "for å åpne status",
-  "keyboard_shortcuts.favourite": "for å favorisere",
-  "keyboard_shortcuts.favourites": "for å åpne favoriserings lista",
-  "keyboard_shortcuts.federated": "for å åpne den fødererte tidslinja",
-  "keyboard_shortcuts.heading": "Tastatur hurtigtaster",
-  "keyboard_shortcuts.home": "for åpne heime tidslinja",
-  "keyboard_shortcuts.hotkey": "Hurtigtast",
-  "keyboard_shortcuts.legend": "to display this legend",
-  "keyboard_shortcuts.local": "for å åpne den lokale tidslinja",
-  "keyboard_shortcuts.mention": "for å nemne forfatteren",
-  "keyboard_shortcuts.muted": "for å åpne lista over dempa brukare",
-  "keyboard_shortcuts.my_profile": "for å åpne profilen din",
-  "keyboard_shortcuts.notifications": "for å åpne notifikasjons kolonnen",
-  "keyboard_shortcuts.pinned": "forå åpne lista for festa statuser",
-  "keyboard_shortcuts.profile": "for å åpne forfatteren's profil",
-  "keyboard_shortcuts.reply": "for å svare",
-  "keyboard_shortcuts.requests": "for å åpne lista for føljar førespurnader",
-  "keyboard_shortcuts.search": "for å fokus søkje",
-  "keyboard_shortcuts.start": "for å åpne \"kjem i gong\" kolonna",
-  "keyboard_shortcuts.toggle_hidden": "for å vise/gøyme tekst bak innholds varsel",
-  "keyboard_shortcuts.toggle_sensitivity": "for å vise/gøyme media",
-  "keyboard_shortcuts.toot": "Start ein heilt ny tut",
-  "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
-  "keyboard_shortcuts.up": "for å bevege seg oppover i lista",
-  "lightbox.close": "Lukk",
+  "introduction.interactions.reply.text": "Du kan svare på andre folk sine tutar, som gjer at dei vert lenka saman i ein samtale.",
+  "introduction.welcome.action": "Lat oss koma i gang!",
+  "introduction.welcome.headline": "Fyrste steg",
+  "introduction.welcome.text": "Velkommen til fediverset! Om nokre minutt vil du kunne kringkaste meldingar og snakke med dine vener rundt omkring på eit bredt spekter av tenarar. Men denne tenaren, {domain}, er spesiell – den er vert for profilen din, så hugs namnet på den.",
+  "keyboard_shortcuts.back": "for å gå tilbake",
+  "keyboard_shortcuts.blocked": "for å opna lista med blokkerte brukarar",
+  "keyboard_shortcuts.boost": "for å framheva",
+  "keyboard_shortcuts.column": "for å fokusera på ein status i ei av kolonnane",
+  "keyboard_shortcuts.compose": "for å fokusera tekstfeltet for skriving",
+  "keyboard_shortcuts.description": "Skildring",
+  "keyboard_shortcuts.direct": "for å opna direktemeldingskolonna",
+  "keyboard_shortcuts.down": "for å flytta seg opp og ned i lista",
+  "keyboard_shortcuts.enter": "for å opna status",
+  "keyboard_shortcuts.favourite": "for å merkja som favoritt",
+  "keyboard_shortcuts.favourites": "for å opna favorittlista",
+  "keyboard_shortcuts.federated": "for å opna den samla tidslina",
+  "keyboard_shortcuts.heading": "Snøggtastar",
+  "keyboard_shortcuts.home": "for opna heimetidslina",
+  "keyboard_shortcuts.hotkey": "Snøggtast",
+  "keyboard_shortcuts.legend": "for å visa denne forklåringa",
+  "keyboard_shortcuts.local": "for å opna den lokale tidslina",
+  "keyboard_shortcuts.mention": "for å nemna forfattaren",
+  "keyboard_shortcuts.muted": "for å opna lista over målbundne brukarar",
+  "keyboard_shortcuts.my_profile": "for å opna profilen din",
+  "keyboard_shortcuts.notifications": "for å opna varselskolonna",
+  "keyboard_shortcuts.open_media": "for å opna media",
+  "keyboard_shortcuts.pinned": "for å opna lista over festa tut",
+  "keyboard_shortcuts.profile": "for å opna forfattaren sin profil",
+  "keyboard_shortcuts.reply": "for å svara",
+  "keyboard_shortcuts.requests": "for å opna lista med fylgjeførespurnader",
+  "keyboard_shortcuts.search": "for å fokusera søket",
+  "keyboard_shortcuts.start": "for å opna \"kom i gang\"-feltet",
+  "keyboard_shortcuts.toggle_hidden": "for å visa/gøyma tekst bak innhaldsvarsel",
+  "keyboard_shortcuts.toggle_sensitivity": "for å visa/gøyma media",
+  "keyboard_shortcuts.toot": "for å laga ein heilt ny tut",
+  "keyboard_shortcuts.unfocus": "å ufokusere komponerings-/søkefeltet",
+  "keyboard_shortcuts.up": "for å flytta seg opp på lista",
+  "lightbox.close": "Lukk att",
   "lightbox.next": "Neste",
-  "lightbox.previous": "Forrige",
+  "lightbox.previous": "Førre",
   "lightbox.view_context": "Sjå kontekst",
   "lists.account.add": "Legg til i liste",
-  "lists.account.remove": "Ta burt fra liste",
+  "lists.account.remove": "Fjern frå liste",
   "lists.delete": "Slett liste",
   "lists.edit": "Rediger liste",
-  "lists.edit.submit": "Bytt tittel",
+  "lists.edit.submit": "Endre tittel",
   "lists.new.create": "Legg til liste",
-  "lists.new.title_placeholder": "Ny liste tittel",
-  "lists.search": "Søk gjennom folk du føljar",
+  "lists.new.title_placeholder": "Ny listetittel",
+  "lists.search": "Søk gjennom folk du følgjer",
   "lists.subheading": "Dine lister",
-  "load_pending": "{count, plural, one {# new item} other {# new items}}",
-  "loading_indicator.label": "Laster...",
-  "media_gallery.toggle_visible": "Toggle visibility",
+  "load_pending": "{count, plural, one {# nytt element} other {# nye element}}",
+  "loading_indicator.label": "Lastar...",
+  "media_gallery.toggle_visible": "Gjer synleg/usynleg",
   "missing_indicator.label": "Ikkje funne",
-  "missing_indicator.sublabel": "Denne ressursen ble ikkje funne",
-  "mute_modal.hide_notifications": "Gøyme notifikasjoner frå denne brukaren?",
-  "navigation_bar.apps": "Mobil apper",
-  "navigation_bar.blocks": "Blokka brukare",
-  "navigation_bar.community_timeline": "Lokal tidslinje",
-  "navigation_bar.compose": "Lag ein ny status",
-  "navigation_bar.direct": "Direkte meldingar",
+  "missing_indicator.sublabel": "Fann ikkje ressursen",
+  "mute_modal.hide_notifications": "Gøyme varsel frå denne brukaren?",
+  "navigation_bar.apps": "Mobilappar",
+  "navigation_bar.blocks": "Blokkerte brukarar",
+  "navigation_bar.bookmarks": "Bokmerke",
+  "navigation_bar.community_timeline": "Lokal tidsline",
+  "navigation_bar.compose": "Lag eit nytt tut",
+  "navigation_bar.direct": "Direktemeldingar",
   "navigation_bar.discover": "Oppdag",
-  "navigation_bar.domain_blocks": "Gøymte domener",
+  "navigation_bar.domain_blocks": "Skjulte domene",
   "navigation_bar.edit_profile": "Rediger profil",
-  "navigation_bar.favourites": "Favoritter",
-  "navigation_bar.filters": "Demp ord",
-  "navigation_bar.follow_requests": "Føljar førespurnader",
-  "navigation_bar.follows_and_followers": "Føljer og føljare",
-  "navigation_bar.info": "Om denne tenarmaskinen",
-  "navigation_bar.keyboard_shortcuts": "Hurtigtaster",
+  "navigation_bar.favourites": "Favorittar",
+  "navigation_bar.filters": "Målbundne ord",
+  "navigation_bar.follow_requests": "Fylgjeførespurnader",
+  "navigation_bar.follows_and_followers": "Fylgje og fylgjarar",
+  "navigation_bar.info": "Om denne tenaren",
+  "navigation_bar.keyboard_shortcuts": "Snøggtastar",
   "navigation_bar.lists": "Lister",
   "navigation_bar.logout": "Logg ut",
-  "navigation_bar.mutes": "Dempa brukare",
-  "navigation_bar.personal": "Personlig",
-  "navigation_bar.pins": "Festa tuter",
-  "navigation_bar.preferences": "Preferanser",
-  "navigation_bar.public_timeline": "Federert tidslinje",
-  "navigation_bar.security": "Sikkerheit",
-  "notification.favourite": "{name} likte din status",
-  "notification.follow": "{name} fulgte deg",
-  "notification.mention": "{name} nevnte deg",
-  "notification.poll": "Ein avstemming du har votert i har endt",
-  "notification.reblog": "{name} delte statusen din",
-  "notifications.clear": "Klarer notifikasjoner",
-  "notifications.clear_confirmation": "Er du sikker på at du vill permanent klarere alle notifikasjonene dine?",
-  "notifications.column_settings.alert": "Desktop notifikasjoner",
-  "notifications.column_settings.favourite": "Favoritter:",
-  "notifications.column_settings.filter_bar.advanced": "Vis alle kategorier",
-  "notifications.column_settings.filter_bar.category": "Rask filter bar",
+  "navigation_bar.mutes": "Målbundne brukarar",
+  "navigation_bar.personal": "Personleg",
+  "navigation_bar.pins": "Festa tut",
+  "navigation_bar.preferences": "Innstillingar",
+  "navigation_bar.public_timeline": "Føderert tidsline",
+  "navigation_bar.security": "Tryggleik",
+  "notification.favourite": "{name} merkte statusen din som favoritt",
+  "notification.follow": "{name} fylgde deg",
+  "notification.follow_request": "{name} har bedt om å fylgja deg",
+  "notification.mention": "{name} nemnde deg",
+  "notification.own_poll": "Rundspørjinga di er ferdig",
+  "notification.poll": "Ei rundspørjing du har røysta i er ferdig",
+  "notification.reblog": "{name} framheva statusen din",
+  "notifications.clear": "Tøm varsel",
+  "notifications.clear_confirmation": "Er du sikker på at du vil fjerna alle varsla dine for alltid?",
+  "notifications.column_settings.alert": "Skrivebordsvarsel",
+  "notifications.column_settings.favourite": "Favorittar:",
+  "notifications.column_settings.filter_bar.advanced": "Vis alle kategoriar",
+  "notifications.column_settings.filter_bar.category": "Snarfilterlinje",
   "notifications.column_settings.filter_bar.show": "Vis",
-  "notifications.column_settings.follow": "Nye føljare:",
+  "notifications.column_settings.follow": "Nye fylgjarar:",
+  "notifications.column_settings.follow_request": "Ny fylgjarførespurnader:",
   "notifications.column_settings.mention": "Nemningar:",
-  "notifications.column_settings.poll": "Avstemming resultater:",
-  "notifications.column_settings.push": "Push varsler",
-  "notifications.column_settings.reblog": "Framhevinger:",
+  "notifications.column_settings.poll": "Røysteresultat:",
+  "notifications.column_settings.push": "Pushvarsel",
+  "notifications.column_settings.reblog": "Framhevingar:",
   "notifications.column_settings.show": "Vis i kolonne",
-  "notifications.column_settings.sound": "Køyr lyd",
+  "notifications.column_settings.sound": "Spel av lyd",
   "notifications.filter.all": "Alle",
-  "notifications.filter.boosts": "Framhevinger",
-  "notifications.filter.favourites": "Favoritter",
-  "notifications.filter.follows": "Føljare",
+  "notifications.filter.boosts": "Framhevingar",
+  "notifications.filter.favourites": "Favorittar",
+  "notifications.filter.follows": "Fylgjer",
   "notifications.filter.mentions": "Nemningar",
-  "notifications.filter.polls": "Avstemmings resultater",
-  "notifications.group": "{count} notifikasjoner",
+  "notifications.filter.polls": "Røysteresultat",
+  "notifications.group": "{count} varsel",
   "poll.closed": "Lukka",
-  "poll.refresh": "Forfriske",
-  "poll.total_people": "{count, plural, one {# person} other {# people}}",
-  "poll.total_votes": "{count, plural, one {# vote} other {# votes}}",
-  "poll.vote": "Voter",
-  "poll.voted": "Du voterte for dette svaret",
-  "poll_button.add_poll": "Start ein avstemming",
-  "poll_button.remove_poll": "Slett avstemming",
-  "privacy.change": "Juster status synligheit",
-  "privacy.direct.long": "Post berre direkte til nemnte brukare",
+  "poll.refresh": "Oppdater",
+  "poll.total_people": "{count, plural, one {# person} other {# folk}}",
+  "poll.total_votes": "{count, plural, one {# røyst} other {# røyster}}",
+  "poll.vote": "Røyst",
+  "poll.voted": "Du røysta på dette svaret",
+  "poll_button.add_poll": "Start ei meiningsmåling",
+  "poll_button.remove_poll": "Fjern røyst",
+  "privacy.change": "Juster status-synlegheit",
+  "privacy.direct.long": "Legg berre ut for nemnde brukarar",
   "privacy.direct.short": "Direkte",
-  "privacy.private.long": "Post til berre føljare",
-  "privacy.private.short": "Berre-føljare",
-  "privacy.public.long": "Post til offentlege tidslinjar",
+  "privacy.private.long": "Post kun til følgjarar",
+  "privacy.private.short": "Kun følgjarar",
+  "privacy.public.long": "Post til offentlege tidslinjer",
   "privacy.public.short": "Offentleg",
-  "privacy.unlisted.long": "Do not show in public timelines",
+  "privacy.unlisted.long": "Ikkje post til offentlege tidslinjer",
   "privacy.unlisted.short": "Uoppført",
-  "refresh": "Forfrisk",
+  "refresh": "Oppdater",
   "regeneration_indicator.label": "Lastar…",
-  "regeneration_indicator.sublabel": "Din startside driver å blir forberedt!",
-  "relative_time.days": "{number}d",
-  "relative_time.hours": "{number}h",
-  "relative_time.just_now": "no",
-  "relative_time.minutes": "{number}m",
-  "relative_time.seconds": "{number}s",
-  "reply_indicator.cancel": "Avbrot",
-  "report.forward": "Send videre til {target}",
-  "report.forward_hint": "Denne brukaren er frå ein anna tenarmaskin. Send ein anonymisert versjon av rapporten til døm også?",
-  "report.hint": "Denne rapporten vill bli sendt til din tenarmaskin moderator. Du kan gi ein forklaring til kvifor du rapportérer denne brukaren under:",
-  "report.placeholder": "Tilleggskommentarer",
+  "regeneration_indicator.sublabel": "Heimetidslinja di vert førebudd!",
+  "relative_time.days": "{number}dg",
+  "relative_time.hours": "{number}t",
+  "relative_time.just_now": "nå",
+  "relative_time.minutes": "{number}min",
+  "relative_time.seconds": "{number}sek",
+  "reply_indicator.cancel": "Avbryt",
+  "report.forward": "Vidaresend til {target}",
+  "report.forward_hint": "Kontoen er frå ein annan tenar. Vil du senda ein anonymisert kopi av rapporten dit òg?",
+  "report.hint": "Rapporten vil verte sendt til dine tenarmoderatorar. Du kan oppgje ei forklaring på kvifor du rapporterer denne kontoen, under:",
+  "report.placeholder": "Tilleggskommentarar",
   "report.submit": "Send inn",
-  "report.target": "Rapportér {target}",
+  "report.target": "Rapporterer {target}",
   "search.placeholder": "Søk",
-  "search_popout.search_format": "Avansert søke 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.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": "status",
-  "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
+  "search_popout.tips.text": "Enkel tekst returnerer samsvarande visningsnamn, brukarnamn og emneknaggar",
   "search_popout.tips.user": "brukar",
   "search_results.accounts": "Folk",
-  "search_results.hashtags": "Emneknagger",
-  "search_results.statuses": "Tutar",
-  "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.",
-  "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
-  "status.admin_account": "Open moderation interface for @{name}",
-  "status.admin_status": "Open this status in the moderation interface",
-  "status.block": "Block @{name}",
-  "status.cancel_reblog_private": "Unboost",
-  "status.cannot_reblog": "This post cannot be boosted",
-  "status.copy": "Copy link to status",
+  "search_results.hashtags": "Emneknaggar",
+  "search_results.statuses": "Tut",
+  "search_results.statuses_fts_disabled": "På denne Matsodon-tenaren kan du ikkje søkja på tut etter innhaldet deira.",
+  "search_results.total": "{count, number} {count, plural, one {treff} other {treff}}",
+  "status.admin_account": "Opne moderasjonsgrensesnitt for @{name}",
+  "status.admin_status": "Opne denne statusen i moderasjonsgrensesnittet",
+  "status.block": "Blokker @{name}",
+  "status.bookmark": "Bokmerk",
+  "status.cancel_reblog_private": "Opphev framheving",
+  "status.cannot_reblog": "Denne posten kan ikkje framhevast",
+  "status.copy": "Kopier lenke til status",
   "status.delete": "Slett",
-  "status.detailed_status": "Detaljert samtale syn",
-  "status.direct": "Direkte meld @{name}",
-  "status.embed": "Bygge inn",
-  "status.favourite": "Lik",
-  "status.filtered": "Filter",
+  "status.detailed_status": "Detaljert samtalevisning",
+  "status.direct": "Send melding til @{name}",
+  "status.embed": "Bygg inn",
+  "status.favourite": "Favoritt",
+  "status.filtered": "Filtrert",
   "status.load_more": "Last inn meir",
-  "status.media_hidden": "Media gøymt",
+  "status.media_hidden": "Medium gøymd",
   "status.mention": "Nemn @{name}",
   "status.more": "Meir",
-  "status.mute": "Demp @{name}",
-  "status.mute_conversation": "Demp samtale",
-  "status.open": "Utvid statusen",
+  "status.mute": "Målbind @{name}",
+  "status.mute_conversation": "Målbind samtale",
+  "status.open": "Utvid denne statusen",
   "status.pin": "Fest på profil",
-  "status.pinned": "Fasta tut",
+  "status.pinned": "Festa tut",
   "status.read_more": "Les meir",
-  "status.reblog": "Fremhev",
-  "status.reblog_private": "Fremhev til orginale sjåare",
-  "status.reblogged_by": "{name} fremheivd",
-  "status.reblogs.empty": "Ikkje nokon har fremhevd tuten enno. Om nokon gjør det kjem det opp her.",
-  "status.redraft": "Slett & gjer om",
+  "status.reblog": "Framhev",
+  "status.reblog_private": "Framhev til dei originale mottakarane",
+  "status.reblogged_by": "{name} framheva",
+  "status.reblogs.empty": "Ingen har framheva dette tutet enno. Om nokon gjer, så dukkar det opp her.",
+  "status.redraft": "Slett & skriv på nytt",
+  "status.remove_bookmark": "Fjern bokmerke",
   "status.reply": "Svar",
   "status.replyAll": "Svar til tråd",
-  "status.report": "Rapportér @{name}",
+  "status.report": "Rapporter @{name}",
   "status.sensitive_warning": "Sensitivt innhald",
   "status.share": "Del",
   "status.show_less": "Vis mindre",
@@ -379,45 +392,48 @@
   "status.show_more_all": "Vis meir for alle",
   "status.show_thread": "Vis tråd",
   "status.uncached_media_warning": "Ikkje tilgjengeleg",
-  "status.unmute_conversation": "Ikke demp samtale",
-  "status.unpin": "Angre festing på profil",
-  "suggestions.dismiss": "Avbrot forslag",
-  "suggestions.header": "Du e kanskje interessert i…",
+  "status.unmute_conversation": "Opphev målbinding av samtalen",
+  "status.unpin": "Løys frå profil",
+  "suggestions.dismiss": "Avslå framlegg",
+  "suggestions.header": "Du er kanskje interessert i…",
   "tabs_bar.federated_timeline": "Føderert",
   "tabs_bar.home": "Heim",
   "tabs_bar.local_timeline": "Lokal",
-  "tabs_bar.notifications": "Notifikasjoner",
+  "tabs_bar.notifications": "Varsel",
   "tabs_bar.search": "Søk",
-  "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": "Kort tid gjennstår",
-  "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left",
-  "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking",
-  "trends.trending_now": "Kva som trender no",
-  "ui.beforeunload": "Din kladd vil bli forkastet om du forlater Mastodon.",
-  "upload_area.title": "Dra & slipp for å laste opp",
-  "upload_button.label": "Legg til media ({formats})",
-  "upload_error.limit": "Fil størrelsen er for stor.",
-  "upload_error.poll": "Fil opplasting ikkje lov i hop med avstemming.",
-  "upload_form.description": "Beskriv for synshemmede",
-  "upload_form.edit": "Redigér",
+  "time_remaining.days": "{number, plural, one {# dag} other {# dagar}} igjen",
+  "time_remaining.hours": "{number, plural, one {# time} other {# timar}} igjen",
+  "time_remaining.minutes": "{number, plural, one {# minutt} other {# minutt}} igjen",
+  "time_remaining.moments": "Kort tid igjen",
+  "time_remaining.seconds": "{number, plural, one {# sekund} other {# sekund}} igjen",
+  "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {folk}} snakkar",
+  "trends.trending_now": "Populært no",
+  "ui.beforeunload": "Kladden din forsvinn om du forlèt Mastodon no.",
+  "upload_area.title": "Drag & slepp for å lasta opp",
+  "upload_button.label": "Legg til medium ({formats})",
+  "upload_error.limit": "Du har gått over opplastingsgrensa.",
+  "upload_error.poll": "Filopplasting ikkje tillate med meiningsmålingar.",
+  "upload_form.audio_description": "Beskriv det for folk med hørselstap",
+  "upload_form.description": "Skildr for synshemja",
+  "upload_form.edit": "Rediger",
   "upload_form.undo": "Slett",
-  "upload_modal.analyzing_picture": "Analyserer bilde…",
+  "upload_form.video_description": "Beskriv det for folk med hørselstap eller synshemminger",
+  "upload_modal.analyzing_picture": "Analyserer bilete…",
   "upload_modal.apply": "Bruk",
   "upload_modal.description_placeholder": "Ein rask brun rev hoppar over den late hunden",
-  "upload_modal.detect_text": "Finn tekst i bildet",
-  "upload_modal.edit_media": "Redigér media",
-  "upload_modal.hint": "Klikk og dra sirkelen på forsyneren for å fokusere synspunktet slik det ska synes i alle minityrbildene.",
-  "upload_modal.preview_label": "Framsyn ({ratio})",
+  "upload_modal.detect_text": "Gjenkjenn tekst i biletet",
+  "upload_modal.edit_media": "Rediger medium",
+  "upload_modal.hint": "Klikk og dra sirkelen på førehandsvisninga for å velge fokuspunktet som alltid vil vere synleg på alle miniatyrbileta.",
+  "upload_modal.preview_label": "Førehandsvis ({ratio})",
   "upload_progress.label": "Lastar opp...",
   "video.close": "Lukk video",
+  "video.download": "Last ned fil",
   "video.exit_fullscreen": "Lukk fullskjerm",
-  "video.expand": "Expand video",
+  "video.expand": "Utvid video",
   "video.fullscreen": "Fullskjerm",
   "video.hide": "Gøym video",
   "video.mute": "Demp lyd",
   "video.pause": "Pause",
-  "video.play": "Spel",
+  "video.play": "Spel av",
   "video.unmute": "Av-dempe lyd"
 }
diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json
index 5c84c08fe..215cdb150 100644
--- a/app/javascript/mastodon/locales/no.json
+++ b/app/javascript/mastodon/locales/no.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Legg til eller fjern fra lister",
   "account.badges.bot": "Bot",
+  "account.badges.group": "Gruppe",
   "account.block": "Blokkér @{name}",
   "account.block_domain": "Skjul alt fra {domain}",
   "account.blocked": "Blokkert",
@@ -8,7 +9,7 @@
   "account.direct": "Direct Message @{name}",
   "account.domain_blocked": "Domenet skjult",
   "account.edit_profile": "Rediger profil",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "Vis frem på profilen",
   "account.follow": "Følg",
   "account.followers": "Følgere",
   "account.followers.empty": "Ingen følger denne brukeren ennå.",
@@ -24,7 +25,7 @@
   "account.moved_to": "{name} har flyttet til:",
   "account.mute": "Demp @{name}",
   "account.mute_notifications": "Ignorer varsler fra @{name}",
-  "account.muted": "Muted",
+  "account.muted": "Dempet",
   "account.never_active": "Aldri",
   "account.posts": "Innlegg",
   "account.posts_with_replies": "Toots with replies",
@@ -34,14 +35,15 @@
   "account.show_reblogs": "Vis boosts fra @{name}",
   "account.unblock": "Avblokker @{name}",
   "account.unblock_domain": "Vis {domain}",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "Ikke vis frem på profilen",
   "account.unfollow": "Avfølg",
   "account.unmute": "Avdemp @{name}",
   "account.unmute_notifications": "Vis varsler fra @{name}",
-  "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.",
-  "alert.rate_limited.title": "Rate limited",
+  "alert.rate_limited.message": "Vennligst prøv igjen etter kl. {retry_time, time, medium}.",
+  "alert.rate_limited.title": "Hastighetsbegrenset",
   "alert.unexpected.message": "En uventet feil oppstod.",
   "alert.unexpected.title": "Oops!",
+  "announcement.announcement": "Kunngjøring",
   "autosuggest_hashtag.per_week": "{count} per uke",
   "boost_modal.combo": "You kan trykke {combo} for å hoppe over dette neste gang",
   "bundle_column_error.body": "Noe gikk galt mens denne komponenten lastet.",
@@ -51,10 +53,11 @@
   "bundle_modal_error.message": "Noe gikk galt da denne komponenten lastet.",
   "bundle_modal_error.retry": "Prøv igjen",
   "column.blocks": "Blokkerte brukere",
+  "column.bookmarks": "Bokmerker",
   "column.community": "Lokal tidslinje",
   "column.direct": "Direktemeldinger",
   "column.directory": "Bla gjennom profiler",
-  "column.domain_blocks": "Hidden domains",
+  "column.domain_blocks": "Skjulte domener",
   "column.favourites": "Likt",
   "column.follow_requests": "Følgeforespørsler",
   "column.home": "Hjem",
@@ -82,16 +85,18 @@
   "compose_form.poll.duration": "Avstemningens varighet",
   "compose_form.poll.option_placeholder": "Valg {number}",
   "compose_form.poll.remove_option": "Fjern dette valget",
+  "compose_form.poll.switch_to_multiple": "Endre avstemning til å tillate flere valg",
+  "compose_form.poll.switch_to_single": "Endre avstemning til å tillate ett valg",
   "compose_form.publish": "Tut",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Merk media som sensitivt",
-  "compose_form.sensitive.marked": "Media is marked as sensitive",
-  "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
-  "compose_form.spoiler.marked": "Text is hidden behind warning",
-  "compose_form.spoiler.unmarked": "Text is not hidden",
+  "compose_form.sensitive.marked": "Mediet er merket som sensitiv",
+  "compose_form.sensitive.unmarked": "Mediet er ikke merket som sensitiv",
+  "compose_form.spoiler.marked": "Teksten er skjult bak en advarsel",
+  "compose_form.spoiler.unmarked": "Teksten er ikke skjult",
   "compose_form.spoiler_placeholder": "Innholdsadvarsel",
   "confirmation_modal.cancel": "Avbryt",
-  "confirmations.block.block_and_report": "Block & Report",
+  "confirmations.block.block_and_report": "Blokker og rapporter",
   "confirmations.block.confirm": "Blokkèr",
   "confirmations.block.message": "Er du sikker på at du vil blokkere {name}?",
   "confirmations.delete.confirm": "Slett",
@@ -103,22 +108,22 @@
   "confirmations.logout.confirm": "Logg ut",
   "confirmations.logout.message": "Er du sikker på at du vil logge ut?",
   "confirmations.mute.confirm": "Demp",
-  "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.explanation": "Dette vil skjule innlegg fra dem og innlegg som nevner dem, men det vil fortsatt la dem se dine innlegg og å følge deg.",
   "confirmations.mute.message": "Er du sikker på at du vil dempe {name}?",
-  "confirmations.redraft.confirm": "Delete & redraft",
+  "confirmations.redraft.confirm": "Slett og drøft på nytt",
   "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.",
   "confirmations.reply.confirm": "Svar",
-  "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
+  "confirmations.reply.message": "Å svare nå vil overskrive meldingen du skriver for øyeblikket. Er du sikker på at du vil fortsette?",
   "confirmations.unfollow.confirm": "Slutt å følge",
   "confirmations.unfollow.message": "Er du sikker på at du vil slutte å følge {name}?",
-  "conversation.delete": "Delete conversation",
-  "conversation.mark_as_read": "Mark as read",
-  "conversation.open": "View conversation",
+  "conversation.delete": "Slett samtalen",
+  "conversation.mark_as_read": "Marker som lest",
+  "conversation.open": "Vis samtale",
   "conversation.with": "Med {names}",
-  "directory.federated": "From known fediverse",
-  "directory.local": "From {domain} only",
-  "directory.new_arrivals": "New arrivals",
-  "directory.recently_active": "Recently active",
+  "directory.federated": "Fra det kjente strømiverset",
+  "directory.local": "Kun fra {domain}",
+  "directory.new_arrivals": "Nye ankomster",
+  "directory.recently_active": "Nylig aktiv",
   "embed.instructions": "Kopier koden under for å bygge inn denne statusen på hjemmesiden din.",
   "embed.preview": "Slik kommer det til å se ut:",
   "emoji_button.activity": "Aktivitet",
@@ -135,176 +140,182 @@
   "emoji_button.search_results": "Søkeresultat",
   "emoji_button.symbols": "Symboler",
   "emoji_button.travel": "Reise & steder",
-  "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.account_timeline": "Ingen tuter er her!",
+  "empty_column.account_unavailable": "Profilen er utilgjengelig",
+  "empty_column.blocks": "Du har ikke blokkert noen brukere enda.",
+  "empty_column.bookmarked_statuses": "Du har ikke bokmerket noen tuter enda. Når du bokmerker en, vil den dukke opp her.",
   "empty_column.community": "Den lokale tidslinjen er tom. Skriv noe offentlig for å få snøballen til å rulle!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
-  "empty_column.domain_blocks": "There are no hidden domains yet.",
-  "empty_column.favourited_statuses": "You don't have any favourite toots yet. When you favourite one, it will show up here.",
-  "empty_column.favourites": "No one has favourited this toot yet. When someone does, they will show up here.",
-  "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.direct": "Du har ingen direktemeldinger enda. Når du mottar eller sender en, vil den dukke opp her.",
+  "empty_column.domain_blocks": "Det er ingen skjulte domener enda.",
+  "empty_column.favourited_statuses": "Du har ikke likt noen tuter enda. Når du liker en, vil den dukke opp her.",
+  "empty_column.favourites": "Ingen har likt denne tuten enda. Når noen gjør det, vil de dukke opp her.",
+  "empty_column.follow_requests": "Du har ingen følgeforespørsler enda. Når du mottar en, vil den dukke opp her.",
   "empty_column.hashtag": "Det er ingenting i denne hashtagen ennå.",
   "empty_column.home": "Du har ikke fulgt noen ennå. Besøk {publlic} eller bruk søk for å komme i gang og møte andre brukere.",
   "empty_column.home.public_timeline": "en offentlig tidslinje",
   "empty_column.list": "Det er ingenting i denne listen ennå. Når medlemmene av denne listen legger ut nye statuser vil de dukke opp her.",
-  "empty_column.lists": "You don't have any lists yet. When you create one, it will show up here.",
-  "empty_column.mutes": "You haven't muted any users yet.",
+  "empty_column.lists": "Du har ingen lister enda. Når du lager en, vil den dukke opp her.",
+  "empty_column.mutes": "Du har ikke dempet noen brukere enda.",
   "empty_column.notifications": "Du har ingen varsler ennå. Kommuniser med andre for å begynne samtalen.",
   "empty_column.public": "Det er ingenting her! Skriv noe offentlig, eller følg brukere manuelt fra andre instanser for å fylle den opp",
-  "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.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.",
-  "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard",
-  "errors.unexpected_crash.report_issue": "Report issue",
+  "error.unexpected_crash.explanation": "På grunn av en bug i koden vår eller et nettleserkompatibilitetsproblem, kunne denne siden ikke vises riktig.",
+  "error.unexpected_crash.next_steps": "Prøv å oppfriske siden. Dersom det ikke hjelper, vil du kanskje fortsatt kunne bruke Mastodon gjennom en annen nettleser eller app.",
+  "errors.unexpected_crash.copy_stacktrace": "Kopier stacktrace-en til utklippstavlen",
+  "errors.unexpected_crash.report_issue": "Rapporter en feil",
   "follow_request.authorize": "Autorisér",
   "follow_request.reject": "Avvis",
-  "getting_started.developers": "Developers",
-  "getting_started.directory": "Profile directory",
-  "getting_started.documentation": "Documentation",
+  "getting_started.developers": "Utviklere",
+  "getting_started.directory": "Profilmappe",
+  "getting_started.documentation": "Dokumentasjon",
   "getting_started.heading": "Kom i gang",
-  "getting_started.invite": "Invite people",
+  "getting_started.invite": "Inviter folk",
   "getting_started.open_source_notice": "Mastodon er fri programvare. Du kan bidra eller rapportere problemer på GitHub på {github}.",
-  "getting_started.security": "Security",
-  "getting_started.terms": "Terms of service",
-  "hashtag.column_header.tag_mode.all": "and {additional}",
-  "hashtag.column_header.tag_mode.any": "or {additional}",
-  "hashtag.column_header.tag_mode.none": "without {additional}",
-  "hashtag.column_settings.select.no_options_message": "No suggestions found",
-  "hashtag.column_settings.select.placeholder": "Enter hashtags…",
-  "hashtag.column_settings.tag_mode.all": "All of these",
-  "hashtag.column_settings.tag_mode.any": "Any of these",
-  "hashtag.column_settings.tag_mode.none": "None of these",
+  "getting_started.security": "Kontoinnstillinger",
+  "getting_started.terms": "Bruksvilkår",
+  "hashtag.column_header.tag_mode.all": "og {additional}",
+  "hashtag.column_header.tag_mode.any": "eller {additional}",
+  "hashtag.column_header.tag_mode.none": "uten {additional}",
+  "hashtag.column_settings.select.no_options_message": "Ingen forslag ble funnet",
+  "hashtag.column_settings.select.placeholder": "Skriv inn emneknagger …",
+  "hashtag.column_settings.tag_mode.all": "Alle disse",
+  "hashtag.column_settings.tag_mode.any": "Enhver av disse",
+  "hashtag.column_settings.tag_mode.none": "Ingen av disse",
   "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
-  "home.column_settings.basic": "Enkel",
+  "home.column_settings.basic": "Enkelt",
   "home.column_settings.show_reblogs": "Vis fremhevinger",
   "home.column_settings.show_replies": "Vis svar",
-  "intervals.full.days": "{number, plural, one {# day} other {# days}}",
-  "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
-  "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}",
-  "introduction.federation.action": "Next",
-  "introduction.federation.federated.headline": "Federated",
-  "introduction.federation.federated.text": "Public posts from other servers of the fediverse will appear in the federated timeline.",
-  "introduction.federation.home.headline": "Home",
-  "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!",
-  "introduction.federation.local.headline": "Local",
-  "introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
-  "introduction.interactions.action": "Finish toot-orial!",
-  "introduction.interactions.favourite.headline": "Favourite",
-  "introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
-  "introduction.interactions.reblog.headline": "Boost",
-  "introduction.interactions.reblog.text": "You can share other people's toots with your followers by boosting them.",
+  "intervals.full.days": "{number, plural,one {# dag} other {# dager}}",
+  "intervals.full.hours": "{number, plural, one {# time} other {# timer}}",
+  "intervals.full.minutes": "{number, plural, one {# minutt} other {# minutter}}",
+  "introduction.federation.action": "Neste",
+  "introduction.federation.federated.headline": "Felles",
+  "introduction.federation.federated.text": "Offentlige innlegg fra andre tjenere i strømiverset vil dukke opp i fellestidslinjen.",
+  "introduction.federation.home.headline": "Hjem",
+  "introduction.federation.home.text": "Innlegg fra folk du følger vil dukke opp på hjemmestrømmen din. Du kan følge hvem som helst på enhver tjener!",
+  "introduction.federation.local.headline": "Lokalt",
+  "introduction.federation.local.text": "Offentlige innlegg fra folk på samme tjener som deg, vil dukke opp i den lokale tidslinjen.",
+  "introduction.interactions.action": "Avslutt veiledningen!",
+  "introduction.interactions.favourite.headline": "Lik",
+  "introduction.interactions.favourite.text": "Du kan lagre en tut til senere, og la forfatteren vite at du satte pris på det, ved å like det.",
+  "introduction.interactions.reblog.headline": "Fremhev",
+  "introduction.interactions.reblog.text": "Du kan dele andre folks tutinger med dine følgere ved å fremheve dem.",
   "introduction.interactions.reply.headline": "Svar",
-  "introduction.interactions.reply.text": "You can reply to other people's and your own toots, which will chain them together in a conversation.",
-  "introduction.welcome.action": "Let's go!",
-  "introduction.welcome.headline": "First steps",
-  "introduction.welcome.text": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.",
+  "introduction.interactions.reply.text": "Du kan svare på tuten til andre folk og til deg selv, som vil binde dem sammen til en samtale.",
+  "introduction.welcome.action": "La oss sette i gang!",
+  "introduction.welcome.headline": "De første skritt",
+  "introduction.welcome.text": "Velkommen til strømiverset! Om noen øyeblikk, vil du kunne sende meldinger og snakke med dine venner over et bredt spekter av tjenere. Men denne tjeneren, {domain}, er spesiell, for den inneholder profilen din, så husk på navnet dens.",
   "keyboard_shortcuts.back": "for å navigere tilbake",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "åpne listen over blokkerte brukere",
   "keyboard_shortcuts.boost": "å fremheve",
   "keyboard_shortcuts.column": "å fokusere en status i en av kolonnene",
   "keyboard_shortcuts.compose": "å fokusere komponeringsfeltet",
   "keyboard_shortcuts.description": "Beskrivelse",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "åpne direktemeldingskolonnen",
   "keyboard_shortcuts.down": "for å flytte ned i listen",
-  "keyboard_shortcuts.enter": "to open status",
+  "keyboard_shortcuts.enter": "å åpne status",
   "keyboard_shortcuts.favourite": "for å favorittmarkere",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "åpne listen over likte ting",
+  "keyboard_shortcuts.federated": "for å åpne fellestidslinjen",
   "keyboard_shortcuts.heading": "Keyboard Shortcuts",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "åpne hjemmetidslinjen",
   "keyboard_shortcuts.hotkey": "Lyntast",
   "keyboard_shortcuts.legend": "å vise denne forklaringen",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "åpne den lokale tidslinjen",
   "keyboard_shortcuts.mention": "å nevne forfatter",
-  "keyboard_shortcuts.muted": "to open muted users list",
-  "keyboard_shortcuts.my_profile": "to open your profile",
-  "keyboard_shortcuts.notifications": "to open notifications column",
-  "keyboard_shortcuts.pinned": "to open pinned toots list",
-  "keyboard_shortcuts.profile": "to open author's profile",
+  "keyboard_shortcuts.muted": "åpne listen over dempede brukere",
+  "keyboard_shortcuts.my_profile": "å åpne profilen din",
+  "keyboard_shortcuts.notifications": "åpne varslingskolonnen",
+  "keyboard_shortcuts.open_media": "å åpne media",
+  "keyboard_shortcuts.pinned": "åpne listen over klistrede tuter",
+  "keyboard_shortcuts.profile": "åpne forfatterens profil",
   "keyboard_shortcuts.reply": "for å svare",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "åpne følgingsforespørselslisten",
   "keyboard_shortcuts.search": "å fokusere søk",
-  "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.start": "åpne «Sett i gang»-kolonnen",
+  "keyboard_shortcuts.toggle_hidden": "å vise/skjule tekst bak en innholdsadvarsel",
+  "keyboard_shortcuts.toggle_sensitivity": "å vise/skjule media",
   "keyboard_shortcuts.toot": "å starte en helt ny tut",
   "keyboard_shortcuts.unfocus": "å ufokusere komponerings-/søkefeltet",
   "keyboard_shortcuts.up": "å flytte opp i listen",
   "lightbox.close": "Lukk",
   "lightbox.next": "Neste",
   "lightbox.previous": "Forrige",
-  "lightbox.view_context": "View context",
+  "lightbox.view_context": "Vis sammenheng",
   "lists.account.add": "Legg til i listen",
   "lists.account.remove": "Fjern fra listen",
   "lists.delete": "Slett listen",
   "lists.edit": "Rediger listen",
-  "lists.edit.submit": "Change title",
+  "lists.edit.submit": "Endre tittel",
   "lists.new.create": "Ligg til liste",
   "lists.new.title_placeholder": "Ny listetittel",
   "lists.search": "Søk blant personer du følger",
   "lists.subheading": "Dine lister",
-  "load_pending": "{count, plural, one {# new item} other {# new items}}",
+  "load_pending": "{count, plural,one {# ny gjenstand} other {# nye gjenstander}}",
   "loading_indicator.label": "Laster...",
   "media_gallery.toggle_visible": "Veksle synlighet",
   "missing_indicator.label": "Ikke funnet",
   "missing_indicator.sublabel": "Denne ressursen ble ikke funnet",
   "mute_modal.hide_notifications": "Skjul varslinger fra denne brukeren?",
-  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.apps": "Mobilapper",
   "navigation_bar.blocks": "Blokkerte brukere",
+  "navigation_bar.bookmarks": "Bokmerker",
   "navigation_bar.community_timeline": "Lokal tidslinje",
-  "navigation_bar.compose": "Compose new toot",
-  "navigation_bar.direct": "Direct messages",
-  "navigation_bar.discover": "Discover",
-  "navigation_bar.domain_blocks": "Hidden domains",
+  "navigation_bar.compose": "Skriv en ny tut",
+  "navigation_bar.direct": "Direktemeldinger",
+  "navigation_bar.discover": "Oppdag",
+  "navigation_bar.domain_blocks": "Skjulte domener",
   "navigation_bar.edit_profile": "Rediger profil",
   "navigation_bar.favourites": "Favoritter",
-  "navigation_bar.filters": "Muted words",
+  "navigation_bar.filters": "Stilnede ord",
   "navigation_bar.follow_requests": "Følgeforespørsler",
-  "navigation_bar.follows_and_followers": "Follows and followers",
+  "navigation_bar.follows_and_followers": "Følginger og følgere",
   "navigation_bar.info": "Utvidet informasjon",
   "navigation_bar.keyboard_shortcuts": "Tastatursnarveier",
   "navigation_bar.lists": "Lister",
   "navigation_bar.logout": "Logg ut",
   "navigation_bar.mutes": "Dempede brukere",
-  "navigation_bar.personal": "Personal",
+  "navigation_bar.personal": "Personlig",
   "navigation_bar.pins": "Festa tuter",
   "navigation_bar.preferences": "Preferanser",
   "navigation_bar.public_timeline": "Felles tidslinje",
   "navigation_bar.security": "Sikkerhet",
   "notification.favourite": "{name} likte din status",
   "notification.follow": "{name} fulgte deg",
+  "notification.follow_request": "{name} har bedt om å få følge deg",
   "notification.mention": "{name} nevnte deg",
-  "notification.poll": "A poll you have voted in has ended",
+  "notification.own_poll": "Avstemningen din er ferdig",
+  "notification.poll": "En avstemning du har stemt på har avsluttet",
   "notification.reblog": "{name} fremhevde din status",
   "notifications.clear": "Fjern varsler",
   "notifications.clear_confirmation": "Er du sikker på at du vil fjerne alle dine varsler permanent?",
   "notifications.column_settings.alert": "Skrivebordsvarslinger",
   "notifications.column_settings.favourite": "Likt:",
-  "notifications.column_settings.filter_bar.advanced": "Display all categories",
-  "notifications.column_settings.filter_bar.category": "Quick filter bar",
-  "notifications.column_settings.filter_bar.show": "Show",
+  "notifications.column_settings.filter_bar.advanced": "Vis alle kategorier",
+  "notifications.column_settings.filter_bar.category": "Hurtigfiltreringslinje",
+  "notifications.column_settings.filter_bar.show": "Vis",
   "notifications.column_settings.follow": "Nye følgere:",
+  "notifications.column_settings.follow_request": "Nye følgerforespørsler:",
   "notifications.column_settings.mention": "Nevnt:",
-  "notifications.column_settings.poll": "Poll results:",
+  "notifications.column_settings.poll": "Avstemningsresultater:",
   "notifications.column_settings.push": "Push varsler",
   "notifications.column_settings.reblog": "Fremhevet:",
   "notifications.column_settings.show": "Vis i kolonne",
   "notifications.column_settings.sound": "Spill lyd",
   "notifications.filter.all": "Alle",
-  "notifications.filter.boosts": "Boosts",
+  "notifications.filter.boosts": "Fremhevinger",
   "notifications.filter.favourites": "Favoritter",
-  "notifications.filter.follows": "Follows",
-  "notifications.filter.mentions": "Mentions",
-  "notifications.filter.polls": "Poll results",
-  "notifications.group": "{count} notifications",
-  "poll.closed": "Closed",
+  "notifications.filter.follows": "Følginger",
+  "notifications.filter.mentions": "Nevnelser",
+  "notifications.filter.polls": "Avstemningsresultater",
+  "notifications.group": "{count} varslinger",
+  "poll.closed": "Lukket",
   "poll.refresh": "Oppdater",
-  "poll.total_people": "{count, plural, one {# person} other {# people}}",
-  "poll.total_votes": "{count, plural, one {# vote} other {# votes}}",
-  "poll.vote": "Vote",
-  "poll.voted": "You voted for this answer",
-  "poll_button.add_poll": "Add a poll",
-  "poll_button.remove_poll": "Remove poll",
+  "poll.total_people": "{count, plural, one {# person} other {# personer}}",
+  "poll.total_votes": "{count, plural, one {# stemme} other {# stemmer}}",
+  "poll.vote": "Stem",
+  "poll.voted": "Du stemte på dette svaret",
+  "poll_button.add_poll": "Legg til en avstemning",
+  "poll_button.remove_poll": "Fjern avstemningen",
   "privacy.change": "Justér synlighet",
   "privacy.direct.long": "Post kun til nevnte brukere",
   "privacy.direct.short": "Direkte",
@@ -314,45 +325,46 @@
   "privacy.public.short": "Offentlig",
   "privacy.unlisted.long": "Ikke vis i offentlige tidslinjer",
   "privacy.unlisted.short": "Uoppført",
-  "refresh": "Refresh",
+  "refresh": "Oppfrisk",
   "regeneration_indicator.label": "Laster…",
   "regeneration_indicator.sublabel": "Dine startside forberedes!",
   "relative_time.days": "{number}d",
-  "relative_time.hours": "{number}h",
+  "relative_time.hours": "{number}t",
   "relative_time.just_now": "nå",
   "relative_time.minutes": "{number}m",
   "relative_time.seconds": "{number}s",
   "reply_indicator.cancel": "Avbryt",
-  "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": "Videresend til {target}",
+  "report.forward_hint": "Denne kontoen er fra en annen tjener. Vil du sende en anonymisert kopi av rapporten dit også?",
   "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
   "report.placeholder": "Tilleggskommentarer",
   "report.submit": "Send inn",
   "report.target": "Rapporterer",
   "search.placeholder": "Søk",
   "search_popout.search_format": "Avansert søkeformat",
-  "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
+  "search_popout.tips.full_text": "Enkel tekst gir resultater for statuser du har skrevet, likt, 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": "People",
-  "search_results.hashtags": "Hashtags",
-  "search_results.statuses": "Toots",
-  "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.",
+  "search_results.accounts": "Folk",
+  "search_results.hashtags": "Emneknagger",
+  "search_results.statuses": "Tuter",
+  "search_results.statuses_fts_disabled": "Å søke i tuter etter innhold er ikke skrudd på i denne Mastodon-tjeneren.",
   "search_results.total": "{count, number} {count, plural, one {resultat} other {resultater}}",
-  "status.admin_account": "Open moderation interface for @{name}",
-  "status.admin_status": "Open this status in the moderation interface",
-  "status.block": "Block @{name}",
-  "status.cancel_reblog_private": "Unboost",
+  "status.admin_account": "Åpne moderatorgrensesnittet for @{name}",
+  "status.admin_status": "Åpne denne statusen i moderatorgrensesnittet",
+  "status.block": "Blokkér @{name}",
+  "status.bookmark": "Bokmerke",
+  "status.cancel_reblog_private": "Fjern fremheving",
   "status.cannot_reblog": "Denne posten kan ikke fremheves",
-  "status.copy": "Copy link to status",
+  "status.copy": "Kopier lenken til statusen",
   "status.delete": "Slett",
-  "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.detailed_status": "Detaljert samtalevisning",
+  "status.direct": "Send direktemelding til @{name}",
   "status.embed": "Bygge inn",
   "status.favourite": "Lik",
-  "status.filtered": "Filtered",
+  "status.filtered": "Filtrert",
   "status.load_more": "Last mer",
   "status.media_hidden": "Media skjult",
   "status.mention": "Nevn @{name}",
@@ -361,57 +373,61 @@
   "status.mute_conversation": "Demp samtale",
   "status.open": "Utvid denne statusen",
   "status.pin": "Fest på profilen",
-  "status.pinned": "Pinned toot",
+  "status.pinned": "Festet tut",
   "status.read_more": "Les mer",
   "status.reblog": "Fremhev",
-  "status.reblog_private": "Boost to original audience",
+  "status.reblog_private": "Fremhev til det opprinnelige publikummet",
   "status.reblogged_by": "Fremhevd av {name}",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
-  "status.redraft": "Delete & re-draft",
+  "status.reblogs.empty": "Ingen har fremhevet denne tuten enda. Når noen gjør det, vil de dukke opp her.",
+  "status.redraft": "Slett og drøft på nytt",
+  "status.remove_bookmark": "Fjern bokmerke",
   "status.reply": "Svar",
   "status.replyAll": "Svar til samtale",
   "status.report": "Rapporter @{name}",
   "status.sensitive_warning": "Følsomt innhold",
   "status.share": "Del",
   "status.show_less": "Vis mindre",
-  "status.show_less_all": "Show less for all",
+  "status.show_less_all": "Vis mindre for alle",
   "status.show_more": "Vis mer",
-  "status.show_more_all": "Show more for all",
-  "status.show_thread": "Show thread",
+  "status.show_more_all": "Vis mer for alle",
+  "status.show_thread": "Vis tråden",
   "status.uncached_media_warning": "Ikke tilgjengelig",
   "status.unmute_conversation": "Ikke demp samtale",
   "status.unpin": "Angre festing på profilen",
-  "suggestions.dismiss": "Dismiss suggestion",
-  "suggestions.header": "You might be interested in…",
+  "suggestions.dismiss": "Utelukk forslaget",
+  "suggestions.header": "Du er kanskje interessert i …",
   "tabs_bar.federated_timeline": "Felles",
   "tabs_bar.home": "Hjem",
   "tabs_bar.local_timeline": "Lokal",
   "tabs_bar.notifications": "Varslinger",
   "tabs_bar.search": "Søk",
-  "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",
-  "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking",
-  "trends.trending_now": "Trending now",
+  "time_remaining.days": "{number, plural,one {# dag} other {# dager}} igjen",
+  "time_remaining.hours": "{number, plural, one {# time} other {# timer}} igjen",
+  "time_remaining.minutes": "{number, plural, one {# minutt} other {# minutter}} igjen",
+  "time_remaining.moments": "Gjenværende øyeblikk",
+  "time_remaining.seconds": "{number, plural, one {# sekund} other {# sekunder}} igjen",
+  "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {personer}} snakker om det",
+  "trends.trending_now": "Trender nå",
   "ui.beforeunload": "Din kladd vil bli forkastet om du forlater Mastodon.",
   "upload_area.title": "Dra og slipp for å laste opp",
   "upload_button.label": "Legg til media",
-  "upload_error.limit": "File upload limit exceeded.",
-  "upload_error.poll": "File upload not allowed with polls.",
+  "upload_error.limit": "Filopplastingsgrensen er oversteget.",
+  "upload_error.poll": "Filopplasting inni avstemninger er ikke tillatt.",
+  "upload_form.audio_description": "Beskriv det for folk med hørselstap",
   "upload_form.description": "Beskriv for synshemmede",
-  "upload_form.edit": "Edit",
+  "upload_form.edit": "Rediger",
   "upload_form.undo": "Angre",
-  "upload_modal.analyzing_picture": "Analyzing picture…",
+  "upload_form.video_description": "Beskriv det for folk med hørselstap eller synshemminger",
+  "upload_modal.analyzing_picture": "Analyserer bildet …",
   "upload_modal.apply": "Bruk",
-  "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.preview_label": "Preview ({ratio})",
+  "upload_modal.description_placeholder": "Når du en gang kommer, neste sommer, skal vi atter drikke vin",
+  "upload_modal.detect_text": "Oppdag tekst i bildet",
+  "upload_modal.edit_media": "Rediger media",
+  "upload_modal.hint": "Klikk eller dra sirkelen i forhåndsvisningen for å velge hovedpunktet som alltid vil bli vist i alle miniatyrbilder.",
+  "upload_modal.preview_label": "Forhåndsvisning ({ratio})",
   "upload_progress.label": "Laster opp...",
   "video.close": "Lukk video",
+  "video.download": "Last ned fil",
   "video.exit_fullscreen": "Lukk fullskjerm",
   "video.expand": "Utvid video",
   "video.fullscreen": "Fullskjerm",
diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json
index 1ca5f42ed..b7701c17e 100644
--- a/app/javascript/mastodon/locales/oc.json
+++ b/app/javascript/mastodon/locales/oc.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Ajustar o tirar de las listas",
   "account.badges.bot": "Robòt",
+  "account.badges.group": "Group",
   "account.block": "Blocar @{name}",
   "account.block_domain": "Tot amagar del domeni {domain}",
   "account.blocked": "Blocat",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Taus limitat",
   "alert.unexpected.message": "Una error s’es producha.",
   "alert.unexpected.title": "Ops !",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} per setmana",
   "boost_modal.combo": "Podètz botar {combo} per passar aquò lo còp que ven",
   "bundle_column_error.body": "Quicòm a fach mèuca pendent lo cargament d’aqueste compausant.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Quicòm a fach mèuca pendent lo cargament d’aqueste compausant.",
   "bundle_modal_error.retry": "Tornar ensajar",
   "column.blocks": "Personas blocadas",
+  "column.bookmarks": "Marcadors",
   "column.community": "Flux public local",
   "column.direct": "Messatges dirèctes",
   "column.directory": "Percórrer los perfils",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Durada del sondatge",
   "compose_form.poll.option_placeholder": "Opcion {number}",
   "compose_form.poll.remove_option": "Levar aquesta opcion",
+  "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": "Tut",
   "compose_form.publish_loud": "{publish} !",
   "compose_form.sensitive.hide": "Marcar coma sensible",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Cap de tuts aquí !",
   "empty_column.account_unavailable": "Perfil pas disponible",
   "empty_column.blocks": "Avètz pas blocat degun pel moment.",
+  "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.domain_blocks": "I a pas encara cap de domeni amagat.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "dobrir la lista dels utilizaires silenciats",
   "keyboard_shortcuts.my_profile": "dobrir vòstre perfil",
   "keyboard_shortcuts.notifications": "dobrir la colomna de notificacions",
+  "keyboard_shortcuts.open_media": "dobrir lo mèdia",
   "keyboard_shortcuts.pinned": "dobrir la lista dels tuts penjats",
   "keyboard_shortcuts.profile": "dobrir lo perfil de l’autor",
   "keyboard_shortcuts.reply": "respondre",
@@ -226,7 +233,7 @@
   "keyboard_shortcuts.search": "anar a la recèrca",
   "keyboard_shortcuts.start": "dobrir la colomna « Per començar »",
   "keyboard_shortcuts.toggle_hidden": "mostrar/amagar lo tèxte dels avertiments",
-  "keyboard_shortcuts.toggle_sensitivity": "per mostrar/rescondre los mèdias",
+  "keyboard_shortcuts.toggle_sensitivity": "mostrar/rescondre los mèdias",
   "keyboard_shortcuts.toot": "començar un estatut tot novèl",
   "keyboard_shortcuts.unfocus": "quitar lo camp tèxte/de recèrca",
   "keyboard_shortcuts.up": "far montar dins la lista",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Rescondre las notificacions d’aquesta persona ?",
   "navigation_bar.apps": "Aplicacions mobil",
   "navigation_bar.blocks": "Personas blocadas",
+  "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",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Seguretat",
   "notification.favourite": "{name} a ajustat a sos favorits",
   "notification.follow": "{name} vos sèc",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} vos a mencionat",
+  "notification.own_poll": "Vòstre sondatge es acabat",
   "notification.poll": "Avètz participat a un sondatge que ven de s’acabar",
   "notification.reblog": "{name} a partejat vòstre estatut",
   "notifications.clear": "Escafar",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Barra de recèrca rapida",
   "notifications.column_settings.filter_bar.show": "Mostrar",
   "notifications.column_settings.follow": "Nòus seguidors :",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "Mencions :",
   "notifications.column_settings.poll": "Resultats del sondatge :",
   "notifications.column_settings.push": "Notificacions",
@@ -344,6 +355,7 @@
   "status.admin_account": "Dobrir l’interfàcia de moderacion per @{name}",
   "status.admin_status": "Dobrir aqueste estatut dins l’interfàcia de moderacion",
   "status.block": "Blocar @{name}",
+  "status.bookmark": "Marcador",
   "status.cancel_reblog_private": "Quitar de partejar",
   "status.cannot_reblog": "Aqueste estatut pòt pas èsser partejat",
   "status.copy": "Copiar lo ligam de l’estatut",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} a partejat",
   "status.reblogs.empty": "Degun a pas encara partejat aqueste tut. Quand qualqu’un o farà, apareisserà aquí.",
   "status.redraft": "Escafar e tornar formular",
+  "status.remove_bookmark": "Suprimir lo marcador",
   "status.reply": "Respondre",
   "status.replyAll": "Respondre a la conversacion",
   "status.report": "Senhalar @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Ajustar un mèdia (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_error.limit": "Talha maximum pels mandadís subrepassada.",
   "upload_error.poll": "Lo mandadís de fichièr es pas autorizat pels sondatges.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "Descripcion pels mal vesents",
   "upload_form.edit": "Modificar",
   "upload_form.undo": "Suprimir",
+  "upload_form.video_description": "Describe for people with hearing loss or visual impairment",
   "upload_modal.analyzing_picture": "Analisi de l’imatge…",
   "upload_modal.apply": "Aplicar",
   "upload_modal.description_placeholder": "Lo dròlle bilingüe manja un yaourt de ròcs exagonals e kiwis verds farà un an mai",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Apercebut ({ratio})",
   "upload_progress.label": "Mandadís…",
   "video.close": "Tampar la vidèo",
+  "video.download": "Telecargar lo fichièr",
   "video.exit_fullscreen": "Sortir plen ecran",
   "video.expand": "Agrandir la vidèo",
   "video.fullscreen": "Ecran complèt",
diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json
index b3513ea46..3874f1596 100644
--- a/app/javascript/mastodon/locales/pl.json
+++ b/app/javascript/mastodon/locales/pl.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Dodaj lub usuń z list",
   "account.badges.bot": "Bot",
+  "account.badges.group": "Group",
   "account.block": "Blokuj @{name}",
   "account.block_domain": "Blokuj wszystko z {domain}",
   "account.blocked": "Zablokowany(-a)",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Ograniczony czasowo",
   "alert.unexpected.message": "Wystąpił nieoczekiwany błąd.",
   "alert.unexpected.title": "O nie!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} co tydzień",
   "boost_modal.combo": "Naciśnij {combo}, aby pominąć to następnym razem",
   "bundle_column_error.body": "Coś poszło nie tak podczas ładowania tego składnika.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Coś poszło nie tak podczas ładowania tego składnika.",
   "bundle_modal_error.retry": "Spróbuj ponownie",
   "column.blocks": "Zablokowani użytkownicy",
+  "column.bookmarks": "Zakładki",
   "column.community": "Lokalna oś czasu",
   "column.direct": "Wiadomości bezpośrednie",
   "column.directory": "Przeglądaj profile",
@@ -86,6 +89,8 @@
   "compose_form.poll.duration": "Czas trwania głosowania",
   "compose_form.poll.option_placeholder": "Opcja {number}",
   "compose_form.poll.remove_option": "Usuń tę opcję",
+  "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": "Wyślij",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Oznacz multimedia jako wrażliwe",
@@ -142,6 +147,7 @@
   "empty_column.account_timeline": "Brak wpisów tutaj!",
   "empty_column.account_unavailable": "Profil niedostępny",
   "empty_column.blocks": "Nie zablokowałeś(-aś) jeszcze żadnego użytkownika.",
+  "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.domain_blocks": "Brak ukrytych domen.",
@@ -223,6 +229,7 @@
   "keyboard_shortcuts.muted": "aby przejść do listy wyciszonych użytkowników",
   "keyboard_shortcuts.my_profile": "aby otworzyć własny profil",
   "keyboard_shortcuts.notifications": "aby otworzyć kolumnę powiadomień",
+  "keyboard_shortcuts.open_media": "aby otworzyć zawartość multimedialną",
   "keyboard_shortcuts.pinned": "aby przejść do listy przypiętych wpisów",
   "keyboard_shortcuts.profile": "aby przejść do profilu autora wpisu",
   "keyboard_shortcuts.reply": "aby odpowiedzieć",
@@ -255,6 +262,7 @@
   "mute_modal.hide_notifications": "Chcesz ukryć powiadomienia od tego użytkownika?",
   "navigation_bar.apps": "Aplikacje mobilne",
   "navigation_bar.blocks": "Zablokowani użytkownicy",
+  "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",
@@ -278,7 +286,9 @@
   "navigation_bar.security": "Bezpieczeństwo",
   "notification.favourite": "{name} dodał(a) Twój wpis do ulubionych",
   "notification.follow": "{name} zaczął(-ęła) Cię śledzić",
+  "notification.follow_request": "{name} poprosił(a) o możliwość śledzenia Cię",
   "notification.mention": "{name} wspomniał(a) o tobie",
+  "notification.own_poll": "Twoje głosowanie zakończyło się",
   "notification.poll": "Głosowanie w którym brałeś(-aś) udział zakończyła się",
   "notification.reblog": "{name} podbił(a) Twój wpis",
   "notifications.clear": "Wyczyść powiadomienia",
@@ -289,6 +299,7 @@
   "notifications.column_settings.filter_bar.category": "Szybkie filtrowanie",
   "notifications.column_settings.filter_bar.show": "Pokaż",
   "notifications.column_settings.follow": "Nowi śledzący:",
+  "notifications.column_settings.follow_request": "Nowe prośby o możliwość śledzenia:",
   "notifications.column_settings.mention": "Wspomnienia:",
   "notifications.column_settings.poll": "Wyniki głosowania:",
   "notifications.column_settings.push": "Powiadomienia push",
@@ -349,6 +360,7 @@
   "status.admin_account": "Otwórz interfejs moderacyjny dla @{name}",
   "status.admin_status": "Otwórz ten wpis w interfejsie moderacyjnym",
   "status.block": "Zablokuj @{name}",
+  "status.bookmark": "Dodaj zakładkę",
   "status.cancel_reblog_private": "Cofnij podbicie",
   "status.cannot_reblog": "Ten wpis nie może zostać podbity",
   "status.copy": "Skopiuj odnośnik do wpisu",
@@ -373,6 +385,7 @@
   "status.reblogged_by": "{name} podbił(a)",
   "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ę",
   "status.reply": "Odpowiedz",
   "status.replyAll": "Odpowiedz na wątek",
   "status.report": "Zgłoś @{name}",
@@ -405,9 +418,11 @@
   "upload_button.label": "Dodaj zawartość multimedialną (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_error.limit": "Przekroczono limit plików do wysłania.",
   "upload_error.poll": "Dołączanie plików nie dozwolone z głosowaniami.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "Wprowadź opis dla niewidomych i niedowidzących",
   "upload_form.edit": "Edytuj",
   "upload_form.undo": "Usuń",
+  "upload_form.video_description": "Describe for people with hearing loss or visual impairment",
   "upload_modal.analyzing_picture": "Analizowanie obrazu…",
   "upload_modal.apply": "Zastosuj",
   "upload_modal.description_placeholder": "Pchnąć w tę łódź jeża lub ośm skrzyń fig",
@@ -417,6 +432,7 @@
   "upload_modal.preview_label": "Podgląd ({ratio})",
   "upload_progress.label": "Wysyłanie…",
   "video.close": "Zamknij film",
+  "video.download": "Pobierz plik",
   "video.exit_fullscreen": "Opuść tryb pełnoekranowy",
   "video.expand": "Rozszerz film",
   "video.fullscreen": "Pełny ekran",
diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json
index 7c6a1fd22..f78c327fb 100644
--- a/app/javascript/mastodon/locales/pt-BR.json
+++ b/app/javascript/mastodon/locales/pt-BR.json
@@ -1,29 +1,30 @@
 {
-  "account.add_or_remove_from_list": "Adicionar ou remover de listas",
+  "account.add_or_remove_from_list": "Adicionar ou remover das listas",
   "account.badges.bot": "Robô",
+  "account.badges.group": "Grupo",
   "account.block": "Bloquear @{name}",
-  "account.block_domain": "Esconder tudo de {domain}",
+  "account.block_domain": "Bloquear {domain}",
   "account.blocked": "Bloqueado",
   "account.cancel_follow_request": "Cancelar solicitação para seguir",
-  "account.direct": "Direct Message @{name}",
-  "account.domain_blocked": "Domínio escondido",
+  "account.direct": "Enviar toot direto para @{name}",
+  "account.domain_blocked": "Domínio bloqueado",
   "account.edit_profile": "Editar perfil",
   "account.endorse": "Destacar no perfil",
   "account.follow": "Seguir",
   "account.followers": "Seguidores",
-  "account.followers.empty": "Ninguém segue esse usuário no momento.",
-  "account.follows": "Segue",
-  "account.follows.empty": "Esse usuário não segue ninguém no momento.",
+  "account.followers.empty": "Nada aqui.",
+  "account.follows": "Seguindo",
+  "account.follows.empty": "Nada aqui.",
   "account.follows_you": "Segue você",
-  "account.hide_reblogs": "Esconder compartilhamentos de @{name}",
+  "account.hide_reblogs": "Ocultar boosts de @{name}",
   "account.last_status": "Última atividade",
-  "account.link_verified_on": "A posse desse link foi verificada em {date}",
-  "account.locked_info": "Essa conta está trancada. Se você a seguir sua solicitação será revisada manualmente.",
+  "account.link_verified_on": "O link foi verificado em {date}",
+  "account.locked_info": "Esta conta está trancada. Sua solicitação para seguir requer aprovação manual do usuário.",
   "account.media": "Mídia",
   "account.mention": "Mencionar @{name}",
   "account.moved_to": "{name} se mudou para:",
   "account.mute": "Silenciar @{name}",
-  "account.mute_notifications": "Silenciar notificações de @{name}",
+  "account.mute_notifications": "Ocultar notificações de @{name}",
   "account.muted": "Silenciado",
   "account.never_active": "Nunca",
   "account.posts": "Toots",
@@ -31,40 +32,42 @@
   "account.report": "Denunciar @{name}",
   "account.requested": "Aguardando aprovação. Clique para cancelar a solicitação",
   "account.share": "Compartilhar perfil de @{name}",
-  "account.show_reblogs": "Mostra compartilhamentos de @{name}",
+  "account.show_reblogs": "Mostrar boosts de @{name}",
   "account.unblock": "Desbloquear @{name}",
   "account.unblock_domain": "Desbloquear {domain}",
   "account.unendorse": "Não destacar no perfil",
   "account.unfollow": "Deixar de seguir",
-  "account.unmute": "Não silenciar @{name}",
-  "account.unmute_notifications": "Retirar silêncio das notificações vindas de @{name}",
+  "account.unmute": "Desfazer silêncio de @{name}",
+  "account.unmute_notifications": "Mostrar notificações de @{name}",
   "alert.rate_limited.message": "Por favor tente novamente após {retry_time, time, medium}.",
-  "alert.rate_limited.title": "Limite de tentativas",
-  "alert.unexpected.message": "Um erro inesperado ocorreu.",
+  "alert.rate_limited.title": "Tentativas limitadas",
+  "alert.unexpected.message": "Ocorreu um erro inesperado.",
   "alert.unexpected.title": "Eita!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} por semana",
-  "boost_modal.combo": "Você pode pressionar {combo} para ignorar este diálogo na próxima vez",
-  "bundle_column_error.body": "Algo de errado aconteceu enquanto este componente era carregado.",
+  "boost_modal.combo": "Pressione {combo} para ignorar este diálogo na próxima vez",
+  "bundle_column_error.body": "Ocorreu um problema ao carregar este componente.",
   "bundle_column_error.retry": "Tente novamente",
   "bundle_column_error.title": "Erro de rede",
   "bundle_modal_error.close": "Fechar",
-  "bundle_modal_error.message": "Algo de errado aconteceu enquanto este componente era carregado.",
+  "bundle_modal_error.message": "Ocorreu um problema ao carregar este componente.",
   "bundle_modal_error.retry": "Tente novamente",
   "column.blocks": "Usuários bloqueados",
+  "column.bookmarks": "Salvos",
   "column.community": "Local",
-  "column.direct": "Mensagens diretas",
+  "column.direct": "Mensagens Diretas",
   "column.directory": "Explorar perfis",
-  "column.domain_blocks": "Domínios escondidos",
+  "column.domain_blocks": "Domínios bloqueados",
   "column.favourites": "Favoritos",
   "column.follow_requests": "Seguidores pendentes",
   "column.home": "Página inicial",
   "column.lists": "Listas",
   "column.mutes": "Usuários silenciados",
   "column.notifications": "Notificações",
-  "column.pins": "Postagens fixadas",
-  "column.public": "Global",
+  "column.pins": "Toots fixados",
+  "column.public": "Linha global",
   "column_back_button.label": "Voltar",
-  "column_header.hide_settings": "Esconder configurações",
+  "column_header.hide_settings": "Ocultar configurações",
   "column_header.moveLeft_settings": "Mover coluna para a esquerda",
   "column_header.moveRight_settings": "Mover coluna para a direita",
   "column_header.pin": "Fixar",
@@ -73,192 +76,197 @@
   "column_subheading.settings": "Configurações",
   "community.column_settings.media_only": "Apenas mídia",
   "compose_form.direct_message_warning": "Este toot só será enviado aos usuários mencionados.",
-  "compose_form.direct_message_warning_learn_more": "Saber mais",
-  "compose_form.hashtag_warning": "Esse toot não será listado em nenhuma hashtag por ser não listado. Somente toots públicos podem ser pesquisados por hashtag.",
-  "compose_form.lock_disclaimer": "A sua conta não está {locked}. Qualquer pessoa pode te seguir e visualizar postagens direcionadas a apenas seguidores.",
+  "compose_form.direct_message_warning_learn_more": "Saiba mais",
+  "compose_form.hashtag_warning": "O toot não será listado nas hashtags porque não é público. Somente toots públicos podem ser pesquisados por hashtag. Dica: Poste como Não-listado.",
+  "compose_form.lock_disclaimer": "Sua conta não está {locked}. Qualquer pessoa pode te seguir e ver seus toots privados.",
   "compose_form.lock_disclaimer.lock": "trancada",
   "compose_form.placeholder": "No que você está pensando?",
-  "compose_form.poll.add_option": "Adicionar uma opção",
+  "compose_form.poll.add_option": "Adicionar opção",
   "compose_form.poll.duration": "Duração da enquete",
   "compose_form.poll.option_placeholder": "Opção {number}",
-  "compose_form.poll.remove_option": "Remover essa opção",
-  "compose_form.publish": "Publicar",
+  "compose_form.poll.remove_option": "Remover opção",
+  "compose_form.poll.switch_to_multiple": "Alterar para múltiplos votos",
+  "compose_form.poll.switch_to_single": "Alterar para voto único",
+  "compose_form.publish": "TOOT",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Marcar mídia como sensível",
   "compose_form.sensitive.marked": "Mídia está marcada como sensível",
   "compose_form.sensitive.unmarked": "Mídia não está marcada como sensível",
-  "compose_form.spoiler.marked": "O texto está escondido por um aviso de conteúdo",
-  "compose_form.spoiler.unmarked": "O texto não está escondido",
-  "compose_form.spoiler_placeholder": "Aviso de conteúdo",
+  "compose_form.spoiler.marked": "O texto está oculto por um aviso de conteúdo",
+  "compose_form.spoiler.unmarked": "Sem Aviso de Conteúdo",
+  "compose_form.spoiler_placeholder": "Aviso de Conteúdo aqui",
   "confirmation_modal.cancel": "Cancelar",
   "confirmations.block.block_and_report": "Bloquear e denunciar",
   "confirmations.block.confirm": "Bloquear",
-  "confirmations.block.message": "Você tem certeza de que quer bloquear {name}?",
+  "confirmations.block.message": "Você tem certeza de que deseja bloquear {name}?",
   "confirmations.delete.confirm": "Excluir",
-  "confirmations.delete.message": "Você tem certeza de que quer excluir esta postagem?",
+  "confirmations.delete.message": "Excluir este toot?",
   "confirmations.delete_list.confirm": "Excluir",
-  "confirmations.delete_list.message": "Você tem certeza que quer deletar permanentemente a lista?",
-  "confirmations.domain_block.confirm": "Esconder o domínio inteiro",
-  "confirmations.domain_block.message": "Você quer mesmo bloquear {domain} inteiro? Na maioria dos casos, silenciar ou bloquear alguns usuários é o suficiente e o recomendado. Você não vai ver conteúdo desse domínio em nenhuma das timelines públicas ou nas suas notificações. Seus seguidores desse domínio serão removidos.",
+  "confirmations.delete_list.message": "Você tem certeza de que deseja excluir esta lista?",
+  "confirmations.domain_block.confirm": "Bloquear domínio",
+  "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.logout.confirm": "Sair",
-  "confirmations.logout.message": "Tem certeza que deseja encerrar a sessão?",
+  "confirmations.logout.message": "Você tem certeza de que deseja sair?",
   "confirmations.mute.confirm": "Silenciar",
-  "confirmations.mute.explanation": "Isto irá esconder postagens e postagens que mencionam, mas ainda vai permitir que eles vejam suas publicações e sigam você.",
-  "confirmations.mute.message": "Você tem certeza de que quer silenciar {name}?",
-  "confirmations.redraft.confirm": "Apagar & usar como rascunho",
-  "confirmations.redraft.message": "Você tem certeza que deseja apagar esse status e usá-lo como rascunho? Seus compartilhamentos e favoritos serão perdidos e as respostas ao toot original ficarão desconectadas.",
+  "confirmations.mute.explanation": "Isso ocultará toots deles e toots mencionando-os, mas ainda permitirá que eles vejam seus toots e te sigam.",
+  "confirmations.mute.message": "Você tem certeza de que deseja silenciar {name}?",
+  "confirmations.redraft.confirm": "Excluir e rascunhar",
+  "confirmations.redraft.message": "Você tem certeza de que deseja apagar o toot e usá-lo como rascunho? Boosts e favoritos serão perdidos e as respostas ao toot original ficarão desconectadas.",
   "confirmations.reply.confirm": "Responder",
-  "confirmations.reply.message": "Responder agora vai sobrescrever a mensagem que você está compondo. Você tem certeza que quer continuar?",
+  "confirmations.reply.message": "Responder agora vai sobrescrever o toot que você está compondo. Deseja continuar?",
   "confirmations.unfollow.confirm": "Deixar de seguir",
-  "confirmations.unfollow.message": "Você tem certeza de que quer deixar de seguir {name}?",
+  "confirmations.unfollow.message": "Deixar de seguir {name}?",
   "conversation.delete": "Excluir conversa",
   "conversation.mark_as_read": "Marcar como lida",
   "conversation.open": "Ver conversa",
   "conversation.with": "Com {names}",
-  "directory.federated": "De fediverso conhecido",
-  "directory.local": "De {domain} apenas",
+  "directory.federated": "Do fediverso",
+  "directory.local": "Apenas do {domain}",
   "directory.new_arrivals": "Acabaram de chegar",
-  "directory.recently_active": "Reverta esta propriedade para seu valor padrão",
-  "embed.instructions": "Incorpore esta postagem em seu site copiando o código abaixo.",
-  "embed.preview": "Aqui está uma previsão de como ficará:",
+  "directory.recently_active": "Ativos recentemente",
+  "embed.instructions": "Incorpore este toot no seu site copiando o código abaixo.",
+  "embed.preview": "Veja como vai ficar:",
   "emoji_button.activity": "Atividades",
-  "emoji_button.custom": "Customizados",
+  "emoji_button.custom": "Personalizados",
   "emoji_button.flags": "Bandeiras",
-  "emoji_button.food": "Comidas & Bebidas",
-  "emoji_button.label": "Inserir Emoji",
+  "emoji_button.food": "Comidas e Bebidas",
+  "emoji_button.label": "Inserir emoji",
   "emoji_button.nature": "Natureza",
-  "emoji_button.not_found": "Não tem emojos! (╯°□°)╯︵ ┻━┻",
+  "emoji_button.not_found": "Sem emojis! (╯°□°)╯︵ ┻━┻",
   "emoji_button.objects": "Objetos",
   "emoji_button.people": "Pessoas",
   "emoji_button.recent": "Usados frequentemente",
-  "emoji_button.search": "Buscar...",
-  "emoji_button.search_results": "Resultados da busca",
+  "emoji_button.search": "Pesquisar...",
+  "emoji_button.search_results": "Resultados da pesquisa",
   "emoji_button.symbols": "Símbolos",
-  "emoji_button.travel": "Viagens & Lugares",
-  "empty_column.account_timeline": "Não há toots aqui!",
+  "emoji_button.travel": "Viagens e Lugares",
+  "empty_column.account_timeline": "Nada aqui!",
   "empty_column.account_unavailable": "Perfil indisponível",
-  "empty_column.blocks": "Você ainda não bloqueou nenhum usuário.",
-  "empty_column.community": "A timeline local está vazia. Escreva algo publicamente para começar!",
-  "empty_column.direct": "Você não tem nenhuma mensagem direta ainda. Quando você enviar ou receber uma, as mensagens aparecerão por aqui.",
-  "empty_column.domain_blocks": "Ainda não há nenhum domínio escondido.",
-  "empty_column.favourited_statuses": "Você ainda não tem nenhum toot favorito. Quando você favoritar um toot, ele aparecerá aqui.",
-  "empty_column.favourites": "Ninguém favoritou esse toot até agora. Quando alguém favoritar, a pessoa aparecerá aqui.",
-  "empty_column.follow_requests": "Você não tem nenhum pedido de seguir por agora. Quando você receber um, ele aparecerá aqui.",
-  "empty_column.hashtag": "Ainda não há qualquer conteúdo com essa hashtag.",
-  "empty_column.home": "Você ainda não segue usuário algum. Visite a timeline {public} ou use o buscador para procurar e conhecer outros usuários.",
-  "empty_column.home.public_timeline": "global",
-  "empty_column.list": "Ainda não há nada nesta lista. Quando membros dessa lista fizerem novas postagens, elas aparecerão aqui.",
-  "empty_column.lists": "Você ainda não tem nenhuma lista. Quando você criar uma, ela aparecerá aqui.",
-  "empty_column.mutes": "Você ainda não silenciou nenhum usuário.",
-  "empty_column.notifications": "Você ainda não possui notificações. Interaja com outros usuários para começar a conversar.",
-  "empty_column.public": "Não há nada aqui! Escreva algo publicamente ou siga manualmente usuários de outras instâncias",
-  "error.unexpected_crash.explanation": "Devido a um bug em nosso código ou a um problema de compatibilidade do navegador, esta página não pode ser exibida corretamente.",
-  "error.unexpected_crash.next_steps": "Tente atualizar a página. Se isso não ajudar, você ainda pode usar Mastodon através de um navegador diferente ou aplicativo nativo.",
-  "errors.unexpected_crash.copy_stacktrace": "Copiar stacktrace para a área de transferência",
+  "empty_column.blocks": "Nada aqui.",
+  "empty_column.bookmarked_statuses": "Sem toots salvos. Quando você salvar alguns, eles aparecerão aqui.",
+  "empty_column.community": "A linha do tempo local está vazia. Poste algo publicamente para começar!",
+  "empty_column.direct": "Nada aqui. Quando você enviar ou receber toots diretos, eles aparecerão aqui.",
+  "empty_column.domain_blocks": "Nada aqui.",
+  "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_requests": "Nada aqui. Quando você receber solicitações de seguidores, elas aparecerão aqui.",
+  "empty_column.hashtag": "Nada aqui.",
+  "empty_column.home": "Sua linha do tempo está vazia. Visite a linha {public} ou use a pesquisa para conhecer outros usuários!",
+  "empty_column.home.public_timeline": "a linha do tempo pública",
+  "empty_column.list": "Nada aqui. Quando os usuários nesta lista postarem toots, eles aparecerão aqui.",
+  "empty_column.lists": "Nada aqui. Quando você criar listas, elas aparecerão aqui.",
+  "empty_column.mutes": "Nada aqui.",
+  "empty_column.notifications": "Nada aqui. Interaja com outros usuários para começar a conversar.",
+  "empty_column.public": "Nada aqui! Poste algo publicamente ou siga usuários de outras instâncias",
+  "error.unexpected_crash.explanation": "Devido a um problema em nosso código ou de compatibilidade com o navegador, esta página não pôde ser exibida corretamente.",
+  "error.unexpected_crash.next_steps": "Tente atualizar a página. Se não resolver, você ainda pode usar o Mastodon em outro navegador ou aplicativo.",
+  "errors.unexpected_crash.copy_stacktrace": "Copiar estado da pilha para a área de transferência",
   "errors.unexpected_crash.report_issue": "Reportar problema",
-  "follow_request.authorize": "Autorizar",
-  "follow_request.reject": "Rejeitar",
+  "follow_request.authorize": "Aprovar",
+  "follow_request.reject": "Vetar",
   "getting_started.developers": "Desenvolvedores",
   "getting_started.directory": "Diretório de perfis",
   "getting_started.documentation": "Documentação",
   "getting_started.heading": "Primeiros passos",
-  "getting_started.invite": "Convide pessoas",
-  "getting_started.open_source_notice": "Mastodon é um software de código aberto. Você pode contribuir ou reportar problemas na página do GitHub do projeto: {github}.",
-  "getting_started.security": "Segurança",
+  "getting_started.invite": "Convidar pessoas",
+  "getting_started.open_source_notice": "Mastodon é um software de código aberto. Você pode contribuir ou reportar problemas na página do projeto no GitHub em {github}.",
+  "getting_started.security": "Configurações da conta",
   "getting_started.terms": "Termos de serviço",
   "hashtag.column_header.tag_mode.all": "e {additional}",
   "hashtag.column_header.tag_mode.any": "ou {additional}",
   "hashtag.column_header.tag_mode.none": "sem {additional}",
   "hashtag.column_settings.select.no_options_message": "Nenhuma sugestão encontrada",
-  "hashtag.column_settings.select.placeholder": "Adicione as hashtags…",
+  "hashtag.column_settings.select.placeholder": "Adicione hashtags…",
   "hashtag.column_settings.tag_mode.all": "Todas essas",
   "hashtag.column_settings.tag_mode.any": "Qualquer uma dessas",
   "hashtag.column_settings.tag_mode.none": "Nenhuma dessas",
-  "hashtag.column_settings.tag_toggle": "Incluir outras hashtags nessa coluna",
+  "hashtag.column_settings.tag_toggle": "Adicionar mais hashtags",
   "home.column_settings.basic": "Básico",
-  "home.column_settings.show_reblogs": "Mostrar compartilhamentos",
-  "home.column_settings.show_replies": "Mostrar as respostas",
+  "home.column_settings.show_reblogs": "Mostrar boosts",
+  "home.column_settings.show_replies": "Mostrar respostas",
   "intervals.full.days": "{number, plural, one {# dia} other {# dias}}",
   "intervals.full.hours": "{number, plural, one {# hora} other {# horas}}",
   "intervals.full.minutes": "{number, plural, one {# minuto} other {# minutos}}",
   "introduction.federation.action": "Próximo",
   "introduction.federation.federated.headline": "Global",
-  "introduction.federation.federated.text": "Posts públicos de outros servidores do fediverso vão aparecer na timeline global.",
-  "introduction.federation.home.headline": "Início",
-  "introduction.federation.home.text": "Posts de pessoas que você segue vão aparecer na sua página inicial. Você pode seguir pessoas de qualquer servidor!",
+  "introduction.federation.federated.text": "Toots públicos de outras instâncias do fediverso aparecerão na linha global.",
+  "introduction.federation.home.headline": "Página inicial",
+  "introduction.federation.home.text": "Os toots das pessoas que você segue aparecerão na página inicial. Você pode seguir pessoas de qualquer instância!",
   "introduction.federation.local.headline": "Local",
-  "introduction.federation.local.text": "Posts públicos de pessoas no mesmo servidor que você vão aparecer na timeline local.",
-  "introduction.interactions.action": "Finalizar o tutorial!",
+  "introduction.federation.local.text": "Toots públicos de pessoas na mesma instância que você aparecerão na linha local.",
+  "introduction.interactions.action": "Terminar o tutorial!",
   "introduction.interactions.favourite.headline": "Favoritos",
-  "introduction.interactions.favourite.text": "Você pode salvar um toot pra mais tarde, e deixar a pessoa que postou saber que você gostou, favoritando-o.",
-  "introduction.interactions.reblog.headline": "Compartilhamento",
-  "introduction.interactions.reblog.text": "Você pode mostrar toots de outras pessoas aos seus seguidores compartilhando.",
+  "introduction.interactions.favourite.text": "Ao favoritar, você salva o toot para mais tarde ou sinaliza ao autor que você gostou do toot.",
+  "introduction.interactions.reblog.headline": "Boost",
+  "introduction.interactions.reblog.text": "Ao dar boost, você compartilha toots de outras pessoas para seus seguidores.",
   "introduction.interactions.reply.headline": "Responder",
-  "introduction.interactions.reply.text": "Você pode responder a toots de outras pessoas e aos seus, e isso vai uni-los em uma conversa.",
-  "introduction.welcome.action": "Vamos!",
+  "introduction.interactions.reply.text": "Você pode responder aos toots de outras pessoas e formar uma conversa ou responder aos seus e formar uma sequência.",
+  "introduction.welcome.action": "É HORA DO SHOW!",
   "introduction.welcome.headline": "Primeiros passos",
-  "introduction.welcome.text": "Boas vindas ao fediverso! Em alguns momentos, você vai poder transmitir mensagens e falar com pessoas amigas através de uma variedade de servidores. Mas esse servidor, {domain}, é especial—é onde o seu perfil está hospedado, então lembre do nome.",
-  "keyboard_shortcuts.back": "para navegar de volta",
-  "keyboard_shortcuts.blocked": "para abrir a lista de usuários bloqueados",
-  "keyboard_shortcuts.boost": "para compartilhar",
-  "keyboard_shortcuts.column": "Focar um status em uma das colunas",
-  "keyboard_shortcuts.compose": "para focar a área de redação",
+  "introduction.welcome.text": "Boas vindas ao fediverso! Logo você vai poder publicar mensagens e falar com pessoas amigas através de uma variedade de instâncias. Mas esta instância, {domain}, é especial — é onde o seu perfil está guardado, então lembre do nome dela com carinho.",
+  "keyboard_shortcuts.back": "voltar",
+  "keyboard_shortcuts.blocked": "abrir lista de usuários bloqueados",
+  "keyboard_shortcuts.boost": "dar boost",
+  "keyboard_shortcuts.column": "focar no toot de uma das colunas",
+  "keyboard_shortcuts.compose": "focar na composição",
   "keyboard_shortcuts.description": "Descrição",
-  "keyboard_shortcuts.direct": "para abrir a coluna de mensagens diretas",
-  "keyboard_shortcuts.down": "para mover para baixo na lista",
-  "keyboard_shortcuts.enter": "para expandir um status",
-  "keyboard_shortcuts.favourite": "para adicionar aos favoritos",
-  "keyboard_shortcuts.favourites": "para abrir a lista de favoritos",
-  "keyboard_shortcuts.federated": "para abrir a timeline global",
+  "keyboard_shortcuts.direct": "abrir Mensagens Diretas",
+  "keyboard_shortcuts.down": "mover para baixo na linha",
+  "keyboard_shortcuts.enter": "expandir um toot",
+  "keyboard_shortcuts.favourite": "favoritar",
+  "keyboard_shortcuts.favourites": "abrir os favoritos",
+  "keyboard_shortcuts.federated": "abrir linha global",
   "keyboard_shortcuts.heading": "Atalhos de teclado",
-  "keyboard_shortcuts.home": "para abrir a página inicial",
+  "keyboard_shortcuts.home": "abrir página inicial",
   "keyboard_shortcuts.hotkey": "Atalho",
-  "keyboard_shortcuts.legend": "para mostrar essa legenda",
-  "keyboard_shortcuts.local": "para abrir a timeline local",
-  "keyboard_shortcuts.mention": "para mencionar o autor",
-  "keyboard_shortcuts.muted": "para abrir a lista de usuários silenciados",
-  "keyboard_shortcuts.my_profile": "para abrir o seu perfil",
-  "keyboard_shortcuts.notifications": "para abrir a coluna de notificações",
-  "keyboard_shortcuts.pinned": "para abrir a lista de toots fixados",
-  "keyboard_shortcuts.profile": "para abrir o perfil do autor",
-  "keyboard_shortcuts.reply": "para responder",
-  "keyboard_shortcuts.requests": "para abrir a lista de seguidores pendentes",
-  "keyboard_shortcuts.search": "para focar a pesquisa",
-  "keyboard_shortcuts.start": "para abrir a coluna \"primeiros passos\"",
-  "keyboard_shortcuts.toggle_hidden": "mostrar/esconder o texto com aviso de conteúdo",
-  "keyboard_shortcuts.toggle_sensitivity": "mostrar/esconder mídia",
-  "keyboard_shortcuts.toot": "para compor um novo toot",
-  "keyboard_shortcuts.unfocus": "para remover o foco da área de composição/pesquisa",
-  "keyboard_shortcuts.up": "para mover para cima na lista",
+  "keyboard_shortcuts.legend": "mostrar estes atalhos",
+  "keyboard_shortcuts.local": "abrir linha local",
+  "keyboard_shortcuts.mention": "mencionar autor",
+  "keyboard_shortcuts.muted": "abrir lista de usuários silenciados",
+  "keyboard_shortcuts.my_profile": "abrir seu perfil",
+  "keyboard_shortcuts.notifications": "abrir notificações",
+  "keyboard_shortcuts.open_media": "abrir mídia",
+  "keyboard_shortcuts.pinned": "abrir toots fixados",
+  "keyboard_shortcuts.profile": "abrir o perfil do autor",
+  "keyboard_shortcuts.reply": "responder",
+  "keyboard_shortcuts.requests": "abrir lista de seguidores pendentes",
+  "keyboard_shortcuts.search": "focar na pesquisa",
+  "keyboard_shortcuts.start": "abrir o \"Primeiros passos\"",
+  "keyboard_shortcuts.toggle_hidden": "mostrar/ocultar o toot com Aviso de Conteúdo",
+  "keyboard_shortcuts.toggle_sensitivity": "mostrar/ocultar mídia",
+  "keyboard_shortcuts.toot": "compor novo toot",
+  "keyboard_shortcuts.unfocus": "sair da composição ou da pesquisa",
+  "keyboard_shortcuts.up": "mover para cima na linha",
   "lightbox.close": "Fechar",
   "lightbox.next": "Próximo",
   "lightbox.previous": "Anterior",
   "lightbox.view_context": "Ver contexto",
-  "lists.account.add": "Adicionar a listas",
+  "lists.account.add": "Adicionar à lista",
   "lists.account.remove": "Remover da lista",
   "lists.delete": "Excluir lista",
   "lists.edit": "Editar lista",
-  "lists.edit.submit": "Mudar o título",
-  "lists.new.create": "Adicionar lista",
-  "lists.new.title_placeholder": "Novo título da lista",
+  "lists.edit.submit": "Renomear",
+  "lists.new.create": "Criar lista",
+  "lists.new.title_placeholder": "Nome da lista",
   "lists.search": "Procurar entre as pessoas que você segue",
   "lists.subheading": "Suas listas",
   "load_pending": "{count, plural, one {# novo item} other {# novos items}}",
   "loading_indicator.label": "Carregando...",
-  "media_gallery.toggle_visible": "Esconder/Mostrar",
+  "media_gallery.toggle_visible": "Mostrar/Ocultar",
   "missing_indicator.label": "Não encontrado",
   "missing_indicator.sublabel": "Esse recurso não pôde ser encontrado",
-  "mute_modal.hide_notifications": "Esconder notificações deste usuário?",
-  "navigation_bar.apps": "Apps",
+  "mute_modal.hide_notifications": "Ocultar notificações deste usuário?",
+  "navigation_bar.apps": "Aplicativos",
   "navigation_bar.blocks": "Usuários bloqueados",
+  "navigation_bar.bookmarks": "Salvos",
   "navigation_bar.community_timeline": "Local",
-  "navigation_bar.compose": "Compor um novo toot",
-  "navigation_bar.direct": "Mensagens diretas",
+  "navigation_bar.compose": "Compor novo toot",
+  "navigation_bar.direct": "Mensagens Diretas",
   "navigation_bar.discover": "Descobrir",
-  "navigation_bar.domain_blocks": "Domínios escondidos",
+  "navigation_bar.domain_blocks": "Domínios bloqueados",
   "navigation_bar.edit_profile": "Editar perfil",
   "navigation_bar.favourites": "Favoritos",
-  "navigation_bar.filters": "Palavras silenciadas",
+  "navigation_bar.filters": "Palavras filtradas",
   "navigation_bar.follow_requests": "Seguidores pendentes",
   "navigation_bar.follows_and_followers": "Seguindo e seguidores",
   "navigation_bar.info": "Mais informações",
@@ -267,53 +275,56 @@
   "navigation_bar.logout": "Sair",
   "navigation_bar.mutes": "Usuários silenciados",
   "navigation_bar.personal": "Pessoal",
-  "navigation_bar.pins": "Postagens fixadas",
+  "navigation_bar.pins": "Toots fixados",
   "navigation_bar.preferences": "Preferências",
   "navigation_bar.public_timeline": "Global",
   "navigation_bar.security": "Segurança",
-  "notification.favourite": "{name} adicionou a sua postagem aos favoritos",
+  "notification.favourite": "{name} favoritou seu toot",
   "notification.follow": "{name} te seguiu",
+  "notification.follow_request": "{name} quer te seguir",
   "notification.mention": "{name} te mencionou",
-  "notification.poll": "Uma enquete em que você votou chegou ao fim",
-  "notification.reblog": "{name} compartilhou a sua postagem",
+  "notification.own_poll": "Sua enquete terminou",
+  "notification.poll": "Uma enquete que você votou terminou",
+  "notification.reblog": "{name} deu boost no seu toot",
   "notifications.clear": "Limpar notificações",
-  "notifications.clear_confirmation": "Você tem certeza de que quer limpar todas as suas notificações permanentemente?",
+  "notifications.clear_confirmation": "Você tem certeza de que quer limpar todas as suas notificações?",
   "notifications.column_settings.alert": "Notificações no computador",
   "notifications.column_settings.favourite": "Favoritos:",
   "notifications.column_settings.filter_bar.advanced": "Mostrar todas as categorias",
   "notifications.column_settings.filter_bar.category": "Barra de filtro rápido",
   "notifications.column_settings.filter_bar.show": "Mostrar",
-  "notifications.column_settings.follow": "Novos seguidores:",
+  "notifications.column_settings.follow": "Seguidores:",
+  "notifications.column_settings.follow_request": "Novos seguidores pendentes:",
   "notifications.column_settings.mention": "Menções:",
-  "notifications.column_settings.poll": "Resultados da enquete:",
+  "notifications.column_settings.poll": "Enquetes:",
   "notifications.column_settings.push": "Enviar notificações",
-  "notifications.column_settings.reblog": "Compartilhamento:",
+  "notifications.column_settings.reblog": "Boosts:",
   "notifications.column_settings.show": "Mostrar nas colunas",
   "notifications.column_settings.sound": "Reproduzir som",
   "notifications.filter.all": "Tudo",
-  "notifications.filter.boosts": "Compartilhamentos",
+  "notifications.filter.boosts": "Boosts",
   "notifications.filter.favourites": "Favoritos",
   "notifications.filter.follows": "Seguidores",
   "notifications.filter.mentions": "Menções",
-  "notifications.filter.polls": "Resultados da enquete",
+  "notifications.filter.polls": "Enquetes",
   "notifications.group": "{count} notificações",
-  "poll.closed": "Fechada",
+  "poll.closed": "Terminou",
   "poll.refresh": "Atualizar",
   "poll.total_people": "{count, plural, one {# pessoa} other {# pessoas}}",
   "poll.total_votes": "{count, plural, one {# voto} other {# votos}}",
   "poll.vote": "Votar",
-  "poll.voted": "Você votou nesta resposta",
-  "poll_button.add_poll": "Adicionar uma enquete",
+  "poll.voted": "Você votou nesta opção",
+  "poll_button.add_poll": "Adicionar enquete",
   "poll_button.remove_poll": "Remover enquete",
-  "privacy.change": "Ajustar a privacidade da mensagem",
-  "privacy.direct.long": "Apenas para usuários mencionados",
-  "privacy.direct.short": "Direta",
-  "privacy.private.long": "Apenas para seus seguidores",
-  "privacy.private.short": "Privada",
-  "privacy.public.long": "Publicar em todos os feeds",
-  "privacy.public.short": "Pública",
-  "privacy.unlisted.long": "Não publicar em feeds públicos",
-  "privacy.unlisted.short": "Não listada",
+  "privacy.change": "Ajustar privacidade do toot",
+  "privacy.direct.long": "Postar apenas para usuários mencionados",
+  "privacy.direct.short": "Direto",
+  "privacy.private.long": "Postar apenas para seguidores",
+  "privacy.private.short": "Privado",
+  "privacy.public.long": "Postar em linhas públicas",
+  "privacy.public.short": "Público",
+  "privacy.unlisted.long": "Não postar em linhas públicas",
+  "privacy.unlisted.short": "Não-listado",
   "refresh": "Atualizar",
   "regeneration_indicator.label": "Carregando…",
   "regeneration_indicator.sublabel": "Sua página inicial está sendo preparada!",
@@ -323,38 +334,39 @@
   "relative_time.minutes": "{number}m",
   "relative_time.seconds": "{number}s",
   "reply_indicator.cancel": "Cancelar",
-  "report.forward": "Encaminhar para {target}",
-  "report.forward_hint": "Essa conta pertence à um outro servidor. Encaminhar uma cópia da denúncia com seus dados tornados anônimos para esse servidor?",
-  "report.hint": "A sua denúncia será enviada aos moderadores da instância. Você pode adicionar uma explicação de porque você está denunciando essa conta abaixo:",
+  "report.forward": "Enviar cópia para {target}",
+  "report.forward_hint": "A conta está em outra instância. Enviar uma cópia anônima da denúncia para lá?",
+  "report.hint": "A denúncia será enviada aos moderadores da instância. Você pode explicar por que você denunciou a conta:",
   "report.placeholder": "Comentários adicionais",
   "report.submit": "Enviar",
-  "report.target": "Denunciar",
+  "report.target": "Denunciando {target}",
   "search.placeholder": "Pesquisar",
-  "search_popout.search_format": "Formato de busca avançado",
-  "search_popout.tips.full_text": "Texto simples retorna status que você escreveu, favoritou, compartilhou ou em que tenha sido mencionado; também retorna nomes de exibição, usuários e hashtags correspondentes.",
+  "search_popout.search_format": "Pesquisa avançada",
+  "search_popout.tips.full_text": "Texto simples retorna seus toots, favoritos, boosts ou toots em que foi mencionado, como também nomes de exibição, usuários e hashtags correspondentes.",
   "search_popout.tips.hashtag": "hashtag",
-  "search_popout.tips.status": "status",
+  "search_popout.tips.status": "toot",
   "search_popout.tips.text": "Texto simples retorna nomes de exibição, usuários e hashtags correspondentes",
   "search_popout.tips.user": "usuário",
   "search_results.accounts": "Pessoas",
   "search_results.hashtags": "Hashtags",
   "search_results.statuses": "Toots",
-  "search_results.statuses_fts_disabled": "Pesquisar toots por seu conteúdo não está habilitado neste servidor Mastodon.",
+  "search_results.statuses_fts_disabled": "Pesquisar toots por seu conteúdo não está ativado nesta instância Mastodon.",
   "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}",
   "status.admin_account": "Abrir interface de moderação para @{name}",
-  "status.admin_status": "Abrir esse status na interface de moderação",
+  "status.admin_status": "Abrir o toot na interface de moderação",
   "status.block": "Bloquear @{name}",
-  "status.cancel_reblog_private": "Desfazer compartilhamento",
-  "status.cannot_reblog": "Esta postagem não pode ser compartilhada",
-  "status.copy": "Copiar o link para o status",
+  "status.bookmark": "Salvar",
+  "status.cancel_reblog_private": "Desfazer boost",
+  "status.cannot_reblog": "Não foi possível dar boost",
+  "status.copy": "Copiar o link do toot",
   "status.delete": "Excluir",
   "status.detailed_status": "Visão detalhada da conversa",
-  "status.direct": "Enviar mensagem direta a @{name}",
+  "status.direct": "Toot direto para @{name}",
   "status.embed": "Incorporar",
-  "status.favourite": "Adicionar aos favoritos",
+  "status.favourite": "Favoritar",
   "status.filtered": "Filtrado",
-  "status.load_more": "Carregar mais",
-  "status.media_hidden": "Mídia escondida",
+  "status.load_more": "Ver mais",
+  "status.media_hidden": "Mídia oculta",
   "status.mention": "Mencionar @{name}",
   "status.more": "Mais",
   "status.mute": "Silenciar @{name}",
@@ -363,61 +375,65 @@
   "status.pin": "Fixar no perfil",
   "status.pinned": "Toot fixado",
   "status.read_more": "Ler mais",
-  "status.reblog": "Compartilhar",
-  "status.reblog_private": "Compartilhar com a audiência original",
-  "status.reblogged_by": "{name} compartilhou",
-  "status.reblogs.empty": "Ninguém compartilhou esse toot até agora. Quando alguém o fizer, eles aparecerão aqui.",
-  "status.redraft": "Apagar & usar como rascunho",
+  "status.reblog": "Dar boost",
+  "status.reblog_private": "Dar boost para audiência original",
+  "status.reblogged_by": "{name} deu boost",
+  "status.reblogs.empty": "Nada aqui. Quando alguém der boost, o autor aparecerá aqui.",
+  "status.redraft": "Excluir e rascunhar",
+  "status.remove_bookmark": "Remover dos Salvos",
   "status.reply": "Responder",
-  "status.replyAll": "Responder à sequência",
+  "status.replyAll": "Responder à conversa",
   "status.report": "Denunciar @{name}",
   "status.sensitive_warning": "Conteúdo sensível",
   "status.share": "Compartilhar",
   "status.show_less": "Mostrar menos",
-  "status.show_less_all": "Mostrar menos para todas as mensagens",
+  "status.show_less_all": "Mostrar menos para todos os toots",
   "status.show_more": "Mostrar mais",
-  "status.show_more_all": "Mostrar mais para todas as mensagens",
-  "status.show_thread": "Mostrar sequência",
+  "status.show_more_all": "Mostrar mais para todos os toots",
+  "status.show_thread": "Mostrar conversa",
   "status.uncached_media_warning": "Não disponível",
   "status.unmute_conversation": "Desativar silêncio desta conversa",
   "status.unpin": "Desafixar do perfil",
-  "suggestions.dismiss": "Ignorar a sugestão",
+  "suggestions.dismiss": "Ignorar sugestão",
   "suggestions.header": "Você pode se interessar por…",
   "tabs_bar.federated_timeline": "Global",
   "tabs_bar.home": "Página inicial",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notificações",
-  "tabs_bar.search": "Buscar",
+  "tabs_bar.search": "Pesquisar",
   "time_remaining.days": "{number, plural, one {# dia restante} other {# dias restantes}}",
   "time_remaining.hours": "{number, plural, one {# hora restante} other {# horas restantes}}",
   "time_remaining.minutes": "{number, plural, one {# minuto restante} other {# minutos restantes}}",
-  "time_remaining.moments": "Momentos restantes",
+  "time_remaining.moments": "Tempo restante",
   "time_remaining.seconds": "{number, plural, one {# segundo restante} other {# segundos restantes}}",
   "trends.count_by_accounts": "{count} {rawCount, plural, one {pessoa} other {pessoas}} falando sobre",
   "trends.trending_now": "Em alta no momento",
   "ui.beforeunload": "Seu rascunho será perdido se você sair do Mastodon.",
   "upload_area.title": "Arraste e solte para enviar",
-  "upload_button.label": "Adicionar mídia (JPEG, PNG, GIF, WebM, MP4, MOV)",
+  "upload_button.label": "Adicionar mídia ({formats})",
   "upload_error.limit": "Limite de envio de arquivos excedido.",
-  "upload_error.poll": "Envio de arquivos não é permitido com enquetes.",
-  "upload_form.description": "Descreva a imagem para deficientes visuais",
+  "upload_error.poll": "Não é possível enviar arquivos com enquetes.",
+  "upload_form.audio_description": "Descrever para deficientes auditivos",
+  "upload_form.description": "Descrever para deficientes visuais",
   "upload_form.edit": "Editar",
   "upload_form.undo": "Remover",
+  "upload_form.video_description": "Descrever para deficientes auditivos e visuais",
   "upload_modal.analyzing_picture": "Analisando imagem…",
   "upload_modal.apply": "Aplicar",
-  "upload_modal.description_placeholder": "Grave e cabisbaixo, o filho justo zelava pela querida mãe doente",
+  "upload_modal.description_placeholder": "Um pequeno jabuti xereta viu dez cegonhas felizes",
   "upload_modal.detect_text": "Detectar texto da imagem",
   "upload_modal.edit_media": "Editar mídia",
-  "upload_modal.hint": "Clique ou arraste o círculo na visualização para escolher o ponto focal que sempre será visto em todas as miniaturas.",
+  "upload_modal.hint": "Clique ou arraste o círculo na prévia para escolher o ponto focal que ficará visível em todas as miniaturas.",
   "upload_modal.preview_label": "Prévia ({ratio})",
-  "upload_progress.label": "Salvando...",
+  "upload_progress.label": "Enviando...",
   "video.close": "Fechar vídeo",
+  "video.download": "Baixar arquivo",
   "video.exit_fullscreen": "Sair da tela cheia",
   "video.expand": "Expandir vídeo",
   "video.fullscreen": "Tela cheia",
-  "video.hide": "Esconder vídeo",
+  "video.hide": "Ocultar vídeo",
   "video.mute": "Silenciar",
-  "video.pause": "Parar",
+  "video.pause": "Pausar",
   "video.play": "Reproduzir",
-  "video.unmute": "Retirar silêncio"
+  "video.unmute": "Desativar silêncio"
 }
diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json
index bb8a94516..b2fb2a012 100644
--- a/app/javascript/mastodon/locales/pt-PT.json
+++ b/app/javascript/mastodon/locales/pt-PT.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Adicionar ou remover das listas",
   "account.badges.bot": "Robô",
+  "account.badges.group": "Grupo",
   "account.block": "Bloquear @{name}",
   "account.block_domain": "Esconder tudo do domínio {domain}",
   "account.blocked": "Bloqueado",
@@ -39,9 +40,10 @@
   "account.unmute": "Não silenciar @{name}",
   "account.unmute_notifications": "Deixar de silenciar @{name}",
   "alert.rate_limited.message": "Volte a tentar depois das {retry_time, time, medium}.",
-  "alert.rate_limited.title": "Rate limited",
+  "alert.rate_limited.title": "Limite de tentativas",
   "alert.unexpected.message": "Ocorreu um erro inesperado.",
   "alert.unexpected.title": "Bolas!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} por semana",
   "boost_modal.combo": "Pode clicar {combo} para não voltar a ver",
   "bundle_column_error.body": "Algo de errado aconteceu enquanto este componente era carregado.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Algo de errado aconteceu enquanto este componente era carregado.",
   "bundle_modal_error.retry": "Tente de novo",
   "column.blocks": "Utilizadores Bloqueados",
+  "column.bookmarks": "Favoritos",
   "column.community": "Cronologia local",
   "column.direct": "Mensagens directas",
   "column.directory": "Procurar perfis",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Duração da votação",
   "compose_form.poll.option_placeholder": "Opção {number}",
   "compose_form.poll.remove_option": "Eliminar esta opção",
+  "compose_form.poll.switch_to_multiple": "Alterar a votação para permitir múltiplas escolhas",
+  "compose_form.poll.switch_to_single": "Alterar a votação para permitir uma única escolha",
   "compose_form.publish": "Toot",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Marcar multimédia como sensível",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Sem toots 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 assinalou toots como favoritos. Quando o fizer, eles aparecerão aqui.",
   "empty_column.community": "A timeline local está vazia. Escreve algo publicamente para começar!",
   "empty_column.direct": "Ainda não tens qualquer mensagem directa. Quando enviares ou receberes alguma, ela irá aparecer aqui.",
   "empty_column.domain_blocks": "Ainda não há qualquer domínio escondido.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "para abrir a lista dos utilizadores silenciados",
   "keyboard_shortcuts.my_profile": "para abrir o teu perfil",
   "keyboard_shortcuts.notifications": "para abrir a coluna das notificações",
+  "keyboard_shortcuts.open_media": "para abrir média",
   "keyboard_shortcuts.pinned": "para abrir a lista dos toots fixados",
   "keyboard_shortcuts.profile": "para abrir o perfil do autor",
   "keyboard_shortcuts.reply": "para responder",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Esconder notificações deste utilizador?",
   "navigation_bar.apps": "Aplicações móveis",
   "navigation_bar.blocks": "Utilizadores bloqueados",
+  "navigation_bar.bookmarks": "Favoritos",
   "navigation_bar.community_timeline": "Cronologia local",
   "navigation_bar.compose": "Escrever novo toot",
   "navigation_bar.direct": "Mensagens directas",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Segurança",
   "notification.favourite": "{name} adicionou o teu estado aos favoritos",
   "notification.follow": "{name} começou a seguir-te",
+  "notification.follow_request": "{name} pediu para segui-lo",
   "notification.mention": "{name} mencionou-te",
+  "notification.own_poll": "A sua votação terminou",
   "notification.poll": "Uma votação em participaste chegou ao fim",
   "notification.reblog": "{name} fez boost ao teu o teu estado",
   "notifications.clear": "Limpar notificações",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Barra de filtros rápidos",
   "notifications.column_settings.filter_bar.show": "Mostrar",
   "notifications.column_settings.follow": "Novos seguidores:",
+  "notifications.column_settings.follow_request": "Novos pedidos de seguidor:",
   "notifications.column_settings.mention": "Menções:",
   "notifications.column_settings.poll": "Resultados da votação:",
   "notifications.column_settings.push": "Notificações Push",
@@ -344,6 +355,7 @@
   "status.admin_account": "Abrir a interface de moderação para @{name}",
   "status.admin_status": "Abrir esta publicação na interface de moderação",
   "status.block": "Bloquear @{name}",
+  "status.bookmark": "Favorito",
   "status.cancel_reblog_private": "Remover boost",
   "status.cannot_reblog": "Não é possível fazer boost a esta publicação",
   "status.copy": "Copiar o link para a publicação",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} fez boost",
   "status.reblogs.empty": "Ainda ninguém fez boost a este toot. Quando alguém o fizer, ele irá aparecer aqui.",
   "status.redraft": "Apagar & reescrever",
+  "status.remove_bookmark": "Remover favorito",
   "status.reply": "Responder",
   "status.replyAll": "Responder à conversa",
   "status.report": "Denunciar @{name}",
@@ -394,15 +407,17 @@
   "time_remaining.moments": "Momentos restantes",
   "time_remaining.seconds": "{número, plural, um {# second} outro {# seconds}} faltam",
   "trends.count_by_accounts": "{count} {rawCount, plural, uma {person} outra {people}} a falar",
-  "trends.trending_now": "Tendencias actuais",
+  "trends.trending_now": "Tendências atuais",
   "ui.beforeunload": "O teu rascunho será perdido se abandonares o Mastodon.",
   "upload_area.title": "Arraste e solte para enviar",
   "upload_button.label": "Adicionar media",
   "upload_error.limit": "Limite máximo do ficheiro a carregar excedido.",
   "upload_error.poll": "Carregamento de ficheiros não é permitido em votações.",
+  "upload_form.audio_description": "Descreva para pessoas com diminuição da acuidade auditiva",
   "upload_form.description": "Descrição da imagem para pessoas com dificuldades visuais",
   "upload_form.edit": "Editar",
   "upload_form.undo": "Apagar",
+  "upload_form.video_description": "Descreva para pessoas com diminuição da acuidade auditiva ou visual",
   "upload_modal.analyzing_picture": "A analizar imagem…",
   "upload_modal.apply": "Aplicar",
   "upload_modal.description_placeholder": "Grave e cabisbaixo, o filho justo zelava pela querida mãe doente",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Pré-visualizar ({ratio})",
   "upload_progress.label": "A enviar...",
   "video.close": "Fechar vídeo",
+  "video.download": "Descarregar ficheiro",
   "video.exit_fullscreen": "Sair de full screen",
   "video.expand": "Expandir vídeo",
   "video.fullscreen": "Ecrã completo",
diff --git a/app/javascript/mastodon/locales/ro.json b/app/javascript/mastodon/locales/ro.json
index 617dbcc0d..4d01ad3a5 100644
--- a/app/javascript/mastodon/locales/ro.json
+++ b/app/javascript/mastodon/locales/ro.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Adaugă sau Elimină din liste",
   "account.badges.bot": "Bot",
+  "account.badges.group": "Group",
   "account.block": "Blochează @{name}",
   "account.block_domain": "Ascunde tot de la {domain}",
   "account.blocked": "Blocat",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Rate limited",
   "alert.unexpected.message": "A apărut o eroare neașteptată.",
   "alert.unexpected.title": "Hopa!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} per week",
   "boost_modal.combo": "Poți apăsa {combo} pentru a omite asta data viitoare",
   "bundle_column_error.body": "Ceva nu a funcționat la încărcarea acestui component.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Ceva nu a funcționat în timupul încărcării acestui component.",
   "bundle_modal_error.retry": "Încearcă din nou",
   "column.blocks": "Utilizatori blocați",
+  "column.bookmarks": "Bookmarks",
   "column.community": "Fluxul Local",
   "column.direct": "Mesaje directe",
   "column.directory": "Browse profiles",
@@ -82,6 +85,8 @@
   "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": "Postează",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Mark media as sensitive",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Nici o postare aici!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "Nu ai blocat nici un utilizator incă.",
+  "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "Fluxul local este gol. Scrie ceva public pentru a împinge bila la vale!",
   "empty_column.direct": "Nu ai nici un mesaj direct incă. Când trimiți sau primești unul, va fi afișat aici.",
   "empty_column.domain_blocks": "Nu sunt domenii ascunse incă.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "să deschidă lista utilizatorilor opriți",
   "keyboard_shortcuts.my_profile": "să deschidă profilul tău",
   "keyboard_shortcuts.notifications": "să deschidă coloana cu notificări",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "să deschidă lista postărilor fixate",
   "keyboard_shortcuts.profile": "să deschidă porfilul autorului",
   "keyboard_shortcuts.reply": "să răspundă",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Ascunzi notificările de la acest utilizator?",
   "navigation_bar.apps": "Aplicații mobile",
   "navigation_bar.blocks": "Utilizatori blocați",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Flux local",
   "navigation_bar.compose": "Compune o nouă postare",
   "navigation_bar.direct": "Mesaje directe",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Securitate",
   "notification.favourite": "{name} a adăugat statusul tău la favorite",
   "notification.follow": "{name} te urmărește",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} te-a menționat",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "A poll you have voted in has ended",
   "notification.reblog": "{name} a redistribuit postarea ta",
   "notifications.clear": "Șterge notificările",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Bară de filtrare rapidă",
   "notifications.column_settings.filter_bar.show": "Arată",
   "notifications.column_settings.follow": "Noi urmăritori:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "Mențiuni:",
   "notifications.column_settings.poll": "Poll results:",
   "notifications.column_settings.push": "Notificări push",
@@ -344,6 +355,7 @@
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_status": "Open this status in the moderation interface",
   "status.block": "Blochează @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Nedistribuit",
   "status.cannot_reblog": "Această postare nu poate fi redistribuită",
   "status.copy": "Copy link to status",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} a redistribuit",
   "status.reblogs.empty": "Nimeni nu a redistribuit această postare până acum. Când cineva o va face, va apărea aici.",
   "status.redraft": "Șterge și adaugă la ciorne",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "Răspunde",
   "status.replyAll": "Răspunde la topic",
   "status.report": "Raportează @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Adaugă media (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "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": "Adaugă o descriere pentru persoanele cu deficiențe de vedere",
   "upload_form.edit": "Edit",
   "upload_form.undo": "Șterge",
+  "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.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "Se Încarcă...",
   "video.close": "Închide video",
+  "video.download": "Download file",
   "video.exit_fullscreen": "Închide",
   "video.expand": "Extinde video",
   "video.fullscreen": "Ecran întreg",
diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json
index c8767e535..6e63a418c 100644
--- a/app/javascript/mastodon/locales/ru.json
+++ b/app/javascript/mastodon/locales/ru.json
@@ -1,8 +1,9 @@
 {
-  "account.add_or_remove_from_list": "Добавить или удалить из списков",
+  "account.add_or_remove_from_list": "Управление списками",
   "account.badges.bot": "Бот",
-  "account.block": "Блокировать",
-  "account.block_domain": "Блокировать все с {domain}",
+  "account.badges.group": "Группа",
+  "account.block": "Заблокировать @{name}",
+  "account.block_domain": "Заблокировать {domain}",
   "account.blocked": "Заблокирован(а)",
   "account.cancel_follow_request": "Отменить запрос",
   "account.direct": "Написать @{name}",
@@ -15,19 +16,19 @@
   "account.follows": "Подписки",
   "account.follows.empty": "Этот пользователь пока ни на кого не подписался.",
   "account.follows_you": "Подписан(а) на вас",
-  "account.hide_reblogs": "Скрыть реблоги от @{name}",
+  "account.hide_reblogs": "Скрыть продвижения от @{name}",
   "account.last_status": "Последняя активность",
   "account.link_verified_on": "Владение этой ссылкой было проверено {date}",
   "account.locked_info": "Это закрытый аккаунт. Его владелец вручную одобряет подписчиков.",
   "account.media": "Медиа",
-  "account.mention": "Упомянуть",
+  "account.mention": "Упомянуть @{name}",
   "account.moved_to": "Ищите {name} здесь:",
-  "account.mute": "Заглушить",
+  "account.mute": "Игнорировать @{name}",
   "account.mute_notifications": "Скрыть уведомления от @{name}",
-  "account.muted": "Приглушён",
+  "account.muted": "Игнорируется",
   "account.never_active": "Никогда",
   "account.posts": "Посты",
-  "account.posts_with_replies": "Посты с ответами",
+  "account.posts_with_replies": "Посты и ответы",
   "account.report": "Пожаловаться",
   "account.requested": "Ожидает подтверждения. Нажмите для отмены",
   "account.share": "Поделиться профилем @{name}",
@@ -36,30 +37,32 @@
   "account.unblock_domain": "Разблокировать {domain}",
   "account.unendorse": "Не рекомендовать в профиле",
   "account.unfollow": "Отписаться",
-  "account.unmute": "Не скрывать @{name}",
+  "account.unmute": "Не игнорировать @{name}",
   "account.unmute_notifications": "Показывать уведомления от @{name}",
-  "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": "Объявление",
   "autosuggest_hashtag.per_week": "{count} / неделю",
-  "boost_modal.combo": "Нажмите {combo}, чтобы пропустить это в следующий раз",
+  "boost_modal.combo": "{combo}, чтобы пропустить это в следующий раз",
   "bundle_column_error.body": "Что-то пошло не так при загрузке этого компонента.",
   "bundle_column_error.retry": "Попробовать снова",
   "bundle_column_error.title": "Ошибка сети",
   "bundle_modal_error.close": "Закрыть",
   "bundle_modal_error.message": "Что-то пошло не так при загрузке этого компонента.",
   "bundle_modal_error.retry": "Попробовать снова",
-  "column.blocks": "Список блокировки",
+  "column.blocks": "Заблокированные пользователи",
+  "column.bookmarks": "Закладки",
   "column.community": "Локальная лента",
   "column.direct": "Личные сообщения",
   "column.directory": "Просмотр профилей",
   "column.domain_blocks": "Скрытые домены",
-  "column.favourites": "Понравившееся",
+  "column.favourites": "Избранное",
   "column.follow_requests": "Запросы на подписку",
   "column.home": "Главная",
   "column.lists": "Списки",
-  "column.mutes": "Список скрытых пользователей",
+  "column.mutes": "Игнорируемые пользователи",
   "column.notifications": "Уведомления",
   "column.pins": "Закреплённый пост",
   "column.public": "Глобальная лента",
@@ -72,21 +75,23 @@
   "column_header.unpin": "Открепить",
   "column_subheading.settings": "Настройки",
   "community.column_settings.media_only": "Только с медиа",
-  "compose_form.direct_message_warning": "Этот статус будет виден только упомянутым пользователям.",
-  "compose_form.direct_message_warning_learn_more": "Узнать больше",
-  "compose_form.hashtag_warning": "Этот пост не будет показывается в поиске по хэштегу, т.к. он непубличный. Только публичные посты можно найти в поиске по хэштегу.",
-  "compose_form.lock_disclaimer": "Ваша учётная запись не {locked}. Любой пользователь может подписаться на вас и просматривать посты для подписчиков.",
-  "compose_form.lock_disclaimer.lock": "закрыт",
-  "compose_form.placeholder": "О чем вы думаете?",
+  "compose_form.direct_message_warning": "Адресованные посты отправляются и видны только упомянутым в них пользователям.",
+  "compose_form.direct_message_warning_learn_more": "Узнать подробнее",
+  "compose_form.hashtag_warning": "Так как этот пост не публичный, он не отобразится в поиске по хэштегам.",
+  "compose_form.lock_disclaimer": "Ваша учётная запись {locked}. Любой пользователь сможет подписаться на вас и просматривать посты для подписчиков.",
+  "compose_form.lock_disclaimer.lock": "не закрыта",
+  "compose_form.placeholder": "О чём думаете?",
   "compose_form.poll.add_option": "Добавить вариант",
   "compose_form.poll.duration": "Продолжительность опроса",
   "compose_form.poll.option_placeholder": "Вариант {number}",
-  "compose_form.poll.remove_option": "Удалить этот вариант",
+  "compose_form.poll.remove_option": "Убрать этот вариант",
+  "compose_form.poll.switch_to_multiple": "Переключить в режим выбора нескольких ответов",
+  "compose_form.poll.switch_to_single": "Переключить в режим выбора одного ответа",
   "compose_form.publish": "Запостить",
   "compose_form.publish_loud": "{publish}!",
-  "compose_form.sensitive.hide": "Пометить медиафайл как чувствительный",
-  "compose_form.sensitive.marked": "Медиафайлы не отмечены как чувствительные",
-  "compose_form.sensitive.unmarked": "Медиафайлы не отмечены как чувствительные",
+  "compose_form.sensitive.hide": "Отметить медиафайл как деликатный",
+  "compose_form.sensitive.marked": "Медиафайл отмечен как деликатный",
+  "compose_form.sensitive.unmarked": "Медиафайл не отмечен как деликатный",
   "compose_form.spoiler.marked": "Текст скрыт за предупреждением",
   "compose_form.spoiler.unmarked": "Текст не скрыт",
   "compose_form.spoiler_placeholder": "Текст предупреждения",
@@ -95,20 +100,20 @@
   "confirmations.block.confirm": "Заблокировать",
   "confirmations.block.message": "Вы уверены, что хотите заблокировать {name}?",
   "confirmations.delete.confirm": "Удалить",
-  "confirmations.delete.message": "Вы уверены, что хотите удалить этот статус?",
+  "confirmations.delete.message": "Вы уверены, что хотите удалить этот пост?",
   "confirmations.delete_list.confirm": "Удалить",
   "confirmations.delete_list.message": "Вы действительно хотите навсегда удалить этот список?",
-  "confirmations.domain_block.confirm": "Блокировать весь домен",
-  "confirmations.domain_block.message": "Вы на самом деле уверены, что хотите блокировать весь {domain}? В большинстве случаев нескольких отдельных блокировок или глушений достаточно.",
+  "confirmations.domain_block.confirm": "Да, заблокировать узел",
+  "confirmations.domain_block.message": "Вы точно уверены, что хотите скрыть все посты с узла {domain}? В большинстве случаев пары блокировок и скрытий вполне достаточно.\n\nПри блокировке узла, вы перестанете получать уведомления оттуда, все посты будут скрыты из публичных лент, а подписчики убраны.",
   "confirmations.logout.confirm": "Выйти",
   "confirmations.logout.message": "Вы уверены, что хотите выйти?",
-  "confirmations.mute.confirm": "Заглушить",
+  "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": "Удалить беседу",
@@ -116,13 +121,13 @@
   "conversation.open": "Просмотр беседы",
   "conversation.with": "С {names}",
   "directory.federated": "Со всей федерации",
-  "directory.local": "Только из {domain}",
+  "directory.local": "Только с {domain}",
   "directory.new_arrivals": "Новички",
   "directory.recently_active": "Недавно активные",
   "embed.instructions": "Встройте этот пост на свой сайт, скопировав следующий код:",
   "embed.preview": "Так это будет выглядеть:",
   "emoji_button.activity": "Занятия",
-  "emoji_button.custom": "Особенные",
+  "emoji_button.custom": "С этого узла",
   "emoji_button.flags": "Флаги",
   "emoji_button.food": "Еда и напитки",
   "emoji_button.label": "Вставить эмодзи",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Здесь нет постов!",
   "empty_column.account_unavailable": "Профиль недоступен",
   "empty_column.blocks": "Вы ещё никого не заблокировали.",
+  "empty_column.bookmarked_statuses": "У вас пока нет постов в закладках. Как добавите один, он отобразится здесь.",
   "empty_column.community": "Локальная лента пуста. Напишите что-нибудь, чтобы разогреть народ!",
   "empty_column.direct": "У вас пока нет личных сообщений. Как только вы отправите или получите одно, оно появится здесь.",
   "empty_column.domain_blocks": "Скрытых доменов пока нет.",
@@ -149,7 +155,7 @@
   "empty_column.home.public_timeline": "публичные ленты",
   "empty_column.list": "В этом списке пока ничего нет.",
   "empty_column.lists": "У вас ещё нет списков. Созданные вами списки будут показаны здесь.",
-  "empty_column.mutes": "Вы ещё никого не скрывали.",
+  "empty_column.mutes": "Вы никого не игнорируете и всех внимательно выслушиваете.",
   "empty_column.notifications": "У вас пока нет уведомлений. Взаимодействуйте с другими, чтобы завести разговор.",
   "empty_column.public": "Здесь ничего нет! Опубликуйте что-нибудь или подпишитесь на пользователей с других узлов, чтобы заполнить ленту",
   "error.unexpected_crash.explanation": "Из-за несовместимого браузера или ошибки в нашем коде, эта страница не может быть корректно отображена.",
@@ -183,16 +189,16 @@
   "intervals.full.minutes": "{number, plural, one {# минута} few {# минуты} other {# минут}}",
   "introduction.federation.action": "Далее",
   "introduction.federation.federated.headline": "Глобальная лента",
-  "introduction.federation.federated.text": "Публичные статусы с других серверов федеративной сети расположатся в глобальной ленте.",
+  "introduction.federation.federated.text": "Публичные посты с других серверов федеративной сети расположатся в глобальной ленте.",
   "introduction.federation.home.headline": "Домашняя лента",
   "introduction.federation.home.text": "Посты людей, на которых вы подписаны, будут отображаться в домашней ленте. Подписываться можно на кого угодно, независимо от узла.",
   "introduction.federation.local.headline": "Локальная лента",
-  "introduction.federation.local.text": "Публичные статусы от людей с того же сервера, что и вы, будут отображены в локальной ленте.",
+  "introduction.federation.local.text": "Публичные посты от людей с того же сервера, что и вы, будут отображены в локальной ленте.",
   "introduction.interactions.action": "Завершить обучение",
   "introduction.interactions.favourite.headline": "Отметки «нравится»",
-  "introduction.interactions.favourite.text": "Вы можете отметить статус, чтобы вернуться к нему позже и дать знать автору, что запись вам понравилась, поставив отметку \"нравится\".",
+  "introduction.interactions.favourite.text": "Дайте автору знать, что пост вам понравился и вернитесь к нему позже, добавив его в избранное.",
   "introduction.interactions.reblog.headline": "Продвижения",
-  "introduction.interactions.reblog.text": "Вы можете делиться статусами других людей, продвигая их в своей учётной записи.",
+  "introduction.interactions.reblog.text": "Вы можете делиться постами других людей, продвигая их в своей учётной записи.",
   "introduction.interactions.reply.headline": "Ответы",
   "introduction.interactions.reply.text": "Вы можете отвечать свои и чужие посты, образуя цепочки сообщений (обсуждения).",
   "introduction.welcome.action": "Поехали!",
@@ -206,9 +212,9 @@
   "keyboard_shortcuts.description": "Описание",
   "keyboard_shortcuts.direct": "чтобы показать колонку личных сообщений",
   "keyboard_shortcuts.down": "вниз по списку",
-  "keyboard_shortcuts.enter": "развернуть пост",
+  "keyboard_shortcuts.enter": "открыть пост",
   "keyboard_shortcuts.favourite": "в избранное",
-  "keyboard_shortcuts.favourites": "открыть 'Избранное'",
+  "keyboard_shortcuts.favourites": "открыть «Избранное»",
   "keyboard_shortcuts.federated": "перейти к глобальной ленте",
   "keyboard_shortcuts.heading": "Сочетания клавиш",
   "keyboard_shortcuts.home": "перейти к домашней ленте",
@@ -216,10 +222,11 @@
   "keyboard_shortcuts.legend": "показать это окно",
   "keyboard_shortcuts.local": "перейти к локальной ленте",
   "keyboard_shortcuts.mention": "упомянуть автора поста",
-  "keyboard_shortcuts.muted": "открыть список заглушённых",
+  "keyboard_shortcuts.muted": "открыть список игнорируемых",
   "keyboard_shortcuts.my_profile": "перейти к своему профилю",
   "keyboard_shortcuts.notifications": "перейти к уведомлениям",
-  "keyboard_shortcuts.pinned": "перейти к закреплённым статусам",
+  "keyboard_shortcuts.open_media": "открыть вложение",
+  "keyboard_shortcuts.pinned": "перейти к закреплённым постам",
   "keyboard_shortcuts.profile": "перейти к профилю автора",
   "keyboard_shortcuts.reply": "ответить",
   "keyboard_shortcuts.requests": "перейти к запросам на подписку",
@@ -239,8 +246,8 @@
   "lists.delete": "Удалить список",
   "lists.edit": "Изменить список",
   "lists.edit.submit": "Изменить название",
-  "lists.new.create": "Новый список",
-  "lists.new.title_placeholder": "Заголовок списка",
+  "lists.new.create": "Создать список",
+  "lists.new.title_placeholder": "Название для нового списка",
   "lists.search": "Искать среди подписок",
   "lists.subheading": "Ваши списки",
   "load_pending": "{count, plural, one {# новый элемент} few {# новых элемента} other {# новых элементов}}",
@@ -248,17 +255,18 @@
   "media_gallery.toggle_visible": "Показать/скрыть",
   "missing_indicator.label": "Не найдено",
   "missing_indicator.sublabel": "Запрашиваемый ресурс не найден",
-  "mute_modal.hide_notifications": "Убрать уведомления от этого пользователя?",
+  "mute_modal.hide_notifications": "Скрыть уведомления от этого пользователя?",
   "navigation_bar.apps": "Мобильные приложения",
   "navigation_bar.blocks": "Список блокировки",
+  "navigation_bar.bookmarks": "Закладки",
   "navigation_bar.community_timeline": "Локальная лента",
-  "navigation_bar.compose": "Создать новый статус",
+  "navigation_bar.compose": "Создать новый пост",
   "navigation_bar.direct": "Личные сообщения",
   "navigation_bar.discover": "Изучайте",
   "navigation_bar.domain_blocks": "Скрытые домены",
   "navigation_bar.edit_profile": "Изменить профиль",
-  "navigation_bar.favourites": "Понравившееся",
-  "navigation_bar.filters": "Заглушенные слова",
+  "navigation_bar.favourites": "Избранное",
+  "navigation_bar.filters": "Игнорируемые слова",
   "navigation_bar.follow_requests": "Запросы на подписку",
   "navigation_bar.follows_and_followers": "Подписки и подписчики",
   "navigation_bar.info": "Об узле",
@@ -271,29 +279,32 @@
   "navigation_bar.preferences": "Настройки",
   "navigation_bar.public_timeline": "Глобальная лента",
   "navigation_bar.security": "Безопасность",
-  "notification.favourite": "{name} добавил(а) ваш статус в избранное",
+  "notification.favourite": "{name} добавил(а) ваш пост в избранное",
   "notification.follow": "{name} подписался (-лась) на вас",
+  "notification.follow_request": "{name} отправил запрос на подписку",
   "notification.mention": "{name} упомянул(а) вас",
+  "notification.own_poll": "Ваш опрос закончился",
   "notification.poll": "Опрос, в котором вы приняли участие, завершился",
-  "notification.reblog": "{name} продвинул(а) ваш статус",
+  "notification.reblog": "{name} продвинул(а) ваш пост",
   "notifications.clear": "Очистить уведомления",
   "notifications.clear_confirmation": "Вы уверены, что хотите очистить все уведомления?",
-  "notifications.column_settings.alert": "Десктопные уведомления",
-  "notifications.column_settings.favourite": "Нравится:",
+  "notifications.column_settings.alert": "Уведомления в фоне",
+  "notifications.column_settings.favourite": "Ваш пост добавили в «избранное»:",
   "notifications.column_settings.filter_bar.advanced": "Отображать все категории",
-  "notifications.column_settings.filter_bar.category": "Фильтры по категориям",
-  "notifications.column_settings.filter_bar.show": "Показывать",
-  "notifications.column_settings.follow": "Новые подписчики:",
-  "notifications.column_settings.mention": "Упоминания:",
-  "notifications.column_settings.poll": "Результаты опроса:",
-  "notifications.column_settings.push": "Push-уведомления",
-  "notifications.column_settings.reblog": "Продвижения:",
-  "notifications.column_settings.show": "Показывать в колонке",
+  "notifications.column_settings.filter_bar.category": "Панель сортировки",
+  "notifications.column_settings.filter_bar.show": "Отображать панель сортировки",
+  "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.filter.all": "Все",
+  "notifications.filter.all": "Всё",
   "notifications.filter.boosts": "Продвижения",
-  "notifications.filter.favourites": "Отметки \"нравится\"",
-  "notifications.filter.follows": "Новые подписчики",
+  "notifications.filter.favourites": "Отметки «избранного»",
+  "notifications.filter.follows": "Подписки",
   "notifications.filter.mentions": "Упоминания",
   "notifications.filter.polls": "Результаты опросов",
   "notifications.group": "{count} уведомл.",
@@ -305,26 +316,26 @@
   "poll.voted": "Вы проголосовали за этот вариант",
   "poll_button.add_poll": "Добавить опрос",
   "poll_button.remove_poll": "Удалить опрос",
-  "privacy.change": "Изменить видимость статуса",
+  "privacy.change": "Изменить видимость поста",
   "privacy.direct.long": "Показать только упомянутым",
-  "privacy.direct.short": "Направленный",
+  "privacy.direct.short": "Адресованный",
   "privacy.private.long": "Показать только подписчикам",
   "privacy.private.short": "Приватный",
   "privacy.public.long": "Показать в публичных лентах",
   "privacy.public.short": "Публичный",
-  "privacy.unlisted.long": "Не показывать в лентах",
+  "privacy.unlisted.long": "Не показывать в публичных лентах",
   "privacy.unlisted.short": "Скрытый",
   "refresh": "Обновить",
   "regeneration_indicator.label": "Загрузка…",
   "regeneration_indicator.sublabel": "Один момент, мы подготавливаем вашу ленту!",
-  "relative_time.days": "{number}д",
-  "relative_time.hours": "{number}ч",
+  "relative_time.days": "{number} д",
+  "relative_time.hours": "{number} ч",
   "relative_time.just_now": "только что",
-  "relative_time.minutes": "{number}м",
-  "relative_time.seconds": "{number}с",
+  "relative_time.minutes": "{number} мин",
+  "relative_time.seconds": "{number} с",
   "reply_indicator.cancel": "Отмена",
   "report.forward": "Переслать в {target}",
-  "report.forward_hint": "Этот аккаунт расположен на другом узле. Отправить туда анонимную копию вашей жалобы?",
+  "report.forward_hint": "Эта учётная запись расположена на другом узле. Отправить туда анонимную копию вашей жалобы?",
   "report.hint": "Жалоба будет отправлена модераторам вашего узла. Вы также можете указать подробную причину жалобы ниже:",
   "report.placeholder": "Комментарий",
   "report.submit": "Отправить",
@@ -333,53 +344,55 @@
   "search_popout.search_format": "Продвинутый формат поиска",
   "search_popout.tips.full_text": "Поиск по простому тексту отобразит посты, которые вы написали, добавили в избранное, продвинули или в которых были упомянуты, а также подходящие имена пользователей и хэштеги.",
   "search_popout.tips.hashtag": "хэштег",
-  "search_popout.tips.status": "статус",
+  "search_popout.tips.status": "пост",
   "search_popout.tips.text": "Простой ввод текста покажет совпадающие имена пользователей, отображаемые имена и хэштеги",
   "search_popout.tips.user": "пользователь",
   "search_results.accounts": "Люди",
   "search_results.hashtags": "Хэштеги",
   "search_results.statuses": "Посты",
-  "search_results.statuses_fts_disabled": "Поиск постов по их контенту не поддерживается на этом сервере Mastodon.",
+  "search_results.statuses_fts_disabled": "Поиск постов по их содержанию не поддерживается данным узлом Mastodon.",
   "search_results.total": "{count, number} {count, plural, one {результат} few {результата} many {результатов} other {результатов}}",
   "status.admin_account": "Открыть интерфейс модератора для @{name}",
-  "status.admin_status": "Открыть этот статус в интерфейсе модератора",
+  "status.admin_status": "Открыть этот пост в интерфейсе модератора",
   "status.block": "Заблокировать @{name}",
+  "status.bookmark": "Добавить в закладки",
   "status.cancel_reblog_private": "Не продвигать",
-  "status.cannot_reblog": "Этот статус не может быть продвинут",
-  "status.copy": "Копировать ссылку на запись",
+  "status.cannot_reblog": "Этот пост не может быть продвинут",
+  "status.copy": "Скопировать ссылку на пост",
   "status.delete": "Удалить",
   "status.detailed_status": "Подробный просмотр обсуждения",
   "status.direct": "Написать @{name}",
-  "status.embed": "Встроить",
+  "status.embed": "Встроить на свой сайт",
   "status.favourite": "Нравится",
   "status.filtered": "Отфильтровано",
-  "status.load_more": "Показать еще",
-  "status.media_hidden": "Медиа скрыто",
+  "status.load_more": "Загрузить остальное",
+  "status.media_hidden": "Файл скрыт",
   "status.mention": "Упомянуть @{name}",
   "status.more": "Больше",
-  "status.mute": "Заглушить @{name}",
-  "status.mute_conversation": "Заглушить всё обсуждение",
-  "status.open": "Развернуть статус",
+  "status.mute": "Игнорировать @{name}",
+  "status.mute_conversation": "Игнорировать обсуждение",
+  "status.open": "Открыть пост",
   "status.pin": "Закрепить в профиле",
-  "status.pinned": "Закреплённый статус",
+  "status.pinned": "Закреплённый пост",
   "status.read_more": "Ещё",
   "status.reblog": "Продвинуть",
   "status.reblog_private": "Продвинуть для своей аудитории",
   "status.reblogged_by": "{name} продвинул(а)",
-  "status.reblogs.empty": "Никто ещё не продвинул этот статус. Как только кто-то это сделает, они появятся здесь.",
+  "status.reblogs.empty": "Никто ещё не продвинул этот пост. Как только кто-то это сделает, они появятся здесь.",
   "status.redraft": "Удалить и исправить",
+  "status.remove_bookmark": "Убрать из закладок",
   "status.reply": "Ответить",
   "status.replyAll": "Ответить всем",
   "status.report": "Пожаловаться",
-  "status.sensitive_warning": "Чувствительный контент",
+  "status.sensitive_warning": "Деликатное содержимое",
   "status.share": "Поделиться",
   "status.show_less": "Свернуть",
-  "status.show_less_all": "Свернуть для всех",
+  "status.show_less_all": "Свернуть все спойлеры в ветке",
   "status.show_more": "Развернуть",
-  "status.show_more_all": "Развернуть для всех",
+  "status.show_more_all": "Развернуть все спойлеры в ветке",
   "status.show_thread": "Показать обсуждение",
-  "status.uncached_media_warning": "Недоступно",
-  "status.unmute_conversation": "Снять глушение с обсуждения",
+  "status.uncached_media_warning": "Файл недоступен",
+  "status.unmute_conversation": "Не игнорировать обсуждение",
   "status.unpin": "Открепить от профиля",
   "suggestions.dismiss": "Удалить предложение",
   "suggestions.header": "Вам может быть интересно…",
@@ -392,31 +405,34 @@
   "time_remaining.hours": "{number, plural, one {остался # час} few {осталось # часа} many {осталось # часов} other {осталось # часов}}",
   "time_remaining.minutes": "{number, plural, one {осталась # минута} few {осталось # минуты} many {осталось # минут} other {осталось # минут}}",
   "time_remaining.moments": "остались считанные мгновения",
-  "time_remaining.seconds": "{number, plural, one {осталась # секунду} few {осталось # секунды} many {осталось # секунд} other {осталось # секунд}}",
+  "time_remaining.seconds": "{number, plural, one {# секунда} many {# секунд} other {# секунды}}",
   "trends.count_by_accounts": "{count} {rawCount, plural, one {человек говорит} few {человека говорят} other {человек говорят}} про это",
   "trends.trending_now": "Самое актуальное",
   "ui.beforeunload": "Ваш черновик будет утерян, если вы покинете Mastodon.",
   "upload_area.title": "Перетащите сюда, чтобы загрузить",
-  "upload_button.label": "Добавить медиаконтент",
+  "upload_button.label": "Прикрепить фото, видео или аудио",
   "upload_error.limit": "Достигнут лимит загруженных файлов.",
   "upload_error.poll": "К опросам нельзя прикреплять файлы.",
+  "upload_form.audio_description": "Опишите аудиофайл для людей с нарушением слуха",
   "upload_form.description": "Добавьте описание для людей с нарушениями зрения:",
   "upload_form.edit": "Изменить",
   "upload_form.undo": "Отменить",
+  "upload_form.video_description": "Опишите видео для людей с нарушением слуха или зрения",
   "upload_modal.analyzing_picture": "Обработка изображения…",
   "upload_modal.apply": "Применить",
   "upload_modal.description_placeholder": "На дворе трава, на траве дрова",
   "upload_modal.detect_text": "Найти текст на картинке",
-  "upload_modal.edit_media": "Изменение медиа",
+  "upload_modal.edit_media": "Изменить файл",
   "upload_modal.hint": "Нажмите и перетащите круг в предпросмотре в точку фокуса, которая всегда будет видна на эскизах.",
   "upload_modal.preview_label": "Предпросмотр ({ratio})",
   "upload_progress.label": "Загрузка...",
   "video.close": "Закрыть видео",
+  "video.download": "Загрузить файл",
   "video.exit_fullscreen": "Покинуть полноэкранный режим",
   "video.expand": "Развернуть видео",
   "video.fullscreen": "Полноэкранный режим",
   "video.hide": "Скрыть видео",
-  "video.mute": "Заглушить звук",
+  "video.mute": "Выключить звук",
   "video.pause": "Пауза",
   "video.play": "Пуск",
   "video.unmute": "Включить звук"
diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json
index 0766e703f..976dc572e 100644
--- a/app/javascript/mastodon/locales/sk.json
+++ b/app/javascript/mastodon/locales/sk.json
@@ -1,10 +1,11 @@
 {
   "account.add_or_remove_from_list": "Pridaj do, alebo odober zo zoznamov",
   "account.badges.bot": "Bot",
+  "account.badges.group": "Skupina",
   "account.block": "Blokuj @{name}",
   "account.block_domain": "Ukry všetko z {domain}",
   "account.blocked": "Blokovaný/á",
-  "account.cancel_follow_request": "Zruš požiadanie o sledovanie",
+  "account.cancel_follow_request": "Zruš žiadosť o sledovanie",
   "account.direct": "Priama správa pre @{name}",
   "account.domain_blocked": "Doména ukrytá",
   "account.edit_profile": "Uprav profil",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Tempo obmedzené",
   "alert.unexpected.message": "Vyskytla sa nečakaná chyba.",
   "alert.unexpected.title": "Ups!",
+  "announcement.announcement": "Oboznámenie",
   "autosuggest_hashtag.per_week": "{count} týždenne",
   "boost_modal.combo": "Nabudúce môžeš kliknúť {combo} pre preskočenie",
   "bundle_column_error.body": "Pri načítaní tohto prvku nastala nejaká chyba.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Nastala chyba pri načítaní tohto komponentu.",
   "bundle_modal_error.retry": "Skúsiť znova",
   "column.blocks": "Blokovaní užívatelia",
+  "column.bookmarks": "Záložky",
   "column.community": "Miestna časová os",
   "column.direct": "Priame správy",
   "column.directory": "Prehľadávaj profily",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Trvanie ankety",
   "compose_form.poll.option_placeholder": "Voľba {number}",
   "compose_form.poll.remove_option": "Odstráň túto voľbu",
+  "compose_form.poll.switch_to_multiple": "Zmeň anketu pre povolenie viacerých možností",
+  "compose_form.poll.switch_to_single": "Zmeň anketu na takú s jedinou voľbou",
   "compose_form.publish": "Pošli",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Označ médiá ako chúlostivé",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Niesú tu žiadne príspevky!",
   "empty_column.account_unavailable": "Profil nedostupný",
   "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.domain_blocks": "Žiadne domény ešte niesú skryté.",
@@ -176,8 +182,8 @@
   "hashtag.column_settings.tag_mode.none": "Žiaden z týchto",
   "hashtag.column_settings.tag_toggle": "Vlož dodatočné haštagy pre tento stĺpec",
   "home.column_settings.basic": "Základné",
-  "home.column_settings.show_reblogs": "Zobraziť povýšené",
-  "home.column_settings.show_replies": "Ukázať odpovede",
+  "home.column_settings.show_reblogs": "Ukáž vyzdvihnuté",
+  "home.column_settings.show_replies": "Ukáž odpovede",
   "intervals.full.days": "{number, plural, one {# deň} few {# dní} many {# dní} other {# dní}}",
   "intervals.full.hours": "{number, plural, one {# hodina} few {# hodín} many {# hodín} other {# hodín}}",
   "intervals.full.minutes": "{number, plural, one {# minúta} few {# minút} many {# minút} other {# minút}}",
@@ -192,7 +198,7 @@
   "introduction.interactions.favourite.headline": "Obľúbené",
   "introduction.interactions.favourite.text": "Obľúbením si môžeš príspevok uložiť na neskôr, a zároveň dať jeho autorovi vedieť, že sa ti páčil.",
   "introduction.interactions.reblog.headline": "Vyzdvihni",
-  "introduction.interactions.reblog.text": "Môžeš zdieľať príspevky iných ľudí s vašimi následovateľmi tým, že ich povýšiš.",
+  "introduction.interactions.reblog.text": "Môžeš zdieľať príspevky iných ľudí s tvojími následovateľmi tak, že ich vyzdvihneš.",
   "introduction.interactions.reply.headline": "Odpovedz",
   "introduction.interactions.reply.text": "Odpovedať môžeš na príspevky iných ľudí, aj na svoje vlastné, čím sa spolu prepoja do konverzácie.",
   "introduction.welcome.action": "Poďme do toho!",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "otvor zoznam stíšených užívateľov",
   "keyboard_shortcuts.my_profile": "otvor svoj profil",
   "keyboard_shortcuts.notifications": "otvor panel oboznámení",
+  "keyboard_shortcuts.open_media": "na otvorenie médií",
   "keyboard_shortcuts.pinned": "otvor zoznam pripnutých príspevkov",
   "keyboard_shortcuts.profile": "otvor autorov profil",
   "keyboard_shortcuts.reply": "odpovedať",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Skry oznámenia od tohto používateľa?",
   "navigation_bar.apps": "Aplikácie",
   "navigation_bar.blocks": "Blokovaní užívatelia",
+  "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",
@@ -268,12 +276,14 @@
   "navigation_bar.mutes": "Ignorovaní užívatelia",
   "navigation_bar.personal": "Osobné",
   "navigation_bar.pins": "Pripnuté príspevky",
-  "navigation_bar.preferences": "Voľby",
+  "navigation_bar.preferences": "Nastavenia",
   "navigation_bar.public_timeline": "Federovaná časová os",
   "navigation_bar.security": "Zabezbečenie",
   "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.mention": "{name} ťa spomenul/a",
+  "notification.own_poll": "Tvoja anketa sa skončila",
   "notification.poll": "Anketa v ktorej si hlasoval/a sa skončila",
   "notification.reblog": "{name} zdieľal/a tvoj príspevok",
   "notifications.clear": "Vyčisti oboznámenia",
@@ -284,11 +294,12 @@
   "notifications.column_settings.filter_bar.category": "Rýchle triedenie",
   "notifications.column_settings.filter_bar.show": "Ukáž",
   "notifications.column_settings.follow": "Noví sledujúci:",
+  "notifications.column_settings.follow_request": "Nové žiadosti o následovanie:",
   "notifications.column_settings.mention": "Zmienenia:",
   "notifications.column_settings.poll": "Výsledky ankiet:",
   "notifications.column_settings.push": "Push notifikácie",
   "notifications.column_settings.reblog": "Vyzdvihnutia:",
-  "notifications.column_settings.show": "Zobraz v stĺpci",
+  "notifications.column_settings.show": "Ukáž v stĺpci",
   "notifications.column_settings.sound": "Prehraj zvuk",
   "notifications.filter.all": "Všetky",
   "notifications.filter.boosts": "Vyzdvihnutia",
@@ -344,6 +355,7 @@
   "status.admin_account": "Otvor moderovacie rozhranie užívateľa @{name}",
   "status.admin_status": "Otvor tento príspevok v moderovacom rozhraní",
   "status.block": "Blokuj @{name}",
+  "status.bookmark": "Záložka",
   "status.cancel_reblog_private": "Nezdieľaj",
   "status.cannot_reblog": "Tento príspevok nemôže byť zdieľaný",
   "status.copy": "Skopíruj odkaz na príspevok",
@@ -365,9 +377,10 @@
   "status.read_more": "Čítaj ďalej",
   "status.reblog": "Vyzdvihni",
   "status.reblog_private": "Vyzdvihni k pôvodnému publiku",
-  "status.reblogged_by": "{name} povýšil/a",
+  "status.reblogged_by": "{name} vyzdvihli",
   "status.reblogs.empty": "Nikto ešte nevyzdvihol tento príspevok. Keď tak niekto urobí, bude to zobrazené práve tu.",
   "status.redraft": "Vymaž a prepíš",
+  "status.remove_bookmark": "Odstráň záložku",
   "status.reply": "Odpovedať",
   "status.replyAll": "Odpovedz na diskusiu",
   "status.report": "Nahlás @{name}",
@@ -393,25 +406,28 @@
   "time_remaining.minutes": "Ostáva {number, plural, one {# minúta} few {# minút} many {# minút} other {# minúty}}",
   "time_remaining.moments": "Ostáva už iba chviľka",
   "time_remaining.seconds": "Ostáva {number, plural, one {# sekunda} few {# sekúnd} many {# sekúnd} other {# sekúnd}}",
-  "trends.count_by_accounts": "{count} {rawCount, plural, one {človek vraví} other {ľudia vravia}}",
+  "trends.count_by_accounts": "{count} {rawCount, plural, one {človek spomína} other {ľudí spomína}}",
   "trends.trending_now": "Teraz populárne",
   "ui.beforeunload": "Čo máš rozpísané sa stratí, ak opustíš Mastodon.",
   "upload_area.title": "Pretiahni a pusť pre nahratie",
   "upload_button.label": "Pridaj médiálny súbor (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_error.limit": "Limit pre nahrávanie súborov bol prekročený.",
   "upload_error.poll": "Nahrávanie súborov pri anketách nieje možné.",
+  "upload_form.audio_description": "Popíš, pre ľudí so stratou sluchu",
   "upload_form.description": "Opis pre slabo vidiacich",
   "upload_form.edit": "Uprav",
   "upload_form.undo": "Vymaž",
+  "upload_form.video_description": "Popíš, pre ľudí so stratou sluchu, alebo očným znevýhodnením",
   "upload_modal.analyzing_picture": "Analyzujem obrázok…",
   "upload_modal.apply": "Použi",
   "upload_modal.description_placeholder": "Rýchla hnedá líška skáče ponad lenivého psa",
   "upload_modal.detect_text": "Rozpoznaj text z obrázka",
   "upload_modal.edit_media": "Uprav médiá",
-  "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.hint": "Klikni, alebo potiahni okruh ukážky pre zvolenie z ktorého východzieho bodu bude vždy v dohľadne na všetkých náhľadoch.",
   "upload_modal.preview_label": "Náhľad ({ratio})",
   "upload_progress.label": "Nahráva sa...",
   "video.close": "Zavri video",
+  "video.download": "Stiahni súbor",
   "video.exit_fullscreen": "Vypni zobrazenie na celú obrazovku",
   "video.expand": "Zväčši video",
   "video.fullscreen": "Zobraz na celú obrazovku",
diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json
index ecac0d9c7..bcf3b8d1a 100644
--- a/app/javascript/mastodon/locales/sl.json
+++ b/app/javascript/mastodon/locales/sl.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Dodaj ali odstrani iz seznama",
   "account.badges.bot": "Robot",
+  "account.badges.group": "Group",
   "account.block": "Blokiraj @{name}",
   "account.block_domain": "Skrij vse iz {domain}",
   "account.blocked": "Blokirano",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Rate limited",
   "alert.unexpected.message": "Zgodila se je nepričakovana napaka.",
   "alert.unexpected.title": "Uups!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} per week",
   "boost_modal.combo": "Če želite preskočiti to, lahko pritisnete {combo}",
   "bundle_column_error.body": "Med nalaganjem te komponente je prišlo do napake.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Med nalaganjem te komponente je prišlo do napake.",
   "bundle_modal_error.retry": "Poskusi ponovno",
   "column.blocks": "Blokirani uporabniki",
+  "column.bookmarks": "Bookmarks",
   "column.community": "Lokalna časovnica",
   "column.direct": "Neposredna sporočila",
   "column.directory": "Browse profiles",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Trajanje ankete",
   "compose_form.poll.option_placeholder": "Izbira {number}",
   "compose_form.poll.remove_option": "Odstrani to izbiro",
+  "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": "Tutni",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Označi medij kot občutljiv",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Tukaj ni tutov!",
   "empty_column.account_unavailable": "Profil ni na voljo",
   "empty_column.blocks": "Niste še blokirali nobenega uporabnika.",
+  "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "Lokalna časovnica je prazna. Napišite nekaj javnega, da se bo žoga zakotalila!",
   "empty_column.direct": "Nimate še nobenih neposrednih sporočil. Ko ga boste poslali ali prejeli, se bo prikazal tukaj.",
   "empty_column.domain_blocks": "Še vedno ni skritih domen.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "odpri seznam utišanih uporabnikov",
   "keyboard_shortcuts.my_profile": "odpri svoj profil",
   "keyboard_shortcuts.notifications": "odpri stolpec z obvestili",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "odpri seznam pripetih tutov",
   "keyboard_shortcuts.profile": "odpri avtorjev profil",
   "keyboard_shortcuts.reply": "odgovori",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Skrij obvestila tega uporabnika?",
   "navigation_bar.apps": "Mobilne aplikacije",
   "navigation_bar.blocks": "Blokirani uporabniki",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Lokalna časovnica",
   "navigation_bar.compose": "Sestavi nov tut",
   "navigation_bar.direct": "Neposredna sporočila",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Varnost",
   "notification.favourite": "{name} je vzljubil/a vaš status",
   "notification.follow": "{name} vam sledi",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} vas je omenil/a",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "Glasovanje, v katerem ste sodelovali, se je končalo",
   "notification.reblog": "{name} je spodbudil/a vaš status",
   "notifications.clear": "Počisti obvestila",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Vrstica za hitro filtriranje",
   "notifications.column_settings.filter_bar.show": "Pokaži",
   "notifications.column_settings.follow": "Novi sledilci:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "Omembe:",
   "notifications.column_settings.poll": "Rezultati glasovanja:",
   "notifications.column_settings.push": "Potisna obvestila",
@@ -344,6 +355,7 @@
   "status.admin_account": "Odpri vmesnik za moderiranje za @{name}",
   "status.admin_status": "Odpri status v vmesniku za moderiranje",
   "status.block": "Blokiraj @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Prekini spodbudo",
   "status.cannot_reblog": "Te objave ni mogoče spodbuditi",
   "status.copy": "Kopiraj povezavo do statusa",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} spodbuja",
   "status.reblogs.empty": "Nihče še ni spodbudil tega tuta. Ko se bo to zgodilo, se bodo pojavili tukaj.",
   "status.redraft": "Izbriši in preoblikuj",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "Odgovori",
   "status.replyAll": "Odgovori na objavo",
   "status.report": "Prijavi @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Dodaj medije ({formats})",
   "upload_error.limit": "Omejitev prenosa datoteke je presežena.",
   "upload_error.poll": "Prenos datoteke z anketami ni dovoljen.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "Opišite za slabovidne",
   "upload_form.edit": "Edit",
   "upload_form.undo": "Izbriši",
+  "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.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "Pošiljanje...",
   "video.close": "Zapri video",
+  "video.download": "Download file",
   "video.exit_fullscreen": "Izhod iz celozaslonskega načina",
   "video.expand": "Razširi video",
   "video.fullscreen": "Celozaslonski način",
diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json
index f58466d87..617c3aee3 100644
--- a/app/javascript/mastodon/locales/sq.json
+++ b/app/javascript/mastodon/locales/sq.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Shtoni ose Hiqni prej listash",
   "account.badges.bot": "Robot",
+  "account.badges.group": "Group",
   "account.block": "Blloko @{name}",
   "account.block_domain": "Fshih gjithçka prej {domain}",
   "account.blocked": "E bllokuar",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Rate limited",
   "alert.unexpected.message": "Ndodhi një gabim të papritur.",
   "alert.unexpected.title": "Hëm!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} per week",
   "boost_modal.combo": "Mund të shtypni {combo}, që të anashkalohet kjo herës tjetër",
   "bundle_column_error.body": "Diç shkoi ters teksa ngarkohej ky përbërës.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Diç shkoi ters teksa ngarkohej ky përbërës.",
   "bundle_modal_error.retry": "Riprovoni",
   "column.blocks": "Përdorues të bllokuar",
+  "column.bookmarks": "Bookmarks",
   "column.community": "Rrjedhë kohore vendore",
   "column.direct": "Mesazhe të drejtpërdrejta",
   "column.directory": "Browse profiles",
@@ -82,6 +85,8 @@
   "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": "Mesazh",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Mark media as sensitive",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "S’ka mesazhe këtu!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "S’keni bllokuar ende ndonjë përdorues.",
+  "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "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.domain_blocks": "Ende s’ka përkatësi të fshehura.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "për hapje liste përdoruesish të heshtuar",
   "keyboard_shortcuts.my_profile": "për hapjen e profilit tuaj",
   "keyboard_shortcuts.notifications": "për hapje shtylle njoftimesh",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "për hapje liste mesazhesh të fiksuar",
   "keyboard_shortcuts.profile": "për hapje të profilit të autorit",
   "keyboard_shortcuts.reply": "për t’u përgjigjur",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Të fshihen njoftimet prej këtij përdoruesi?",
   "navigation_bar.apps": "Aplikacione për celular",
   "navigation_bar.blocks": "Përdorues të bllokuar",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Rrjedhë kohore vendore",
   "navigation_bar.compose": "Hartoni mesazh të ri",
   "navigation_bar.direct": "Mesazhe të drejtpërdrejta",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Siguri",
   "notification.favourite": "{name} parapëlqeu gjendjen tuaj",
   "notification.follow": "{name} zuri t’ju ndjekë",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} ju ka përmendur",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "A poll you have voted in has ended",
   "notification.reblog": "{name} përforcoi gjendjen tuaj",
   "notifications.clear": "Pastroji njoftimet",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Shtyllë filtrimesh të shpejta",
   "notifications.column_settings.filter_bar.show": "Shfaq",
   "notifications.column_settings.follow": "Ndjekës të rinj:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "Përmendje:",
   "notifications.column_settings.poll": "Poll results:",
   "notifications.column_settings.push": "Njoftime Push",
@@ -344,6 +355,7 @@
   "status.admin_account": "Hap ndërfaqe moderimi për @{name}",
   "status.admin_status": "Hape këtë gjendje te ndërfaqja e moderimit",
   "status.block": "Blloko @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Shpërforcojeni",
   "status.cannot_reblog": "Ky postim s’mund të përforcohet",
   "status.copy": "Kopjoje lidhjen te gjendje",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} përforcoi",
   "status.reblogs.empty": "Këtë mesazh s’e ka përforcuar njeri deri tani. Kur ta bëjë dikush, kjo do të duket këtu.",
   "status.redraft": "Fshijeni & rihartojeni",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "Përgjigjuni",
   "status.replyAll": "Përgjigjuni rrjedhës",
   "status.report": "Raportojeni @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Shtoni media (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_error.limit": "U tejkalua kufi ngarkimi kartelash.",
   "upload_error.poll": "File upload not allowed with polls.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "Përshkruajeni për persona me probleme shikimi",
   "upload_form.edit": "Edit",
   "upload_form.undo": "Fshije",
+  "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.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "Po ngarkohet…",
   "video.close": "Mbylle videon",
+  "video.download": "Download file",
   "video.exit_fullscreen": "Dil nga mënyra Sa Krejt Ekrani",
   "video.expand": "Zgjeroje videon",
   "video.fullscreen": "Sa krejt ekrani",
diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json
index 59b8459ae..09b30ff5f 100644
--- a/app/javascript/mastodon/locales/sr-Latn.json
+++ b/app/javascript/mastodon/locales/sr-Latn.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Add or Remove from lists",
   "account.badges.bot": "Bot",
+  "account.badges.group": "Group",
   "account.block": "Blokiraj korisnika @{name}",
   "account.block_domain": "Sakrij sve sa domena {domain}",
   "account.blocked": "Blocked",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Rate limited",
   "alert.unexpected.message": "An unexpected error occurred.",
   "alert.unexpected.title": "Oops!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} per week",
   "boost_modal.combo": "Možete pritisnuti {combo} da preskočite ovo sledeći put",
   "bundle_column_error.body": "Nešto je pošlo po zlu prilikom učitavanja ove komponente.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Nešto nije bilo u redu pri učitavanju ove komponente.",
   "bundle_modal_error.retry": "Pokušajte ponovo",
   "column.blocks": "Blokirani korisnici",
+  "column.bookmarks": "Bookmarks",
   "column.community": "Lokalna lajna",
   "column.direct": "Direct messages",
   "column.directory": "Browse profiles",
@@ -82,6 +85,8 @@
   "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": "Tutni",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Mark media as sensitive",
@@ -138,6 +143,7 @@
   "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": "Lokalna lajna je prazna. Napišite nešto javno da lajna produva!",
   "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no hidden domains yet.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "to open muted users list",
   "keyboard_shortcuts.my_profile": "to open your profile",
   "keyboard_shortcuts.notifications": "to open notifications column",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "to open pinned toots list",
   "keyboard_shortcuts.profile": "to open author's profile",
   "keyboard_shortcuts.reply": "da odgovorite",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Sakrij obaveštenja od ovog korisnika?",
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blokirani korisnici",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Lokalna lajna",
   "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Security",
   "notification.favourite": "{name} je stavio Vaš status kao omiljeni",
   "notification.follow": "{name} Vas je zapratio",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} Vas je pomenuo",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "A poll you have voted in has ended",
   "notification.reblog": "{name} je podržao(la) Vaš status",
   "notifications.clear": "Očisti obaveštenja",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Quick filter bar",
   "notifications.column_settings.filter_bar.show": "Show",
   "notifications.column_settings.follow": "Novi pratioci:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "Pominjanja:",
   "notifications.column_settings.poll": "Poll results:",
   "notifications.column_settings.push": "Guraj obaveštenja",
@@ -344,6 +355,7 @@
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_status": "Open this status in the moderation interface",
   "status.block": "Block @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Ovaj status ne može da se podrži",
   "status.copy": "Copy link to status",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} podržao(la)",
   "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
   "status.redraft": "Delete & re-draft",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "Odgovori",
   "status.replyAll": "Odgovori na diskusiju",
   "status.report": "Prijavi korisnika @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Dodaj multimediju",
   "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": "Opiši za slabovide osobe",
   "upload_form.edit": "Edit",
   "upload_form.undo": "Opozovi",
+  "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.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "Otpremam...",
   "video.close": "Zatvori video",
+  "video.download": "Download file",
   "video.exit_fullscreen": "Napusti ceo ekran",
   "video.expand": "Proširi video",
   "video.fullscreen": "Ceo ekran",
diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json
index d222e774d..5b04f9826 100644
--- a/app/javascript/mastodon/locales/sr.json
+++ b/app/javascript/mastodon/locales/sr.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Add or Remove from lists",
   "account.badges.bot": "Бот",
+  "account.badges.group": "Group",
   "account.block": "Блокирај @{name}",
   "account.block_domain": "Сакриј све са домена {domain}",
   "account.blocked": "Блокиран",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Rate limited",
   "alert.unexpected.message": "Појавила се неочекивана грешка.",
   "alert.unexpected.title": "Упс!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} per week",
   "boost_modal.combo": "Можете притиснути {combo} да прескочите ово следећи пут",
   "bundle_column_error.body": "Нешто је пошло по злу приликом учитавања ове компоненте.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Нешто није било у реду при учитавању ове компоненте.",
   "bundle_modal_error.retry": "Покушајте поново",
   "column.blocks": "Блокирани корисници",
+  "column.bookmarks": "Bookmarks",
   "column.community": "Локална временска линија",
   "column.direct": "Директне поруке",
   "column.directory": "Browse profiles",
@@ -82,6 +85,8 @@
   "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": "Труби",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Mark media as sensitive",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "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": "Још увек немате директних порука. Када пошаљете или примите једну, појавиће се овде.",
   "empty_column.domain_blocks": "Још увек нема сакривених домена.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "да отворите листу ућутканих корисника",
   "keyboard_shortcuts.my_profile": "да отворите ваш профил",
   "keyboard_shortcuts.notifications": "да отворите колону обавештења",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "да отворите листу закачених труба",
   "keyboard_shortcuts.profile": "да отворите профил аутора",
   "keyboard_shortcuts.reply": "да одговорите",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Сакриј обавештења од овог корисника?",
   "navigation_bar.apps": "Мобилне апликације",
   "navigation_bar.blocks": "Блокирани корисници",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Локална временска линија",
   "navigation_bar.compose": "Саставите нову трубу",
   "navigation_bar.direct": "Директне поруке",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Безбедност",
   "notification.favourite": "{name} је ставио/ла Ваш статус као омиљени",
   "notification.follow": "{name} Вас је запратио/ла",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} Вас је поменуо/ла",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "A poll you have voted in has ended",
   "notification.reblog": "{name} је подржао/ла Ваш статус",
   "notifications.clear": "Очисти обавештења",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Quick filter bar",
   "notifications.column_settings.filter_bar.show": "Show",
   "notifications.column_settings.follow": "Нови пратиоци:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "Помињања:",
   "notifications.column_settings.poll": "Poll results:",
   "notifications.column_settings.push": "Гурај обавештења",
@@ -344,6 +355,7 @@
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_status": "Open this status in the moderation interface",
   "status.block": "Блокирај @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Уклони подршку",
   "status.cannot_reblog": "Овај статус не може да се подржи",
   "status.copy": "Copy link to status",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} подржао/ла",
   "status.reblogs.empty": "Још увек нико није подржао ову трубу. Када буде подржана, појавиће се овде.",
   "status.redraft": "Избриши и преправи",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "Одговори",
   "status.replyAll": "Одговори на дискусију",
   "status.report": "Пријави корисника @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Додај мултимедију (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "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": "Опишите за особе са оштећеним видом",
   "upload_form.edit": "Edit",
   "upload_form.undo": "Обриши",
+  "upload_form.video_description": "Describe for people with hearing loss or visual impairment",
   "upload_modal.analyzing_picture": "Analyzing picture…",
   "upload_modal.apply": "Apply",
   "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "Отпремам...",
   "video.close": "Затвори видео",
+  "video.download": "Download file",
   "video.exit_fullscreen": "Напусти цео екран",
   "video.expand": "Прошири видео",
   "video.fullscreen": "Цео екран",
diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json
index 9b12f337b..4e778a481 100644
--- a/app/javascript/mastodon/locales/sv.json
+++ b/app/javascript/mastodon/locales/sv.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Lägg till i eller ta bort från listor",
   "account.badges.bot": "Robot",
+  "account.badges.group": "Grupp",
   "account.block": "Blockera @{name}",
   "account.block_domain": "Dölj allt från {domain}",
   "account.blocked": "Blockerad",
@@ -37,11 +38,12 @@
   "account.unendorse": "Visa inte på profil",
   "account.unfollow": "Sluta följ",
   "account.unmute": "Sluta tysta @{name}",
-  "account.unmute_notifications": "Återaktivera notifieringar från @{name}",
+  "account.unmute_notifications": "Återaktivera aviseringar från @{name}",
   "alert.rate_limited.message": "Vänligen försök igen efter {retry_time, time, medium}.",
   "alert.rate_limited.title": "Mängd begränsad",
   "alert.unexpected.message": "Ett oväntat fel uppstod.",
   "alert.unexpected.title": "Hoppsan!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} per vecka",
   "boost_modal.combo": "Du kan trycka {combo} för att slippa detta nästa gång",
   "bundle_column_error.body": "Något gick fel medan denna komponent laddades.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Något gick fel när denna komponent laddades.",
   "bundle_modal_error.retry": "Försök igen",
   "column.blocks": "Blockerade användare",
+  "column.bookmarks": "Bokmärken",
   "column.community": "Lokal tidslinje",
   "column.direct": "Direktmeddelanden",
   "column.directory": "Bläddra bland profiler",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Varaktighet för omröstning",
   "compose_form.poll.option_placeholder": "Val {number}",
   "compose_form.poll.remove_option": "Ta bort detta val",
+  "compose_form.poll.switch_to_multiple": "Ändra enkät för att tillåta flera val",
+  "compose_form.poll.switch_to_single": "Ändra enkät för att tillåta ett enda val",
   "compose_form.publish": "Tut",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Markera media som känsligt",
@@ -99,7 +104,7 @@
   "confirmations.delete_list.confirm": "Ta bort",
   "confirmations.delete_list.message": "Är du säker på att du vill radera denna lista permanent?",
   "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 notifieringar. Dina följare från den domänen komer att tas bort.",
+  "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.logout.confirm": "Logga ut",
   "confirmations.logout.message": "Är du säker på att du vill logga ut?",
   "confirmations.mute.confirm": "Tysta",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Inga inlägg här!",
   "empty_column.account_unavailable": "Profilen ej tillgänglig",
   "empty_column.blocks": "Du har ännu ej blockerat några användare.",
+  "empty_column.bookmarked_statuses": "Du har inte bokmärkt några tutar än. När du gör ett bokmärke 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. När du skickar eller tar emot ett kommer det att visas här.",
   "empty_column.domain_blocks": "Det finns ännu inga dolda domäner.",
@@ -164,7 +170,7 @@
   "getting_started.heading": "Kom igång",
   "getting_started.invite": "Skicka inbjudningar",
   "getting_started.open_source_notice": "Mastodon är programvara med öppen källkod. Du kan bidra eller rapportera problem via GitHub på {github}.",
-  "getting_started.security": "Säkerhet",
+  "getting_started.security": "Kontoinställningar",
   "getting_started.terms": "Användarvillkor",
   "hashtag.column_header.tag_mode.all": "och {additional}",
   "hashtag.column_header.tag_mode.any": "eller {additional}",
@@ -179,8 +185,8 @@
   "home.column_settings.show_reblogs": "Visa knuffar",
   "home.column_settings.show_replies": "Visa svar",
   "intervals.full.days": "{number, plural, one {# dag} other {# dagar}}",
-  "intervals.full.hours": "{hours, plural, one {# timme} other {# timmar}}",
-  "intervals.full.minutes": "{minutes, plural, one {1 minut} other {# minuter}}",
+  "intervals.full.hours": "{number, plural, one {# timme} other {# timmar}}",
+  "intervals.full.minutes": "{number, plural, one {# minut} other {# minuter}}",
   "introduction.federation.action": "Nästa",
   "introduction.federation.federated.headline": "Federerad",
   "introduction.federation.federated.text": "Publika inlägg från andra servrar i servernätverket visas i den förenade tidslinjen.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "för att öppna listan över tystade användare",
   "keyboard_shortcuts.my_profile": "för att öppna din profil",
   "keyboard_shortcuts.notifications": "för att öppna Meddelanden",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "för att öppna Nålade toots",
   "keyboard_shortcuts.profile": "för att öppna skaparens profil",
   "keyboard_shortcuts.reply": "för att svara",
@@ -248,9 +255,10 @@
   "media_gallery.toggle_visible": "Växla synlighet",
   "missing_indicator.label": "Hittades inte",
   "missing_indicator.sublabel": "Den här resursen kunde inte hittas",
-  "mute_modal.hide_notifications": "Dölj notifikationer från denna användare?",
+  "mute_modal.hide_notifications": "Dölj aviseringar från denna användare?",
   "navigation_bar.apps": "Mobilappar",
   "navigation_bar.blocks": "Blockerade användare",
+  "navigation_bar.bookmarks": "Bokmärken",
   "navigation_bar.community_timeline": "Lokal tidslinje",
   "navigation_bar.compose": "Författa ny toot",
   "navigation_bar.direct": "Direktmeddelanden",
@@ -273,20 +281,23 @@
   "navigation_bar.security": "Säkerhet",
   "notification.favourite": "{name} favoriserade din status",
   "notification.follow": "{name} följer dig",
+  "notification.follow_request": "{name} har begärt att följa dig",
   "notification.mention": "{name} nämnde dig",
+  "notification.own_poll": "Din röstning har avslutats",
   "notification.poll": "En omröstning du röstat i har avslutats",
   "notification.reblog": "{name} knuffade din status",
-  "notifications.clear": "Rensa meddelanden",
-  "notifications.clear_confirmation": "Är du säker på att du vill radera alla dina meddelanden permanent?",
-  "notifications.column_settings.alert": "Skrivbordsmeddelanden",
+  "notifications.clear": "Rensa aviseringar",
+  "notifications.clear_confirmation": "Är du säker på att du vill rensa alla dina aviseringar permanent?",
+  "notifications.column_settings.alert": "Skrivbordsaviseringar",
   "notifications.column_settings.favourite": "Favoriter:",
   "notifications.column_settings.filter_bar.advanced": "Visa alla kategorier",
   "notifications.column_settings.filter_bar.category": "Snabbfilter",
   "notifications.column_settings.filter_bar.show": "Visa",
   "notifications.column_settings.follow": "Nya följare:",
+  "notifications.column_settings.follow_request": "Ny följ-förfrågan:",
   "notifications.column_settings.mention": "Omnämningar:",
   "notifications.column_settings.poll": "Omröstningsresultat:",
-  "notifications.column_settings.push": "Push meddelanden",
+  "notifications.column_settings.push": "Push-aviseringar",
   "notifications.column_settings.reblog": "Knuffar:",
   "notifications.column_settings.show": "Visa i kolumnen",
   "notifications.column_settings.sound": "Spela upp ljud",
@@ -344,6 +355,7 @@
   "status.admin_account": "Öppet modereringsgränssnitt för @{name}",
   "status.admin_status": "Öppna denna status i modereringsgränssnittet",
   "status.block": "Blockera @{name}",
+  "status.bookmark": "Bokmärk",
   "status.cancel_reblog_private": "Ta bort knuff",
   "status.cannot_reblog": "Detta inlägg kan inte knuffas",
   "status.copy": "Kopiera länk till status",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} knuffade",
   "status.reblogs.empty": "Ingen har favoriserat den här tutningen än. När någon gör det kommer den att synas här.",
   "status.redraft": "Radera & gör om",
+  "status.remove_bookmark": "Ta bort bokmärke",
   "status.reply": "Svara",
   "status.replyAll": "Svara på tråden",
   "status.report": "Rapportera @{name}",
@@ -386,7 +399,7 @@
   "tabs_bar.federated_timeline": "Förenad",
   "tabs_bar.home": "Hem",
   "tabs_bar.local_timeline": "Lokal",
-  "tabs_bar.notifications": "Meddelanden",
+  "tabs_bar.notifications": "Aviseringar",
   "tabs_bar.search": "Sök",
   "time_remaining.days": "{number, plural, one {# dag} other {# dagar}} kvar",
   "time_remaining.hours": "{hours, plural, one {# timme} other {# timmar}} kvar",
@@ -400,9 +413,11 @@
   "upload_button.label": "Lägg till media",
   "upload_error.limit": "Filöverföringsgränsen överskriden.",
   "upload_error.poll": "Filuppladdning tillåts inte med omröstningar.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "Beskriv för synskadade",
   "upload_form.edit": "Redigera",
   "upload_form.undo": "Ta bort",
+  "upload_form.video_description": "Describe for people with hearing loss or visual impairment",
   "upload_modal.analyzing_picture": "Analyserar bild…",
   "upload_modal.apply": "Verkställ",
   "upload_modal.description_placeholder": "En snabb brun räv hoppar över den lata hunden",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Förhandstitt ({ratio})",
   "upload_progress.label": "Laddar upp...",
   "video.close": "Stäng video",
+  "video.download": "Ladda ner fil",
   "video.exit_fullscreen": "Stäng helskärm",
   "video.expand": "Expandera video",
   "video.fullscreen": "Helskärm",
diff --git a/app/javascript/mastodon/locales/ta.json b/app/javascript/mastodon/locales/ta.json
index 9be141523..8d1a8f14d 100644
--- a/app/javascript/mastodon/locales/ta.json
+++ b/app/javascript/mastodon/locales/ta.json
@@ -1,16 +1,17 @@
 {
-  "account.add_or_remove_from_list": "பட்டியல்களில் இருந்து சேர் அல்லது நீக்குக",
+  "account.add_or_remove_from_list": "பட்டியல்களில் சேர்/நீக்கு",
   "account.badges.bot": "பாட்",
+  "account.badges.group": "குழு",
   "account.block": "@{name} -ஐத் தடு",
   "account.block_domain": "{domain} யில் இருந்து வரும் எல்லாவற்றையும் மறை",
   "account.blocked": "முடக்கப்பட்டது",
-  "account.cancel_follow_request": "பின்தொடரும் முடிவைக் கைவிடவும்",
+  "account.cancel_follow_request": "பின்தொடரும் கோரிக்கையை நிராகரி",
   "account.direct": "நேரடி செய்தி @{name}",
   "account.domain_blocked": "மறைக்கப்பட்டத் தளங்கள்",
   "account.edit_profile": "சுயவிவரத்தை மாற்று",
   "account.endorse": "சுயவிவரத்தில் வெளிப்படுத்து",
   "account.follow": "பின்தொடர்",
-  "account.followers": "பின்பற்றுபவர்கள்",
+  "account.followers": "பின்தொடர்பவர்கள்",
   "account.followers.empty": "இதுவரை யாரும் இந்த பயனரைப் பின்தொடரவில்லை.",
   "account.follows": "பின்தொடர்",
   "account.follows.empty": "இந்த பயனர் இதுவரை யாரையும் பின்தொடரவில்லை.",
@@ -18,7 +19,7 @@
   "account.hide_reblogs": "இருந்து ஊக்கியாக மறை @{name}",
   "account.last_status": "கடைசி செயல்பாடு",
   "account.link_verified_on": "இந்த இணைப்பை உரிமையாளர் சரிபார்க்கப்பட்டது {date}",
-  "account.locked_info": "இந்த கணக்கின் தனியுரிமை நிலை பூட்டப்படவுள்ளது. உரிமையாளர் தன்னை யார் பின்தொடரலாம் என்பதை தானே முடிவு செய்வார்.",
+  "account.locked_info": "இந்தக் கணக்கு தனியுரிமை நிலை பூட்டப்பட்டுள்ளது. அவர்களைப் பின்தொடர்பவர் யார் என்பதை உரிமையாளர் கைமுறையாக மதிப்பாய்வு செய்கிறார்.",
   "account.media": "ஊடகங்கள்",
   "account.mention": "குறிப்பிடு @{name}",
   "account.moved_to": "{name} நகர்த்தப்பட்டது:",
@@ -29,37 +30,39 @@
   "account.posts": "டூட்டுகள்",
   "account.posts_with_replies": "Toots மற்றும் பதில்கள்",
   "account.report": "@{name} -ஐப் புகாரளி",
-  "account.requested": "ஒப்புதலுக்காக காத்திருக்கிறது. கோரிக்கையை ரத்துசெய்ய கிளிக் செய்க",
+  "account.requested": "ஒப்புதலுக்காகக் காத்திருக்கிறது. பின்தொடரும் கோரிக்கையை நீக்க அழுத்தவும்",
   "account.share": "@{name} உடைய விவரத்தை பகிர்",
   "account.show_reblogs": "காட்டு boosts இருந்து @{name}",
-  "account.unblock": "விடுவி @{name}",
-  "account.unblock_domain": "காண்பி {domain}",
-  "account.unendorse": "சுயவிவரத்தில் அம்சம் இல்லை",
-  "account.unfollow": "பின்தொடராட்",
-  "account.unmute": "தடுப்புநீக்கு @{name}",
-  "account.unmute_notifications": "அறிவிப்புகளை அகற்றவும் @{name}",
-  "alert.rate_limited.message": "{retry_time, time, medium} மணிக்குப் பிறகு மீண்டும் முயற்சிக்கவும்.",
-  "alert.rate_limited.title": "விகித அளவுக்கு உட்படுத்தப்பட்டது",
+  "account.unblock": "@{name} மீது தடை நீக்குக",
+  "account.unblock_domain": "{domain} ஐ காண்பி",
+  "account.unendorse": "சுயவிவரத்தில் இடம்பெற வேண்டாம்",
+  "account.unfollow": "பின்தொடர்வதை நிறுத்துக",
+  "account.unmute": "@{name} இன் மீது மௌனத் தடையை நீக்குக",
+  "account.unmute_notifications": "@{name} இலிருந்து அறிவிப்புகளின் மீது மௌனத் தடையை நீக்குக",
+  "alert.rate_limited.message": "{retry_time, time, medium} க்கு பிறகு மீண்டும் முயற்சிக்கவும்.",
+  "alert.rate_limited.title": "விகிதம் வரையறுக்கப்பட்டுள்ளது",
   "alert.unexpected.message": "எதிர்பாராத பிழை ஏற்பட்டுவிட்டது.",
   "alert.unexpected.title": "அச்சச்சோ!",
-  "autosuggest_hashtag.per_week": "வாரத்திற்கு {count}",
+  "announcement.announcement": "Announcement",
+  "autosuggest_hashtag.per_week": "ஒவ்வொரு வாரம் {count}",
   "boost_modal.combo": "நீங்கள் இதை அடுத்தமுறை தவிர்க்க {combo} வை அழுத்தவும்",
-  "bundle_column_error.body": "இந்த பகுதி கூறை ஏற்றம் செய்யும் பொது ஏதோ தவறு ஏற்பட்டுள்ளது.",
-  "bundle_column_error.retry": "மீண்டும் முயற்சி செய்",
+  "bundle_column_error.body": "இக்கூற்றை ஏற்றம் செய்யும்பொழுது ஏதோ தவறு ஏற்பட்டுள்ளது.",
+  "bundle_column_error.retry": "மீண்டும் முயல்க",
   "bundle_column_error.title": "பிணையப் பிழை",
-  "bundle_modal_error.close": "நெருக்கமாக",
-  "bundle_modal_error.message": "இந்த கூறுகளை ஏற்றும்போது ஏதோ தவறு ஏற்பட்டது.",
+  "bundle_modal_error.close": "மூடுக",
+  "bundle_modal_error.message": "இக்கூற்றை ஏற்றம் செய்யும்பொழுது ஏதோ தவறு ஏற்பட்டுள்ளது.",
   "bundle_modal_error.retry": "மீண்டும் முயற்சி செய்",
   "column.blocks": "தடுக்கப்பட்ட பயனர்கள்",
-  "column.community": "உள்ளூர் காலக்கெடு",
-  "column.direct": "நேரடி செய்திகள்",
+  "column.bookmarks": "Bookmarks",
+  "column.community": "சுய நிகழ்வு காலவரிசை",
+  "column.direct": "நேர் சேதிகள்",
   "column.directory": "சுயவிவரங்களை உலாவு",
-  "column.domain_blocks": "மறைந்த களங்கள்",
-  "column.favourites": "விருப்பத்துக்குகந்த",
-  "column.follow_requests": "கோரிக்கைகளை பின்பற்றவும்",
-  "column.home": "முதற்பக்கம்",
-  "column.lists": "குதிரை வீர்ர்கள்",
-  "column.mutes": "முடக்கப்பட்ட பயனர்கள்",
+  "column.domain_blocks": "மறைந்திருக்கும் திரளங்கள்",
+  "column.favourites": "பிடித்தவைகள்",
+  "column.follow_requests": "பின்தொடர அனுமதிகள்",
+  "column.home": "முகப்பு",
+  "column.lists": "பட்டியல்கள்",
+  "column.mutes": "மௌனத் தடை செய்யப்பட்ட பயனர்கள்",
   "column.notifications": "அறிவிப்புகள்",
   "column.pins": "Pinned toot",
   "column.public": "கூட்டாட்சி காலக்கெடு",
@@ -71,7 +74,7 @@
   "column_header.show_settings": "அமைப்புகளைக் காட்டு",
   "column_header.unpin": "பொருத்தப்படாத",
   "column_subheading.settings": "அமைப்புகள்",
-  "community.column_settings.media_only": "மீடியா மட்டுமே",
+  "community.column_settings.media_only": "படங்கள் மட்டுமே",
   "compose_form.direct_message_warning": "This toot will only be sent to all the mentioned users.",
   "compose_form.direct_message_warning_learn_more": "மேலும் அறிக",
   "compose_form.hashtag_warning": "இந்த toot பட்டியலிடப்படாதது போல எந்த ஹேஸ்டேக்கின் கீழ் பட்டியலிடப்படாது. ஹேஸ்டேக் மூலம் பொது டோட்டல்கள் மட்டுமே தேட முடியும்.",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "வாக்கெடுப்பு காலம்",
   "compose_form.poll.option_placeholder": "தேர்ந்தெடுப்ப {number}",
   "compose_form.poll.remove_option": "இந்த விருப்பத்தை அகற்றவும்",
+  "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": "டூட் செய்க",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "அனைவருக்கும் ஏற்ற ஊடகம் இல்லை எனக் குறியிடுக",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "இல்லை toots இங்கே!",
   "empty_column.account_unavailable": "சுயவிவரம் கிடைக்கவில்லை",
   "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": "உங்களிடம் நேரடியான செய்திகள் எதுவும் இல்லை. நீங்கள் ஒன்றை அனுப்பி அல்லது பெறும் போது, அது இங்கே காண்பிக்கும்.",
   "empty_column.domain_blocks": "இன்னும் மறைந்த களங்கள் இல்லை.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "முடக்கப்பட்ட பயனர்களின் பட்டியலைத் திறக்க",
   "keyboard_shortcuts.my_profile": "உங்கள் சுயவிவரத்தை திறக்க",
   "keyboard_shortcuts.notifications": "அறிவிப்பு நெடுவரிசையைத் திறக்க",
+  "keyboard_shortcuts.open_media": "படத்தைத் திறக்க‌",
   "keyboard_shortcuts.pinned": "திறக்க பொருத்தப்பட்டன toots பட்டியல்",
   "keyboard_shortcuts.profile": "ஆசிரியரின் சுயவிவரத்தைத் திறக்க",
   "keyboard_shortcuts.reply": "பதிலளிக்க",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "இந்த பயனரின் அறிவிப்புகளை மறைக்கவா?",
   "navigation_bar.apps": "மொபைல் பயன்பாடுகள்",
   "navigation_bar.blocks": "தடுக்கப்பட்ட பயனர்கள்",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "உள்ளூர் காலக்கெடு",
   "navigation_bar.compose": "புதியவற்றை எழுதுக toot",
   "navigation_bar.direct": "நேரடி செய்திகள்",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "பத்திரம்",
   "notification.favourite": "{name} ஆர்வம் கொண்டவர், உங்கள் நிலை",
   "notification.follow": "{name} உங்களைப் பின்தொடர்கிறார்",
+  "notification.follow_request": "{name} உங்களைப் பின்தொடரக் கோருகிறார்",
   "notification.mention": "{name} நீங்கள் குறிப்பிட்டுள்ளீர்கள்",
+  "notification.own_poll": "கருத்துக்கணிப்பு நிறைவடைந்தது",
   "notification.poll": "நீங்கள் வாக்களித்த வாக்கெடுப்பு முடிவடைந்தது",
   "notification.reblog": "{name} உங்கள் நிலை அதிகரித்தது",
   "notifications.clear": "அறிவிப்புகளை அழிக்கவும்",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "விரைவு வடிகட்டி பட்டை",
   "notifications.column_settings.filter_bar.show": "காட்டு",
   "notifications.column_settings.follow": "புதிய பின்பற்றுபவர்கள்:",
+  "notifications.column_settings.follow_request": "புதிய பின்தொடர் கோரிக்கைகள்:",
   "notifications.column_settings.mention": "குறிப்பிடுகிறது:",
   "notifications.column_settings.poll": "கருத்துக்கணிப்பு முடிவுகள்:",
   "notifications.column_settings.push": "தள் அறிவிப்புகள்",
@@ -344,6 +355,7 @@
   "status.admin_account": "மிதமான இடைமுகத்தை திறக்க @{name}",
   "status.admin_status": "மிதமான இடைமுகத்தில் இந்த நிலையை திறக்கவும்",
   "status.block": "@{name} -ஐத் தடு",
+  "status.bookmark": "அடையாளம் குறி",
   "status.cancel_reblog_private": "இல்லை பூஸ்ட்",
   "status.cannot_reblog": "இந்த இடுகை அதிகரிக்க முடியாது",
   "status.copy": "நிலைக்கு இணைப்பை நகலெடு",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} மதிப்பை உயர்த்து",
   "status.reblogs.empty": "இதுவரை யாரும் இந்த மோதலை அதிகரிக்கவில்லை. யாராவது செய்தால், அவர்கள் இங்கே காண்பார்கள்.",
   "status.redraft": "நீக்கு மற்றும் மீண்டும் வரைவு",
+  "status.remove_bookmark": "அடையாளம் நீக்கு",
   "status.reply": "பதில்",
   "status.replyAll": "நூலுக்கு பதிலளிக்கவும்",
   "status.report": "@{name} மீது புகாரளி",
@@ -400,9 +413,11 @@
   "upload_button.label": "மீடியாவைச் சேர்க்கவும் (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_error.limit": "கோப்பு பதிவேற்ற வரம்பு மீறப்பட்டது.",
   "upload_error.poll": "கோப்பு பதிவேற்றம் அனுமதிக்கப்படவில்லை.",
+  "upload_form.audio_description": "செவித்திறன் குறைபாடு உள்ளவர்களுக்காக விளக்குக‌",
   "upload_form.description": "பார்வையற்ற விவரிக்கவும்",
   "upload_form.edit": "தொகு",
   "upload_form.undo": "நீக்கு",
+  "upload_form.video_description": "செவித்திறன் மற்றும் பார்வைக் குறைபாடு உள்ளவர்களுக்காக விளக்குக‌",
   "upload_modal.analyzing_picture": "படம் ஆராயப்படுகிறது…",
   "upload_modal.apply": "உபயோகி",
   "upload_modal.description_placeholder": "பொருள் விளக்கம்",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "முன்னோட்டம் ({ratio})",
   "upload_progress.label": "ஏற்றுகிறது ...",
   "video.close": "வீடியோவை மூடு",
+  "video.download": "கோப்பைப் பதிவிறக்கவும்",
   "video.exit_fullscreen": "முழு திரையில் இருந்து வெளியேறவும்",
   "video.expand": "வீடியோவை விரிவாக்கு",
   "video.fullscreen": "முழுத்திரை",
diff --git a/app/javascript/mastodon/locales/te.json b/app/javascript/mastodon/locales/te.json
index 3cfbc5786..8b47fec52 100644
--- a/app/javascript/mastodon/locales/te.json
+++ b/app/javascript/mastodon/locales/te.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "జాబితాల నుండి చేర్చు లేదా తీసివేయి",
   "account.badges.bot": "బాట్",
+  "account.badges.group": "Group",
   "account.block": "@{name} ను బ్లాక్ చేయి",
   "account.block_domain": "{domain} నుంచి అన్నీ దాచిపెట్టు",
   "account.blocked": "బ్లాక్ అయినవి",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Rate limited",
   "alert.unexpected.message": "అనుకోని తప్పు జరిగినది.",
   "alert.unexpected.title": "అయ్యో!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} per week",
   "boost_modal.combo": "మీరు తదుపరిసారి దీనిని దాటవేయడానికి {combo} నొక్కవచ్చు",
   "bundle_column_error.body": "ఈ భాగం లోడ్ అవుతున్నప్పుడు ఏదో తప్పు జరిగింది.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "ఈ భాగం లోడ్ అవుతున్నప్పుడు ఏదో తప్పు జరిగింది.",
   "bundle_modal_error.retry": "మళ్ళీ ప్రయత్నించండి",
   "column.blocks": "బ్లాక్ చేయబడిన వినియోగదారులు",
+  "column.bookmarks": "Bookmarks",
   "column.community": "స్థానిక కాలక్రమం",
   "column.direct": "ప్రత్యక్ష సందేశాలు",
   "column.directory": "Browse profiles",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "ఎన్నిక వ్యవధి",
   "compose_form.poll.option_placeholder": "ఎంపిక {number}",
   "compose_form.poll.remove_option": "ఈ ఎంపికను తొలగించు",
+  "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": "టూట్",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Mark media as sensitive",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "ఇక్కడ ఏ టూట్లూ లేవు!No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "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": "మీకు ఇంకా ఏ ప్రత్యక్ష సందేశాలు లేవు. మీరు ఒకదాన్ని పంపినప్పుడు లేదా స్వీకరించినప్పుడు, అది ఇక్కడ చూపబడుతుంది.",
   "empty_column.domain_blocks": "దాచబడిన డొమైన్లు ఇంకా ఏమీ లేవు.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "మ్యూట్ చేయబడిన వినియోగదారుల జాబితాను తెరవడానికి",
   "keyboard_shortcuts.my_profile": "మీ ప్రొఫైల్ను తెరవడానికి",
   "keyboard_shortcuts.notifications": "నోటిఫికేషన్ల నిలువు వరుసను తెరవడానికి",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "అతికించబడిన టూట్ల జాబితాను తెరవడానికి",
   "keyboard_shortcuts.profile": "రచయిత ప్రొఫైల్ ను తెరవాలంటే",
   "keyboard_shortcuts.reply": "ప్రత్యుత్తరం ఇవ్వడానికి",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "ఈ వినియోగదారు నుండి నోటిఫికేషన్లను దాచాలా?",
   "navigation_bar.apps": "మొబైల్ ఆప్ లు",
   "navigation_bar.blocks": "బ్లాక్ చేయబడిన వినియోగదారులు",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "స్థానిక కాలక్రమం",
   "navigation_bar.compose": "కొత్త టూట్ను రాయండి",
   "navigation_bar.direct": "ప్రత్యక్ష సందేశాలు",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "భద్రత",
   "notification.favourite": "{name} మీ స్టేటస్ ను ఇష్టపడ్డారు",
   "notification.follow": "{name} మిమ్మల్ని అనుసరిస్తున్నారు",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} మిమ్మల్ని ప్రస్తావించారు",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "మీరు పాల్గొనిన ఎన్సిక ముగిసినది",
   "notification.reblog": "{name} మీ స్టేటస్ ను బూస్ట్ చేసారు",
   "notifications.clear": "ప్రకటనలను తుడిచివేయు",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "క్విక్ ఫిల్టర్ బార్",
   "notifications.column_settings.filter_bar.show": "చూపించు",
   "notifications.column_settings.follow": "క్రొత్త అనుచరులు:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "ప్రస్తావనలు:",
   "notifications.column_settings.poll": "ఎన్నిక ఫలితాలు:",
   "notifications.column_settings.push": "పుష్ ప్రకటనలు",
@@ -344,6 +355,7 @@
   "status.admin_account": "@{name} కొరకు సమన్వయ వినిమయసీమను తెరువు",
   "status.admin_status": "సమన్వయ వినిమయసీమలో ఈ స్టేటస్ ను తెరవండి",
   "status.block": "@{name} ను బ్లాక్ చేయి",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "బూస్ట్ను తొలగించు",
   "status.cannot_reblog": "ఈ పోస్ట్ను బూస్ట్ చేయడం సాధ్యం కాదు",
   "status.copy": "లంకెను స్టేటస్కు కాపీ చేయి",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} బూస్ట్ చేసారు",
   "status.reblogs.empty": "ఈ టూట్ను ఇంకా ఎవరూ బూస్ట్ చేయలేదు. ఎవరైనా చేసినప్పుడు, అవి ఇక్కడ కనబడతాయి.",
   "status.redraft": "తొలగించు & తిరగరాయు",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "ప్రత్యుత్తరం",
   "status.replyAll": "సంభాషణకు ప్రత్యుత్తరం ఇవ్వండి",
   "status.report": "@{name}పై ఫిర్యాదుచేయు",
@@ -400,9 +413,11 @@
   "upload_button.label": "మీడియాను జోడించండి (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "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": "దృష్టి లోపమున్న వారి కోసం వివరించండి",
   "upload_form.edit": "Edit",
   "upload_form.undo": "తొలగించు",
+  "upload_form.video_description": "Describe for people with hearing loss or visual impairment",
   "upload_modal.analyzing_picture": "Analyzing picture…",
   "upload_modal.apply": "Apply",
   "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "అప్లోడ్ అవుతోంది...",
   "video.close": "వీడియోని మూసివేయి",
+  "video.download": "Download file",
   "video.exit_fullscreen": "పూర్తి స్క్రీన్ నుండి నిష్క్రమించు",
   "video.expand": "వీడియోను విస్తరించండి",
   "video.fullscreen": "పూర్తి స్క్రీన్",
diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json
index 7db168338..2d009b851 100644
--- a/app/javascript/mastodon/locales/th.json
+++ b/app/javascript/mastodon/locales/th.json
@@ -1,9 +1,10 @@
 {
   "account.add_or_remove_from_list": "เพิ่มหรือเอาออกจากรายการ",
   "account.badges.bot": "บอต",
-  "account.block": "บล็อค @{name}",
+  "account.badges.group": "กลุ่ม",
+  "account.block": "ปิดกั้น @{name}",
   "account.block_domain": "ซ่อนทุกอย่างจาก {domain}",
-  "account.blocked": "ถูกบล็อค",
+  "account.blocked": "ปิดกั้นอยู่",
   "account.cancel_follow_request": "ยกเลิกคำขอติดตาม",
   "account.direct": "ส่งข้อความโดยตรงถึง @{name}",
   "account.domain_blocked": "ซ่อนโดเมนอยู่",
@@ -39,9 +40,10 @@
   "account.unmute": "เลิกปิดเสียง @{name}",
   "account.unmute_notifications": "เลิกปิดเสียงการแจ้งเตือนจาก @{name}",
   "alert.rate_limited.message": "โปรดลองใหม่หลังจาก {retry_time, time, medium}",
-  "alert.rate_limited.title": "เข้าใช้งานบ่อยเกินไป",
+  "alert.rate_limited.title": "มีการจำกัดอัตรา",
   "alert.unexpected.message": "เกิดข้อผิดพลาดที่ไม่คาดคิด",
   "alert.unexpected.title": "อุปส์!",
+  "announcement.announcement": "ประกาศ",
   "autosuggest_hashtag.per_week": "{count} ต่อสัปดาห์",
   "boost_modal.combo": "คุณสามารถกด {combo} เพื่อข้ามสิ่งนี้ในครั้งถัดไป",
   "bundle_column_error.body": "มีบางอย่างผิดพลาดขณะโหลดส่วนประกอบนี้",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "มีบางอย่างผิดพลาดขณะโหลดส่วนประกอบนี้",
   "bundle_modal_error.retry": "ลองอีกครั้ง",
   "column.blocks": "ผู้ใช้ที่ปิดกั้นอยู่",
+  "column.bookmarks": "ที่คั่นหน้า",
   "column.community": "เส้นเวลาในเว็บ",
   "column.direct": "ข้อความโดยตรง",
   "column.directory": "เรียกดูโปรไฟล์",
@@ -79,9 +82,11 @@
   "compose_form.lock_disclaimer.lock": "ล็อคอยู่",
   "compose_form.placeholder": "คุณกำลังคิดอะไรอยู่?",
   "compose_form.poll.add_option": "เพิ่มทางเลือก",
-  "compose_form.poll.duration": "ระยะเวลาโพล",
+  "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_loud": "{publish}!",
   "compose_form.sensitive.hide": "ทำเครื่องหมายสื่อว่าละเอียดอ่อน",
@@ -99,11 +104,11 @@
   "confirmations.delete_list.confirm": "ลบ",
   "confirmations.delete_list.message": "คุณแน่ใจหรือไม่ว่าต้องการลบรายการนี้อย่างถาวร?",
   "confirmations.domain_block.confirm": "ซ่อนทั้งโดเมน",
-  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
+  "confirmations.domain_block.message": "คุณแน่ใจจริง ๆ หรือไม่ว่าต้องการปิดกั้นทั้ง {domain}? ในกรณีส่วนใหญ่ การปิดกั้นหรือการปิดเสียงแบบกำหนดเป้าหมายไม่กี่รายการนั้นเพียงพอและเป็นที่นิยม คุณจะไม่เห็นเนื้อหาจากโดเมนนั้นในเส้นเวลาสาธารณะใด ๆ หรือการแจ้งเตือนของคุณ จะเอาผู้ติดตามของคุณจากโดเมนนั้นออก",
   "confirmations.logout.confirm": "ออกจากระบบ",
   "confirmations.logout.message": "คุณแน่ใจหรือไม่ว่าต้องการออกจากระบบ?",
   "confirmations.mute.confirm": "ปิดเสียง",
-  "confirmations.mute.explanation": "การกระทำนี้จะซ่อนโพสต์ของเขาและโพสต์ที่มีการกล่าวถึงเขา แต่ยังอนุญาตให้เขาเห็นโพสต์ของคุณและติดตามคุณได้",
+  "confirmations.mute.explanation": "นี่จะซ่อนโพสต์จากเขาและโพสต์ที่กล่าวถึงเขา แต่จะยังอนุญาตให้เขาเห็นโพสต์ของคุณและติดตามคุณ",
   "confirmations.mute.message": "คุณแน่ใจหรือไม่ว่าต้องการปิดเสียง {name}?",
   "confirmations.redraft.confirm": "ลบแล้วร่างใหม่",
   "confirmations.redraft.message": "คุณแน่ใจหรือไม่ว่าต้องการลบสถานะนี้แล้วร่างใหม่? รายการโปรดและการดันจะหายไป และการตอบกลับโพสต์ดั้งเดิมจะไม่มีความเกี่ยวพัน",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "ไม่มีโพสต์ที่นี่!",
   "empty_column.account_unavailable": "ไม่มีโปรไฟล์",
   "empty_column.blocks": "คุณยังไม่ได้ปิดกั้นผู้ใช้ใด ๆ",
+  "empty_column.bookmarked_statuses": "คุณยังไม่มีโพสต์ที่เพิ่มที่คั่นหน้าไว้ใด ๆ เมื่อคุณเพิ่มที่คั่นหน้าโพสต์ โพสต์จะปรากฏที่นี่",
   "empty_column.community": "เส้นเวลาในเว็บว่างเปล่า เขียนบางอย่างเป็นสาธารณะเพื่อเริ่มต้น!",
   "empty_column.direct": "คุณยังไม่มีข้อความโดยตรงใด ๆ เมื่อคุณส่งหรือรับข้อความ ข้อความจะปรากฏที่นี่",
   "empty_column.domain_blocks": "ยังไม่มีโดเมนที่ซ่อนอยู่",
@@ -152,8 +158,8 @@
   "empty_column.mutes": "คุณยังไม่ได้ปิดเสียงผู้ใช้ใด ๆ",
   "empty_column.notifications": "คุณยังไม่มีการแจ้งเตือนใด ๆ โต้ตอบกับผู้อื่นเพื่อเริ่มการสนทนา",
   "empty_column.public": "ไม่มีสิ่งใดที่นี่! เขียนบางอย่างเป็นสาธารณะ หรือติดตามผู้ใช้จากเซิร์ฟเวอร์อื่น ๆ ด้วยตนเองเพื่อเติมให้เต็ม",
-  "error.unexpected_crash.explanation": "หน้านี้ไม่สามารถแสดงผลได้อย่างถูกต้อง เนื่องจากบั๊กในโค้ดของเราหรือปัญหาความเข้ากับได้กับเบราเซอร์",
-  "error.unexpected_crash.next_steps": "ลองรีเฟรชหน้านี้ หากไม่สามารถช่วยได้ คุณอาจจะยังคงใช้ Mastodon ผ่านเบราเซอร์อื่นหรือผ่านแอปได้",
+  "error.unexpected_crash.explanation": "เนื่องจากข้อบกพร่องในโค้ดของเราหรือปัญหาความเข้ากันได้ของเบราว์เซอร์ จึงไม่สามารถแสดงหน้านี้ได้อย่างถูกต้อง",
+  "error.unexpected_crash.next_steps": "ลองรีเฟรชหน้า หากนั่นไม่ช่วย คุณอาจยังสามารถใช้ Mastodon ผ่านเบราว์เซอร์อื่นหรือแอป",
   "errors.unexpected_crash.copy_stacktrace": "คัดลอกการติดตามสแตกไปยังคลิปบอร์ด",
   "errors.unexpected_crash.report_issue": "รายงานปัญหา",
   "follow_request.authorize": "อนุญาต",
@@ -164,7 +170,7 @@
   "getting_started.heading": "เริ่มต้นใช้งาน",
   "getting_started.invite": "เชิญผู้คน",
   "getting_started.open_source_notice": "Mastodon เป็นซอฟต์แวร์เปิดต้นฉบับ คุณสามารถมีส่วนร่วมหรือรายงานปัญหาที่ GitHub ที่ {github}",
-  "getting_started.security": "ความปลอดภัย",
+  "getting_started.security": "การตั้งค่าบัญชี",
   "getting_started.terms": "เงื่อนไขการให้บริการ",
   "hashtag.column_header.tag_mode.all": "และ {additional}",
   "hashtag.column_header.tag_mode.any": "หรือ {additional}",
@@ -190,14 +196,14 @@
   "introduction.federation.local.text": "โพสต์สาธารณะจากผู้คนในเซิร์ฟเวอร์เดียวกันกับคุณจะปรากฏในเส้นเวลาในเว็บ",
   "introduction.interactions.action": "เสร็จสิ้นบทช่วยสอน!",
   "introduction.interactions.favourite.headline": "ชื่นชอบ",
-  "introduction.interactions.favourite.text": "คุณสามารถบันทึกโพสต์เพื่ออ่านทีหลัง และแจ้งให้ผู้เขียนโพสต์ทราบว่าคุณชอบโพสต์นั้นโดยการชื่นชอบโพสต์",
+  "introduction.interactions.favourite.text": "คุณสามารถบันทึกโพสต์ไว้ในภายหลังและแจ้งให้ผู้สร้างทราบว่าคุณชอบโพสต์โดยการชื่นชอบโพสต์",
   "introduction.interactions.reblog.headline": "ดัน",
-  "introduction.interactions.reblog.text": "คุณสามารถแชร์โพสต์ของผู้อื่นให้กับผู้ติดตามของคุณได้โดยการดันโพสต์",
+  "introduction.interactions.reblog.text": "คุณสามารถแบ่งปันโพสต์ของผู้อื่นกับผู้ติดตามของคุณโดยการดันโพสต์",
   "introduction.interactions.reply.headline": "ตอบกลับ",
-  "introduction.interactions.reply.text": "คุณสามารถตอบกลับโพสต์ของผู้อื่นและโพสต์ของคุณได้ ซึ่งจะถูกรวมเข้าด้วยกันเป็นบทสนทนา",
+  "introduction.interactions.reply.text": "คุณสามารถตอบกลับโพสต์ของผู้อื่นและของคุณเอง ซึ่งจะเชื่อมโยงโพสต์เข้าด้วยกันในการสนทนา",
   "introduction.welcome.action": "ไปกันเลย!",
   "introduction.welcome.headline": "ขั้นตอนแรก",
-  "introduction.welcome.text": "ยินดีต้อนรับสู่เฟดิเวิร์ส! ในอีกสักครู่คุณจะได้เผยแพร่ข้อความและคุยกับเพื่อนของคุณในหลากหลายเซิร์ฟเวอร์ แต่เซิร์ฟเวอร์ {domain} นี้มีความพิเศษ เพราะเป็นที่ที่โปรไฟล์ของคุณตั้งอยู่ จำชื่อไว้ด้วยนะ",
+  "introduction.welcome.text": "ยินดีต้อนรับสู่เฟดิเวิร์ส! ในอีกสักครู่ คุณจะสามารถออกอากาศข้อความและพูดคุยกับเพื่อน ๆ ของคุณผ่านเซิร์ฟเวอร์ที่หลากหลาย แต่เซิร์ฟเวอร์นี้ {domain} มีความพิเศษคือเป็นเซิร์ฟเวอร์ที่โฮสต์โปรไฟล์ของคุณ ดังนั้นจดจำชื่อของเซิร์ฟเวอร์ไว้",
   "keyboard_shortcuts.back": "เพื่อนำทางย้อนกลับ",
   "keyboard_shortcuts.blocked": "เพื่อเปิดรายการผู้ใช้ที่ปิดกั้นอยู่",
   "keyboard_shortcuts.boost": "เพื่อดัน",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "เพื่อเปิดรายการผู้ใช้ที่ปิดเสียงอยู่",
   "keyboard_shortcuts.my_profile": "เพื่อเปิดโปรไฟล์ของคุณ",
   "keyboard_shortcuts.notifications": "เพื่อเปิดคอลัมน์การแจ้งเตือน",
+  "keyboard_shortcuts.open_media": "เพื่อเปิดสื่อ",
   "keyboard_shortcuts.pinned": "เพื่อเปิดรายการโพสต์ที่ปักหมุด",
   "keyboard_shortcuts.profile": "เพื่อเปิดโปรไฟล์ของผู้สร้าง",
   "keyboard_shortcuts.reply": "เพื่อตอบกลับ",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "ซ่อนการแจ้งเตือนจากผู้ใช้นี้?",
   "navigation_bar.apps": "แอปมือถือ",
   "navigation_bar.blocks": "ผู้ใช้ที่ปิดกั้นอยู่",
+  "navigation_bar.bookmarks": "ที่คั่นหน้า",
   "navigation_bar.community_timeline": "เส้นเวลาในเว็บ",
   "navigation_bar.compose": "เขียนโพสต์ใหม่",
   "navigation_bar.direct": "ข้อความโดยตรง",
@@ -273,8 +281,10 @@
   "navigation_bar.security": "ความปลอดภัย",
   "notification.favourite": "{name} ได้ชื่นชอบสถานะของคุณ",
   "notification.follow": "{name} ได้ติดตามคุณ",
+  "notification.follow_request": "{name} ได้ขอติดตามคุณ",
   "notification.mention": "{name} ได้กล่าวถึงคุณ",
-  "notification.poll": "โพลที่คุณได้ลงคะแนนได้สิ้นสุดแล้ว",
+  "notification.own_poll": "การสำรวจความคิดเห็นของคุณได้สิ้นสุดแล้ว",
+  "notification.poll": "การสำรวจความคิดเห็นที่คุณได้ลงคะแนนได้สิ้นสุดแล้ว",
   "notification.reblog": "{name} ได้ดันสถานะของคุณ",
   "notifications.clear": "ล้างการแจ้งเตือน",
   "notifications.clear_confirmation": "คุณแน่ใจหรือไม่ว่าต้องการล้างการแจ้งเตือนทั้งหมดของคุณอย่างถาวร?",
@@ -284,8 +294,9 @@
   "notifications.column_settings.filter_bar.category": "แถบตัวกรองด่วน",
   "notifications.column_settings.filter_bar.show": "แสดง",
   "notifications.column_settings.follow": "ผู้ติดตามใหม่:",
+  "notifications.column_settings.follow_request": "คำขอติดตามใหม่:",
   "notifications.column_settings.mention": "การกล่าวถึง:",
-  "notifications.column_settings.poll": "ผลลัพธ์โพล:",
+  "notifications.column_settings.poll": "ผลลัพธ์การสำรวจความคิดเห็น:",
   "notifications.column_settings.push": "การแจ้งเตือนแบบผลัก",
   "notifications.column_settings.reblog": "การดัน:",
   "notifications.column_settings.show": "แสดงในคอลัมน์",
@@ -295,7 +306,7 @@
   "notifications.filter.favourites": "รายการโปรด",
   "notifications.filter.follows": "การติดตาม",
   "notifications.filter.mentions": "การกล่าวถึง",
-  "notifications.filter.polls": "ผลลัพธ์โพล",
+  "notifications.filter.polls": "ผลลัพธ์การสำรวจความคิดเห็น",
   "notifications.group": "{count} การแจ้งเตือน",
   "poll.closed": "ปิดแล้ว",
   "poll.refresh": "รีเฟรช",
@@ -303,8 +314,8 @@
   "poll.total_votes": "{count, plural, other {# การลงคะแนน}}",
   "poll.vote": "ลงคะแนน",
   "poll.voted": "คุณได้ลงคะแนนให้กับคำตอบนี้",
-  "poll_button.add_poll": "เพิ่มโพล",
-  "poll_button.remove_poll": "เอาโพลออก",
+  "poll_button.add_poll": "เพิ่มการสำรวจความคิดเห็น",
+  "poll_button.remove_poll": "เอาการสำรวจความคิดเห็นออก",
   "privacy.change": "ปรับเปลี่ยนความเป็นส่วนตัวของสถานะ",
   "privacy.direct.long": "โพสต์ไปยังผู้ใช้ที่กล่าวถึงเท่านั้น",
   "privacy.direct.short": "โดยตรง",
@@ -339,11 +350,12 @@
   "search_results.accounts": "ผู้คน",
   "search_results.hashtags": "แฮชแท็ก",
   "search_results.statuses": "โพสต์",
-  "search_results.statuses_fts_disabled": "การค้นหาเนื้อหาในโพสต์ถูกปิดบนเซิร์ฟเวอร์ Mastodon นี้",
+  "search_results.statuses_fts_disabled": "ไม่มีการเปิดใช้งานการค้นหาโพสต์โดยเนื้อหาของโพสต์ในเซิร์ฟเวอร์ Mastodon นี้",
   "search_results.total": "{count, number} {count, plural, other {ผลลัพธ์}}",
   "status.admin_account": "เปิดส่วนติดต่อการควบคุมสำหรับ @{name}",
   "status.admin_status": "เปิดสถานะนี้ในส่วนติดต่อการควบคุม",
   "status.block": "ปิดกั้น @{name}",
+  "status.bookmark": "เพิ่มที่คั่นหน้า",
   "status.cancel_reblog_private": "เลิกดัน",
   "status.cannot_reblog": "ไม่สามารถดันโพสต์นี้",
   "status.copy": "คัดลอกลิงก์ไปยังสถานะ",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} ได้ดัน",
   "status.reblogs.empty": "ยังไม่มีใครดันโพสต์นี้ เมื่อใครสักคนดัน เขาจะปรากฏที่นี่",
   "status.redraft": "ลบแล้วร่างใหม่",
+  "status.remove_bookmark": "เอาที่คั่นหน้าออก",
   "status.reply": "ตอบกลับ",
   "status.replyAll": "ตอบกลับกระทู้",
   "status.report": "รายงาน @{name}",
@@ -378,7 +391,7 @@
   "status.show_more": "แสดงเพิ่มเติม",
   "status.show_more_all": "แสดงเพิ่มเติมทั้งหมด",
   "status.show_thread": "แสดงกระทู้",
-  "status.uncached_media_warning": "ไม่สามารถดูได้",
+  "status.uncached_media_warning": "ไม่พร้อมใช้งาน",
   "status.unmute_conversation": "เลิกปิดเสียงการสนทนา",
   "status.unpin": "ถอนหมุดจากโปรไฟล์",
   "suggestions.dismiss": "ยกเลิกข้อเสนอแนะ",
@@ -397,21 +410,24 @@
   "trends.trending_now": "กำลังนิยม",
   "ui.beforeunload": "แบบร่างของคุณจะหายไปหากคุณออกจาก Mastodon",
   "upload_area.title": "ลากแล้วปล่อยเพื่ออัปโหลด",
-  "upload_button.label": "เพิ่มสื่อ (JPEG, PNG, GIF, WebM, MP4, MOV)",
+  "upload_button.label": "เพิ่มสื่อ ({formats})",
   "upload_error.limit": "เกินขีดจำกัดการอัปโหลดไฟล์",
   "upload_error.poll": "ไม่อนุญาตให้อัปโหลดไฟล์กับการลงคะแนน",
+  "upload_form.audio_description": "อธิบายสำหรับผู้สูญเสียการได้ยิน",
   "upload_form.description": "อธิบายสำหรับผู้บกพร่องทางการมองเห็น",
   "upload_form.edit": "แก้ไข",
   "upload_form.undo": "ลบ",
+  "upload_form.video_description": "อธิบายสำหรับผู้สูญเสียการได้ยินหรือบกพร่องทางการมองเห็น",
   "upload_modal.analyzing_picture": "กำลังวิเคราะห์รูปภาพ…",
   "upload_modal.apply": "นำไปใช้",
-  "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog",
-  "upload_modal.detect_text": "ตรวจจับข้อความจากรูปภาพ",
+  "upload_modal.description_placeholder": "สุนัขจิ้งจอกสีน้ำตาลที่ว่องไวกระโดดข้ามสุนัขขี้เกียจ",
+  "upload_modal.detect_text": "ตรวจหาข้อความจากรูปภาพ",
   "upload_modal.edit_media": "แก้ไขสื่อ",
-  "upload_modal.hint": "คลิกหรือลากวงกลมในภาพเพื่อเลือกจุดโฟกัส ซึ่งจะทำให้ส่วนนั้นอยู่ในกรอบของภาพขนาดย่อตลอดเวลา",
+  "upload_modal.hint": "คลิกหรือลากวงกลมในตัวอย่างเพื่อเลือกจุดโฟกัส ซึ่งจะอยู่ในมุมมองของภาพขนาดย่อทั้งหมดเสมอ",
   "upload_modal.preview_label": "ตัวอย่าง ({ratio})",
   "upload_progress.label": "กำลังอัปโหลด...",
   "video.close": "ปิดวิดีโอ",
+  "video.download": "ดาวน์โหลดไฟล์",
   "video.exit_fullscreen": "ออกจากเต็มหน้าจอ",
   "video.expand": "ขยายวิดีโอ",
   "video.fullscreen": "เต็มหน้าจอ",
diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json
index 89efafd1d..11f9ff2ef 100644
--- a/app/javascript/mastodon/locales/tr.json
+++ b/app/javascript/mastodon/locales/tr.json
@@ -1,11 +1,12 @@
 {
   "account.add_or_remove_from_list": "Listelere ekle veya kaldır",
   "account.badges.bot": "Bot",
+  "account.badges.group": "Grup",
   "account.block": "@{name} adlı kişiyi engelle",
   "account.block_domain": "{domain} alanından her şeyi gizle",
   "account.blocked": "Engellenmiş",
   "account.cancel_follow_request": "Takip isteğini iptal et",
-  "account.direct": "Mesaj gönder : @{name}",
+  "account.direct": "Mesaj gönder @{name}",
   "account.domain_blocked": "Alan adı gizlendi",
   "account.edit_profile": "Profili düzenle",
   "account.endorse": "Profildeki özellik",
@@ -17,7 +18,7 @@
   "account.follows_you": "Seni takip ediyor",
   "account.hide_reblogs": "@{name} kişisinin yinelemelerini gizle",
   "account.last_status": "Son aktivite",
-  "account.link_verified_on": "Bu bağlantının mülkiyeti {date} tarihinde kontrol edildi",
+  "account.link_verified_on": "Bu bağlantının sahipliği {date} tarihinde kontrol edildi",
   "account.locked_info": "Bu hesabın gizlilik durumu kilitli olarak ayarlanmış. Sahibi, onu kimin takip edebileceğini elle inceliyor.",
   "account.media": "Medya",
   "account.mention": "@{name} kullanıcısından bahset",
@@ -35,13 +36,14 @@
   "account.unblock": "@{name} adlı kişinin engelini kaldır",
   "account.unblock_domain": "{domain} göster",
   "account.unendorse": "Profilde özellik yok",
-  "account.unfollow": "Takipi bırak",
+  "account.unfollow": "Takibi bırak",
   "account.unmute": "@{name} adlı kişinin sesini aç",
   "account.unmute_notifications": "@{name} adlı kişinin bildirimlerini aç",
   "alert.rate_limited.message": "Lütfen sonra tekrar deneyin {retry_time, time, medium}.",
   "alert.rate_limited.title": "Oran sınırlıdır",
   "alert.unexpected.message": "Beklenmedik bir hata oluştu.",
   "alert.unexpected.title": "Hay aksi!",
+  "announcement.announcement": "Duyuru",
   "autosuggest_hashtag.per_week": "Haftada {count}",
   "boost_modal.combo": "Bir daha ki sefere {combo} tuşuna basabilirsiniz",
   "bundle_column_error.body": "Bu bileşen yüklenirken bir şeyler ters gitti.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Bu bileşen yüklenirken bir şeyler ters gitti.",
   "bundle_modal_error.retry": "Tekrar deneyin",
   "column.blocks": "Engellenen kullanıcılar",
+  "column.bookmarks": "Yer imleri",
   "column.community": "Yerel zaman tüneli",
   "column.direct": "Doğrudan mesajlar",
   "column.directory": "Profillere göz at",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "Anket süresi",
   "compose_form.poll.option_placeholder": "Seçim {number}",
   "compose_form.poll.remove_option": "Bu seçimi kaldır",
+  "compose_form.poll.switch_to_multiple": "Birden çok seçeneğe izin vermek için anketi değiştir",
+  "compose_form.poll.switch_to_single": "Tek bir seçeneğe izin vermek için anketi değiştir",
   "compose_form.publish": "Tootla",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "Medyayı hassas olarak işaretle",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Burada hiç toot yok!",
   "empty_column.account_unavailable": "Profil kullanılamıyor",
   "empty_column.blocks": "Henüz bir kullanıcıyı engellemediniz.",
+  "empty_column.bookmarked_statuses": "Hiç işaretlediğiniz tootunuz yok. Bir tane olduğunda burada görünecek.",
   "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 mesajınız yok. Bir tane gönderdiğinizde veya aldığınızda burada görünecektir.",
   "empty_column.domain_blocks": "Henüz hiçbir gizli alan adı yok.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "susturulmuş kullanıcı listesini açmak için",
   "keyboard_shortcuts.my_profile": "profilinizi açmak için",
   "keyboard_shortcuts.notifications": "bildirimler sütununu açmak için",
+  "keyboard_shortcuts.open_media": "medyayı açmak için",
   "keyboard_shortcuts.pinned": "sabitlenmiş tootların listesini açmak için",
   "keyboard_shortcuts.profile": "yazarın profilini açmak için",
   "keyboard_shortcuts.reply": "cevaplamak için",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Bu kullanıcıdan bildirimler gizlensin mı?",
   "navigation_bar.apps": "Mobil uygulamalar",
   "navigation_bar.blocks": "Engellenen kullanıcılar",
+  "navigation_bar.bookmarks": "Yer imleri",
   "navigation_bar.community_timeline": "Yerel zaman tüneli",
   "navigation_bar.compose": "Yeni toot oluştur",
   "navigation_bar.direct": "Direkt Mesajlar",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Güvenlik",
   "notification.favourite": "{name} senin durumunu favorilere ekledi",
   "notification.follow": "{name} seni takip ediyor",
+  "notification.follow_request": "{name} sizi takip etme isteği gönderdi",
   "notification.mention": "{name} senden bahsetti",
+  "notification.own_poll": "Anketiniz sona erdi",
   "notification.poll": "Oy verdiğiniz bir anket bitti",
   "notification.reblog": "{name} senin durumunu boost etti",
   "notifications.clear": "Bildirimleri temizle",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Hızlı filtre çubuğu",
   "notifications.column_settings.filter_bar.show": "Göster",
   "notifications.column_settings.follow": "Yeni takipçiler:",
+  "notifications.column_settings.follow_request": "Yeni takip istekleri:",
   "notifications.column_settings.mention": "Bahsedilenler:",
   "notifications.column_settings.poll": "Anket sonuçları:",
   "notifications.column_settings.push": "Push bildirimleri",
@@ -344,6 +355,7 @@
   "status.admin_account": "@{name} için denetim arayüzünü açın",
   "status.admin_status": "Denetim arayüzünde bu durumu açın",
   "status.block": "Engelle : @{name}",
+  "status.bookmark": "Yer imlerine ekle",
   "status.cancel_reblog_private": "Boost'u geri al",
   "status.cannot_reblog": "Bu gönderi boost edilemez",
   "status.copy": "Bağlantı durumunu kopyala",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} boost etti",
   "status.reblogs.empty": "Henüz kimse bu tootu yinelemedi. Biri yaptığında burada görünecek.",
   "status.redraft": "Sil & tekrar taslakla",
+  "status.remove_bookmark": "Yer imini kaldır",
   "status.reply": "Cevapla",
   "status.replyAll": "Konuşmayı cevapla",
   "status.report": "@{name}'i raporla",
@@ -400,9 +413,11 @@
   "upload_button.label": "Görsel ekle",
   "upload_error.limit": "Dosya yükleme sınırı aşıldı.",
   "upload_error.poll": "Anketlerde dosya yüklemesine izin verilmez.",
+  "upload_form.audio_description": "İşitme kaybı olan kişiler için tarif edin",
   "upload_form.description": "Görme engelliler için açıklama",
   "upload_form.edit": "Düzenle",
   "upload_form.undo": "Geri al",
+  "upload_form.video_description": "İşitme kaybı veya görme engeli olan kişiler için tarif edin",
   "upload_modal.analyzing_picture": "Resmi analiz ediyor…",
   "upload_modal.apply": "Uygula",
   "upload_modal.description_placeholder": "Pijamalı hasta yağız şoföre çabucak güvendi",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Ön izleme ({ratio})",
   "upload_progress.label": "Yükleniyor...",
   "video.close": "Videoyu kapat",
+  "video.download": "Dosyayı indir",
   "video.exit_fullscreen": "Tam ekrandan çık",
   "video.expand": "Videoyu genişlet",
   "video.fullscreen": "Tam ekran",
diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json
index c3b3e45b9..061fc50cb 100644
--- a/app/javascript/mastodon/locales/uk.json
+++ b/app/javascript/mastodon/locales/uk.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "Додати або видалити зі списків",
   "account.badges.bot": "Бот",
+  "account.badges.group": "Група",
   "account.block": "Заблокувати @{name}",
   "account.block_domain": "Заглушити {domain}",
   "account.blocked": "Заблоковані",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "Швидкість обмежена",
   "alert.unexpected.message": "Трапилась неочікувана помилка.",
   "alert.unexpected.title": "Ой!",
+  "announcement.announcement": "Оголошення",
   "autosuggest_hashtag.per_week": "{count} в тиждень",
   "boost_modal.combo": "Ви можете натиснути {combo}, щоб пропустити це наступного разу",
   "bundle_column_error.body": "Щось пішло не так під час завантаження компоненту.",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "Щось пішло не так під час завантаження компоненту.",
   "bundle_modal_error.retry": "Спробувати ще раз",
   "column.blocks": "Заблоковані користувачі",
+  "column.bookmarks": "Bookmarks",
   "column.community": "Локальна стрічка",
   "column.direct": "Прямі повідомлення",
   "column.directory": "Переглянути профілі",
@@ -82,6 +85,8 @@
   "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_loud": "{publish}!",
   "compose_form.sensitive.hide": "Позначити медіа як дражливе",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "Тут дмухалок немає!",
   "empty_column.account_unavailable": "Профіль недоступний",
   "empty_column.blocks": "Ви ще не заблокували жодного користувача.",
+  "empty_column.bookmarked_statuses": "У вас ще немає дмухів у закладках. Коли ви щось додасте до заклкдок, воно з'явиться тут.",
   "empty_column.community": "Локальна стрічка пуста. Напишіть щось, щоб розігріти народ!",
   "empty_column.direct": "У вас ще немає прямих повідомлень. Коли ви відправите чи отримаєте якесь, воно з'явиться тут.",
   "empty_column.domain_blocks": "Тут поки немає прихованих доменів.",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "відкрити список заглушених користувачів",
   "keyboard_shortcuts.my_profile": "відкрити ваш профіль",
   "keyboard_shortcuts.notifications": "відкрити колонку сповіщень",
+  "keyboard_shortcuts.open_media": "відкрити медіа",
   "keyboard_shortcuts.pinned": "відкрити список закріплених дмухів",
   "keyboard_shortcuts.profile": "відкрити профіль автора",
   "keyboard_shortcuts.reply": "відповісти",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Приховати сповіщення від користувача?",
   "navigation_bar.apps": "Мобільні додатки",
   "navigation_bar.blocks": "Заблоковані користувачі",
+  "navigation_bar.bookmarks": "Закладки",
   "navigation_bar.community_timeline": "Локальна стрічка",
   "navigation_bar.compose": "Написати новий дмух",
   "navigation_bar.direct": "Прямі повідомлення",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Безпека",
   "notification.favourite": "{name} вподобав(-ла) ваш допис",
   "notification.follow": "{name} підписався(-лась) на Вас",
+  "notification.follow_request": "{name} хоче підписатися на Вас",
   "notification.mention": "{name} згадав(-ла) Вас",
+  "notification.own_poll": "Ваше опитування завершено",
   "notification.poll": "Опитування, у якому ви голосували, закінчилося",
   "notification.reblog": "{name} передмухнув(-ла) Ваш допис",
   "notifications.clear": "Очистити сповіщення",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Панель швидкого фільтру",
   "notifications.column_settings.filter_bar.show": "Показати",
   "notifications.column_settings.follow": "Нові підписники:",
+  "notifications.column_settings.follow_request": "Нові запити на підписку:",
   "notifications.column_settings.mention": "Згадки:",
   "notifications.column_settings.poll": "Результати опитування:",
   "notifications.column_settings.push": "Push-сповіщення",
@@ -344,6 +355,7 @@
   "status.admin_account": "Відкрити інтерфейс модерації для @{name}",
   "status.admin_status": "Відкрити цей статус в інтерфейсі модерації",
   "status.block": "Заблокувати @{name}",
+  "status.bookmark": "У закладки",
   "status.cancel_reblog_private": "Відмінити передмухання",
   "status.cannot_reblog": "Цей допис не може бути передмухнутий",
   "status.copy": "Копіювати посилання до статусу",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} передмухнув(-ла)",
   "status.reblogs.empty": "Ніхто ще не передмухнув цього дмуху. Коли якісь користувачі це зроблять, вони будуть відображені тут.",
   "status.redraft": "Видалити та перестворити",
+  "status.remove_bookmark": "Видалити закладку",
   "status.reply": "Відповісти",
   "status.replyAll": "Відповісти на ланцюжок",
   "status.report": "Поскаржитися на @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Додати медіаконтент ({formats})",
   "upload_error.limit": "Ліміт завантаження файлів перевищено.",
   "upload_error.poll": "Не можна завантажувати файли до опитувань.",
+  "upload_form.audio_description": "Опишіть для людей із вадами слуху",
   "upload_form.description": "Опишіть для людей з вадами зору",
   "upload_form.edit": "Змінити",
   "upload_form.undo": "Видалити",
+  "upload_form.video_description": "Опишіть для людей із вадами слуху або зору",
   "upload_modal.analyzing_picture": "Аналізуємо малюнок…",
   "upload_modal.apply": "Застосувати",
   "upload_modal.description_placeholder": "Щурячий бугай із їжаком-харцизом в'ючись підписали ґешефт у єнах",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Переглянути ({ratio})",
   "upload_progress.label": "Завантаження...",
   "video.close": "Закрити відео",
+  "video.download": "Завантаження файла",
   "video.exit_fullscreen": "Вийти з повноекранного режиму",
   "video.expand": "Розширити відео",
   "video.fullscreen": "На весь екран",
diff --git a/app/javascript/mastodon/locales/ur.json b/app/javascript/mastodon/locales/ur.json
index 39ca86a0c..01477906c 100644
--- a/app/javascript/mastodon/locales/ur.json
+++ b/app/javascript/mastodon/locales/ur.json
@@ -1,91 +1,96 @@
 {
-  "account.add_or_remove_from_list": "Add or Remove from lists",
-  "account.badges.bot": "Bot",
-  "account.block": "Block @{name}",
-  "account.block_domain": "Hide everything from {domain}",
-  "account.blocked": "Blocked",
-  "account.cancel_follow_request": "Cancel follow request",
-  "account.direct": "Direct message @{name}",
-  "account.domain_blocked": "Domain hidden",
-  "account.edit_profile": "Edit profile",
-  "account.endorse": "Feature on profile",
-  "account.follow": "Follow",
-  "account.followers": "Followers",
-  "account.followers.empty": "No one follows this user yet.",
-  "account.follows": "Follows",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
-  "account.follows_you": "Follows you",
-  "account.hide_reblogs": "Hide boosts from @{name}",
-  "account.last_status": "Last active",
-  "account.link_verified_on": "Ownership of this link was checked on {date}",
-  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
-  "account.media": "Media",
-  "account.mention": "Mention @{name}",
-  "account.moved_to": "{name} has moved to:",
-  "account.mute": "Mute @{name}",
-  "account.mute_notifications": "Mute notifications from @{name}",
-  "account.muted": "Muted",
-  "account.never_active": "Never",
-  "account.posts": "Toots",
-  "account.posts_with_replies": "Toots and replies",
-  "account.report": "Report @{name}",
-  "account.requested": "Awaiting approval",
-  "account.share": "Share @{name}'s profile",
-  "account.show_reblogs": "Show boosts from @{name}",
-  "account.unblock": "Unblock @{name}",
-  "account.unblock_domain": "Unhide {domain}",
-  "account.unendorse": "Don't feature on profile",
-  "account.unfollow": "Unfollow",
-  "account.unmute": "Unmute @{name}",
-  "account.unmute_notifications": "Unmute notifications from @{name}",
-  "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.",
+  "account.add_or_remove_from_list": "فہرست میں شامل یا برطرف کریں",
+  "account.badges.bot": "روبوٹ",
+  "account.badges.group": "Group",
+  "account.block": "مسدود @{name}",
+  "account.block_domain": "{domain} سے سب چھپائیں",
+  "account.blocked": "مسدود کردہ",
+  "account.cancel_follow_request": "درخواستِ پیروی منسوخ کریں",
+  "account.direct": "راست پیغام @{name}",
+  "account.domain_blocked": "پوشیدہ ڈومین",
+  "account.edit_profile": "مشخص ترمیم کریں",
+  "account.endorse": "مشکص پر نمایاں کریں",
+  "account.follow": "پیروی کریں",
+  "account.followers": "پیروکار",
+  "account.followers.empty": "\"ہنوز اس صارف کی کوئی پیروی نہیں کرتا\".",
+  "account.follows": "پیروی کرتے ہیں",
+  "account.follows.empty": "\"یہ صارف ہنوز کسی کی پیروی نہیں کرتا ہے\".",
+  "account.follows_you": "آپ کا پیروکار ہے",
+  "account.hide_reblogs": "@{name} سے فروغ چھپائیں",
+  "account.last_status": "آخری فعال",
+  "account.link_verified_on": "اس لنک کی ملکیت کی توثیق {date} پر کی گئی تھی",
+  "account.locked_info": "اس اکاونٹ کا اخفائی ضابطہ مقفل ہے۔ صارف کی پیروی کون کر سکتا ہے اس کا جائزہ وہ خود لیتا ہے.",
+  "account.media": "وسائل",
+  "account.mention": "ذکر @{name}",
+  "account.moved_to": "{name} منتقل ہگیا ہے بہ:",
+  "account.mute": "خاموش @{name}",
+  "account.mute_notifications": "@{name} سے اطلاعات خاموش کریں",
+  "account.muted": "خاموش کردہ",
+  "account.never_active": "کبھی نہیں",
+  "account.posts": "ٹوٹ",
+  "account.posts_with_replies": "ٹوٹ اور جوابات",
+  "account.report": "@{name} اطلاع کریں",
+  "account.requested": "منظوری کا منتظر۔ درخواستِ پیروی منسوخ کرنے کیلئے کلک کریں",
+  "account.share": "@{name} کے مشخص کو بانٹیں",
+  "account.show_reblogs": "@{name} کی افزائشات کو دکھائیں",
+  "account.unblock": "@{name} کو بحال کریں",
+  "account.unblock_domain": "{domain} کو نہ چھپائیں",
+  "account.unendorse": "مشخص پر نمایاں نہ کریں",
+  "account.unfollow": "پیروی ترک کریں",
+  "account.unmute": "@{name} کو با آواز کریں",
+  "account.unmute_notifications": "@{name} سے اطلاعات کو با آواز کریں",
+  "alert.rate_limited.message": "\"{retry_time, time, medium} کے بعد کوشش کریں\".",
   "alert.rate_limited.title": "Rate limited",
-  "alert.unexpected.message": "An unexpected error occurred.",
-  "alert.unexpected.title": "Oops!",
-  "autosuggest_hashtag.per_week": "{count} per week",
-  "boost_modal.combo": "You can press {combo} to skip this next time",
-  "bundle_column_error.body": "Something went wrong while loading this component.",
-  "bundle_column_error.retry": "Try again",
-  "bundle_column_error.title": "Network error",
-  "bundle_modal_error.close": "Close",
-  "bundle_modal_error.message": "Something went wrong while loading this component.",
-  "bundle_modal_error.retry": "Try again",
-  "column.blocks": "Blocked users",
-  "column.community": "Local timeline",
-  "column.direct": "Direct messages",
-  "column.directory": "Browse profiles",
-  "column.domain_blocks": "Hidden domains",
-  "column.favourites": "Favourites",
-  "column.follow_requests": "Follow requests",
-  "column.home": "Home",
-  "column.lists": "Lists",
-  "column.mutes": "Muted users",
-  "column.notifications": "Notifications",
-  "column.pins": "Pinned toot",
-  "column.public": "Federated timeline",
-  "column_back_button.label": "Back",
-  "column_header.hide_settings": "Hide settings",
-  "column_header.moveLeft_settings": "Move column to the left",
-  "column_header.moveRight_settings": "Move column to the right",
-  "column_header.pin": "Pin",
-  "column_header.show_settings": "Show settings",
-  "column_header.unpin": "Unpin",
-  "column_subheading.settings": "Settings",
-  "community.column_settings.media_only": "Media only",
-  "compose_form.direct_message_warning": "This toot will only be sent to all the mentioned users.",
-  "compose_form.direct_message_warning_learn_more": "Learn more",
-  "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
-  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
-  "compose_form.lock_disclaimer.lock": "locked",
-  "compose_form.placeholder": "What is on your mind?",
-  "compose_form.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.publish": "Toot",
+  "alert.unexpected.message": "ایک غیر متوقع سہو ہوا ہے.",
+  "alert.unexpected.title": "ا رے!",
+  "announcement.announcement": "Announcement",
+  "autosuggest_hashtag.per_week": "{count} فی ہفتہ",
+  "boost_modal.combo": "آئیندہ یہ نہ دیکھنے کیلئے آپ {combo} دبا سکتے ہیں",
+  "bundle_column_error.body": "اس عنصر کو برآمد کرتے وقت کچھ خرابی پیش آئی ہے.",
+  "bundle_column_error.retry": "دوبارہ کوشش کریں",
+  "bundle_column_error.title": "نیٹ ورک کی خرابی",
+  "bundle_modal_error.close": "بند کریں",
+  "bundle_modal_error.message": "اس عنصر کو برآمد کرتے وقت کچھ خرابی پیش آئی ہے.",
+  "bundle_modal_error.retry": "دوبارہ کوشش کریں",
+  "column.blocks": "مسدود صارفین",
+  "column.bookmarks": "Bookmarks",
+  "column.community": "مقامی زمانی جدول",
+  "column.direct": "راست پیغام",
+  "column.directory": "مشخصات کا مطالعہ کریں",
+  "column.domain_blocks": "پوشیدہ ڈومین",
+  "column.favourites": "پسندیدہ",
+  "column.follow_requests": "پیروی درخواست",
+  "column.home": "خانہ",
+  "column.lists": "فہرستیں",
+  "column.mutes": "خاموش کردہ صارفین",
+  "column.notifications": "اطلاعات",
+  "column.pins": "چسپاں ٹوٹ",
+  "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.media_only": "وسائل فقط",
+  "compose_form.direct_message_warning": "یہ ٹوٹ صرف مذکورہ صارفین کو بھیجا جائے گا.",
+  "compose_form.direct_message_warning_learn_more": "مزید جانیں",
+  "compose_form.hashtag_warning": "چونکہ یہ ٹوٹ غیر مندرجہ ہے لہذا یہ کسی بھی ہیش ٹیگ کے تحت درج نہیں کیا جائے گا. ہیش ٹیگ کے تحت صرف \nعمومی ٹوٹ تلاش کئے جا سکتے ہیں.",
+  "compose_form.lock_disclaimer": "آپ کا اکاؤنٹ {locked} نہیں ہے. کوئی بھی آپ کے مخصوص برائے پیروکار ٹوٹ دیکھنے کی خاطر آپ کی پیروی کر سکتا ہے.",
+  "compose_form.lock_disclaimer.lock": "مقفل",
+  "compose_form.placeholder": "آپ کیا سوچ رہے ہیں؟",
+  "compose_form.poll.add_option": "انتخاب شامل کریں",
+  "compose_form.poll.duration": "مدتِ رائے",
+  "compose_form.poll.option_placeholder": "انتخاب {number}",
+  "compose_form.poll.remove_option": "یہ انتخاب ہٹا دیں",
+  "compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices",
+  "compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
+  "compose_form.publish": "ٹوٹ",
   "compose_form.publish_loud": "{publish}!",
-  "compose_form.sensitive.hide": "Mark media as sensitive",
-  "compose_form.sensitive.marked": "Media is marked as sensitive",
+  "compose_form.sensitive.hide": "وسائل کو حساس نشاندہ کریں",
+  "compose_form.sensitive.marked": "وسائل حساس نشاندہ ہے",
   "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
   "compose_form.spoiler.marked": "Text is hidden behind warning",
   "compose_form.spoiler.unmarked": "Text is not hidden",
@@ -135,54 +140,55 @@
   "emoji_button.search_results": "Search results",
   "emoji_button.symbols": "Symbols",
   "emoji_button.travel": "Travel & Places",
-  "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.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
-  "empty_column.domain_blocks": "There are no hidden domains yet.",
-  "empty_column.favourited_statuses": "You don't have any favourite toots yet. When you favourite one, it will show up here.",
-  "empty_column.favourites": "No one has favourited this toot yet. When someone does, they will show up here.",
-  "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
-  "empty_column.hashtag": "There is nothing in this hashtag yet.",
-  "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
-  "empty_column.home.public_timeline": "the public timeline",
-  "empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.",
-  "empty_column.lists": "You don't have any lists yet. When you create one, it will show up here.",
-  "empty_column.mutes": "You haven't muted any users yet.",
-  "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
-  "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other servers to fill it up",
+  "empty_column.account_timeline": "یہاں کوئی نوٹس نہیں ہیں!",
+  "empty_column.account_unavailable": "مشخص دستیاب نہیں ہے",
+  "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": "آپ کیلئے کوئی راست پیغام نہیں ہے. جب آپ بھیجیں گے یا وصول کریں گے، یہاں نظر آئےگا.",
+  "empty_column.domain_blocks": "ابھی تک کوئی چھپا ہوا ڈومین نہیں ہے.",
+  "empty_column.favourited_statuses": "آپ کا کوئی پسندیدہ ٹوٹ نہیں ہے. جب آپ پسند کریں گے، یہاں نظر آئےگا.",
+  "empty_column.favourites": "ابھی تک کسی نے بھی اس ٹوٹ کو پسند نہیں کیا ہے. جب بھی کوئی اسے پسند کرے گا، ان کا نام یہاں نظر آئے گا.",
+  "empty_column.follow_requests": "ابھی تک آپ کی پیری کرنے کی درخواست نہیں کی ہے. جب کوئی درخواست کرے گا، ان کا نام یہاں نظر آئے گا.",
+  "empty_column.hashtag": "ابھی یہ ہیش ٹیگ خالی ہے.",
+  "empty_column.home": "آپ کا خانگی جدول خالی ہے! {public} دیکھیں یا شروعات کیلئے تلاش کریں اور دیگر صارفین سے ملیں.",
+  "empty_column.home.public_timeline": "جدولِ عام",
+  "empty_column.list": "یہ فہرست ابھی خالی ہے. جب اس فہرست کے ارکان کچھ تحریر کریں گے، یہاں نظر آئے گا.",
+  "empty_column.lists": "ابھی آپ کی کوئی فہرست نہیں ہے. جب آپ بنائیں گے، وہ یہاں نظر آئے گی.",
+  "empty_column.mutes": "آپ نے ابھی کسی صارف کو خاموش نہیں کیا ہے.",
+  "empty_column.notifications": "ابھی آپ کیلئے کوئی اطلاعات نہیں ہیں. گفتگو شروع کرنے کے لئے دیگر صارفین سے متعامل ہوں.",
+  "empty_column.public": "یہاں کچھ بھی نہیں ہے! کچھ عمومی تحریر کریں یا اس جگہ کو پُر کرنے کے لئے از خود دیگر سرورس کے صارفین کی پیروی کریں",
   "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.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": "صفحے کو تازہ کرنے کی کوشش کریں. اگر کارآمد نہ ہو تو آپ کسی دیگر براؤزر یا مقامی ایپ سے ہنوز ماسٹوڈون استعمال کر سکتے ہیں.",
   "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard",
-  "errors.unexpected_crash.report_issue": "Report issue",
-  "follow_request.authorize": "Authorize",
-  "follow_request.reject": "Reject",
+  "errors.unexpected_crash.report_issue": "مسئلہ کی اطلاع کریں",
+  "follow_request.authorize": "اجازت دیں",
+  "follow_request.reject": "انکار کریں",
   "getting_started.developers": "Developers",
-  "getting_started.directory": "Profile directory",
-  "getting_started.documentation": "Documentation",
-  "getting_started.heading": "Getting started",
-  "getting_started.invite": "Invite people",
-  "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}.",
-  "getting_started.security": "Security",
-  "getting_started.terms": "Terms of service",
-  "hashtag.column_header.tag_mode.all": "and {additional}",
-  "hashtag.column_header.tag_mode.any": "or {additional}",
-  "hashtag.column_header.tag_mode.none": "without {additional}",
-  "hashtag.column_settings.select.no_options_message": "No suggestions found",
-  "hashtag.column_settings.select.placeholder": "Enter hashtags…",
-  "hashtag.column_settings.tag_mode.all": "All of these",
-  "hashtag.column_settings.tag_mode.any": "Any of these",
-  "hashtag.column_settings.tag_mode.none": "None of these",
-  "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
-  "home.column_settings.basic": "Basic",
-  "home.column_settings.show_reblogs": "Show boosts",
-  "home.column_settings.show_replies": "Show replies",
-  "intervals.full.days": "{number, plural, one {# day} other {# days}}",
-  "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
+  "getting_started.directory": "فہرست مشخصات",
+  "getting_started.documentation": "اسناد",
+  "getting_started.heading": "آغاز کریں",
+  "getting_started.invite": "دوستوں کو دعوت دیں",
+  "getting_started.open_source_notice": "ماسٹوڈون آزاد منبع سوفٹویر ہے. آپ {github} گِٹ ہب پر مسائل میں معاونت یا مشکلات کی اطلاع دے سکتے ہیں.",
+  "getting_started.security": "ترتیباتِ اکاؤنٹ",
+  "getting_started.terms": "شرائط خدمات",
+  "hashtag.column_header.tag_mode.all": "اور {additional}",
+  "hashtag.column_header.tag_mode.any": "یا {additional}",
+  "hashtag.column_header.tag_mode.none": "بغیر {additional}",
+  "hashtag.column_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": "اس کالم کے لئے مزید ٹیگز شامل کریں",
+  "home.column_settings.basic": "بنیادی",
+  "home.column_settings.show_reblogs": "افزائشات دکھائیں",
+  "home.column_settings.show_replies": "جوابات دکھائیں",
+  "intervals.full.days": "{number, plural, one {# روز} other {# روز}}",
+  "intervals.full.hours": "{number, plural, one {# ساعت} other {# ساعت}}",
   "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}",
-  "introduction.federation.action": "Next",
-  "introduction.federation.federated.headline": "Federated",
+  "introduction.federation.action": "اگلا",
+  "introduction.federation.federated.headline": "مجموعی",
   "introduction.federation.federated.text": "Public posts from other servers of the fediverse will appear in the federated timeline.",
   "introduction.federation.home.headline": "Home",
   "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "to open muted users list",
   "keyboard_shortcuts.my_profile": "to open your profile",
   "keyboard_shortcuts.notifications": "to open notifications column",
+  "keyboard_shortcuts.open_media": "to open media",
   "keyboard_shortcuts.pinned": "to open pinned toots list",
   "keyboard_shortcuts.profile": "to open author's profile",
   "keyboard_shortcuts.reply": "to reply",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blocked users",
+  "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "Security",
   "notification.favourite": "{name} favourited your status",
   "notification.follow": "{name} followed you",
+  "notification.follow_request": "{name} has requested to follow you",
   "notification.mention": "{name} mentioned you",
+  "notification.own_poll": "Your poll has ended",
   "notification.poll": "A poll you have voted in has ended",
   "notification.reblog": "{name} boosted your status",
   "notifications.clear": "Clear notifications",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "Quick filter bar",
   "notifications.column_settings.filter_bar.show": "Show",
   "notifications.column_settings.follow": "New followers:",
+  "notifications.column_settings.follow_request": "New follow requests:",
   "notifications.column_settings.mention": "Mentions:",
   "notifications.column_settings.poll": "Poll results:",
   "notifications.column_settings.push": "Push notifications",
@@ -344,6 +355,7 @@
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_status": "Open this status in the moderation interface",
   "status.block": "Block @{name}",
+  "status.bookmark": "Bookmark",
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.copy": "Copy link to status",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} boosted",
   "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
   "status.redraft": "Delete & re-draft",
+  "status.remove_bookmark": "Remove bookmark",
   "status.reply": "Reply",
   "status.replyAll": "Reply to thread",
   "status.report": "Report @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "Add media ({formats})",
   "upload_error.limit": "File upload limit exceeded.",
   "upload_error.poll": "File upload not allowed with polls.",
+  "upload_form.audio_description": "Describe for people with hearing loss",
   "upload_form.description": "Describe for the visually impaired",
   "upload_form.edit": "Edit",
   "upload_form.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.description_placeholder": "A quick brown fox jumps over the lazy dog",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "Preview ({ratio})",
   "upload_progress.label": "Uploading…",
   "video.close": "Close video",
+  "video.download": "Download file",
   "video.exit_fullscreen": "Exit full screen",
   "video.expand": "Expand video",
   "video.fullscreen": "Full screen",
diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json
new file mode 100644
index 000000000..07dff79fa
--- /dev/null
+++ b/app/javascript/mastodon/locales/vi.json
@@ -0,0 +1,439 @@
+{
+  "account.add_or_remove_from_list": "Thêm hoặc Xóa khỏi danh sách",
+  "account.badges.bot": "Robot",
+  "account.badges.group": "Nhóm",
+  "account.block": "Chặn @{name}",
+  "account.block_domain": "Ẩn mọi thứ từ {domain}",
+  "account.blocked": "Đã chặn",
+  "account.cancel_follow_request": "Hủy theo yêu cầu",
+  "account.direct": "Nhắn riêng @{name}",
+  "account.domain_blocked": "Miền đã ẩn",
+  "account.edit_profile": "Sửa hồ sơ",
+  "account.endorse": "Đưa lên hồ sơ",
+  "account.follow": "Quan tâm",
+  "account.followers": "Người quan tâm",
+  "account.followers.empty": "Chưa có ai quan tâm",
+  "account.follows": "Đang quan tâm",
+  "account.follows.empty": "Chưa quan tâm ai",
+  "account.follows_you": "Đã quan tâm bạn",
+  "account.hide_reblogs": "Ẩn loan tin từ @{name}",
+  "account.last_status": "Hoạt động lần cuối",
+  "account.link_verified_on": "Quyền sở hữu của liên kết này đã được kiểm tra vào {date}",
+  "account.locked_info": "Tài khoản này đã mở chế độ ẩn. Người sở hữu tài khoản này sẽ tự mình chấp nhận các yêu cầu quan tâm.",
+  "account.media": "Ảnh và video",
+  "account.mention": "Nhắc đến @{name}",
+  "account.moved_to": "{name} đã chuyển đến:",
+  "account.mute": "Ẩn @{name}",
+  "account.mute_notifications": "Tắt thông báo từ @{name}",
+  "account.muted": "Đã ẩn",
+  "account.never_active": "Không bao giờ",
+  "account.posts": "Tút",
+  "account.posts_with_replies": "Tút và trả lời",
+  "account.report": "Báo cáo @{name}",
+  "account.requested": "Đang chờ chấp nhận. Nhấn vào đây để hủy bỏ yêu cầu sau",
+  "account.share": "Chia sẻ hồ sơ @{name}",
+  "account.show_reblogs": "Hiện loan tin các từ @{name}",
+  "account.unblock": "Mở chặn @{name}",
+  "account.unblock_domain": "Bỏ ẩn {domain}",
+  "account.unendorse": "Không đưa lên nổi bật",
+  "account.unfollow": "Ngưng quan tâm",
+  "account.unmute": "Huỷ bỏ qua @{name}",
+  "account.unmute_notifications": "Hiển thị thông báo từ @{name}",
+  "alert.rate_limited.message": "Vui lòng thử lại sau {retry_time, time, Medium}.",
+  "alert.rate_limited.title": "Tỷ lệ giới hạn",
+  "alert.unexpected.message": "Đã xảy ra lỗi không mong muốn.",
+  "alert.unexpected.title": "Ốiii!",
+  "announcement.announcement": "Announcement",
+  "autosuggest_hashtag.per_week": "{tính} mỗi tuần",
+  "boost_modal.combo": "Bạn có thể nhấn {combo} để bỏ qua lần sau",
+  "bundle_column_error.body": "Có gì đó sai sai trong khi tải nội dung này",
+  "bundle_column_error.retry": "Thử lại",
+  "bundle_column_error.title": "Lỗi mạng",
+  "bundle_modal_error.close": "Tắt",
+  "bundle_modal_error.message": "Có gì đó sai sai trong khi tải nội dung này",
+  "bundle_modal_error.retry": "Thử lại",
+  "column.blocks": "Người bị chặn",
+  "column.bookmarks": "Đã lưu",
+  "column.community": "Dòng thời gian của miền",
+  "column.direct": "Tin nhắn",
+  "column.directory": "Duyệt hồ sơ",
+  "column.domain_blocks": "Miền đã ẩn",
+  "column.favourites": "Đã thích",
+  "column.follow_requests": "Yêu cầu quan tâm",
+  "column.home": "Bảng tin",
+  "column.lists": "Danh sách",
+  "column.mutes": "Đã ẩn",
+  "column.notifications": "Thông báo",
+  "column.pins": "Tút được ghim",
+  "column.public": "Dòng thời gian liên kết",
+  "column_back_button.label": "Quay lại",
+  "column_header.hide_settings": "Ẩn các thiết lập",
+  "column_header.moveLeft_settings": "Di chuyển cột sang trái",
+  "column_header.moveRight_settings": "Di chuyển cột sang phải",
+  "column_header.pin": "Ghim",
+  "column_header.show_settings": "Hiển thị cài đặt",
+  "column_header.unpin": "Bỏ ghim",
+  "column_subheading.settings": "Cài đặt",
+  "community.column_settings.media_only": "Chỉ tút có media",
+  "compose_form.direct_message_warning": "Tút này sẽ chỉ gửi cho người được nhắc đến.",
+  "compose_form.direct_message_warning_learn_more": "Tìm hiểu thêm",
+  "compose_form.hashtag_warning": "Tút này sẽ không được xuất hiện khi tìm kiếm theo hashtag vì nó không công khai. Chỉ Tút công khai có thể được tìm kiếm theo hashtag.",
+  "compose_form.lock_disclaimer": "Tài khoản của bạn không được {locked}. Bất cứ ai cũng có thể quan tâm bạn và xem bài viết đi bạn đăng cho người quan tâm",
+  "compose_form.lock_disclaimer.lock": "khóa",
+  "compose_form.placeholder": "Bạn đang nghĩ gì?",
+  "compose_form.poll.add_option": "Thêm lựa chọn",
+  "compose_form.poll.duration": "Thời hạn",
+  "compose_form.poll.option_placeholder": "Lựa chọn {number}",
+  "compose_form.poll.remove_option": "Xóa lựa chọn này",
+  "compose_form.poll.switch_to_multiple": "Sửa thăm dò để cho phép nhiều lựa chọn",
+  "compose_form.poll.switch_to_single": "Sửa thăm dò để cho phép một lựa chọn",
+  "compose_form.publish": "Tút",
+  "compose_form.publish_loud": "{publish}!",
+  "compose_form.sensitive.hide": "Đánh dấu hình ảnh nhạy cảm",
+  "compose_form.sensitive.marked": "Media đã được đánh dấu nhạy cảm",
+  "compose_form.sensitive.unmarked": "Media đã không được đánh dấu nhạy cảm",
+  "compose_form.spoiler.marked": "Văn bản ẩn dấu sau lời cảnh báo",
+  "compose_form.spoiler.unmarked": "Văn bản không bị ẩn",
+  "compose_form.spoiler_placeholder": "Viết cảnh báo của bạn ở đây",
+  "confirmation_modal.cancel": "Hủy bỏ",
+  "confirmations.block.block_and_report": "Chặn và Báo cáo",
+  "confirmations.block.confirm": "Chặn",
+  "confirmations.block.message": "Bạn có chắc chắn muốn chặn {name}?",
+  "confirmations.delete.confirm": "Xóa bỏ",
+  "confirmations.delete.message": "Bạn có chắc chắn muốn xóa tút này?",
+  "confirmations.delete_list.confirm": "Xóa bỏ",
+  "confirmations.delete_list.message": "Bạn có chắc chắn muốn xóa vĩnh viễn danh sách này?",
+  "confirmations.domain_block.confirm": "Ẩn toàn bộ miền",
+  "confirmations.domain_block.message": "Bạn có thực sự, thực sự chắc chắn rằng bạn muốn ẩn toàn bộ từ {domain}? Hầu hết các trường hợp, bạn chỉ cần chặn hoặc ẩn một vài tài khoản thì phù hợp hơn. Ẩn toàn bộ miền khiến bạn sẽ không thấy nội dung từ miền đó trong bất kỳ nơi nào kể cả thông báo. người quan tâm bạn từ miền đó sẽ bị xóa luôn.",
+  "confirmations.logout.confirm": "Đăng xuất",
+  "confirmations.logout.message": "Bạn có chắc chắn bạn muốn thoát?",
+  "confirmations.mute.confirm": "Ẩn",
+  "confirmations.mute.explanation": "Điều này sẽ ẩn bài đăng từ họ và bài viết đề cập đến họ, nhưng nó vẫn sẽ cho phép họ xem bài đăng của bạn và quan tâm bạn.",
+  "confirmations.mute.message": "Bạn có chắc chắn muốn ẩn {name}?",
+  "confirmations.redraft.confirm": "Xóa & đưa vào nháp",
+  "confirmations.redraft.message": "Bạn có chắc chắn muốn xóa tút này và đưa nó vào nháp? Những lượt thích và loan tin tút sẽ bị mất, và những tút trả lời tút này sẽ mất đầu mối.",
+  "confirmations.reply.confirm": "Trả lời",
+  "confirmations.reply.message": "Nội dung bạn đang soạn thảo sẽ bị ghi đè, bạn có tiếp tục?",
+  "confirmations.unfollow.confirm": "Ngưng quan tâm",
+  "confirmations.unfollow.message": "Bạn có chắc chắn muốn ngưng quan tâm {name}?",
+  "conversation.delete": "Xóa cuộc hội thoại",
+  "conversation.mark_as_read": "đánh dấu là đã đọc",
+  "conversation.open": "Xem cuộc hội thoại",
+  "conversation.with": "Với {tên}",
+  "directory.federated": "Từ fediverse được biết đến",
+  "directory.local": "Chỉ từ {domain}",
+  "directory.new_arrivals": "Điểm đến mới",
+  "directory.recently_active": "Mới hoạt động",
+  "embed.instructions": "Nhúng tút này trên trang web của bạn bằng cách sao chép đoạn mã dưới đây.",
+  "embed.preview": "Nó sẽ hiển thị như dưới đây:",
+  "emoji_button.activity": "Hoạt động",
+  "emoji_button.custom": "Riêng",
+  "emoji_button.flags": "Cờ",
+  "emoji_button.food": "Ăn uống",
+  "emoji_button.label": "Chèn biểu emoj",
+  "emoji_button.nature": "Thiên nhiên",
+  "emoji_button.not_found": "Không emojos !! (╯ ° □ °) ╯( ┻━┻",
+  "emoji_button.objects": "Đối tượng",
+  "emoji_button.people": "Người",
+  "emoji_button.recent": "Hay dùng",
+  "emoji_button.search": "Tìm kiếm...",
+  "emoji_button.search_results": "Kết quả tìm kiếm",
+  "emoji_button.symbols": "Biểu tượng",
+  "emoji_button.travel": "Du lịch",
+  "empty_column.account_timeline": "Chưa có tút nào",
+  "empty_column.account_unavailable": "Hồ sơ không truy cập được",
+  "empty_column.blocks": "Bạn chưa chặn ai cả.",
+  "empty_column.bookmarked_statuses": "Bạn chưa lưu tút nào, nếu có sẽ thấy ở đây.",
+  "empty_column.community": "Chưa có gì trong dòng thời gian chung của miền cả, bạn hãy tút gì đó đi!",
+  "empty_column.direct": "Bạn không có bất kỳ tin nhắn trực tiếp nêu ra. Khi bạn gửi hoặc nhận một, nó sẽ hiển thị ở đây.",
+  "empty_column.domain_blocks": "Chưa ẩn miền nào",
+  "empty_column.favourited_statuses": "Bạn chưa thích tút nào, nếu có sẽ thấy ở đây.",
+  "empty_column.favourites": "Chưa ai thích tút này, nếu có bạn sẽ thấy ở đây.",
+  "empty_column.follow_requests": "Bạn chưa có yêu cầu nào. Khi bạn nhận được yêu cầu, nó sẽ hiển ở đây.",
+  "empty_column.hashtag": "Chưa có bài đăng nào sử dụng hashtag này.",
+  "empty_column.home": "Bảng tin của bạn đang trống! Truy cập {public} hoặc sử dụng tìm kiếm để quan tâm và trao đổi với những người khác đi.",
+  "empty_column.home.public_timeline": "Dòng thời gian công khai",
+  "empty_column.list": "Không có gì trong danh sách này là nêu ra. Khi các thành viên của danh sách này gửi trạng thái mới, họ sẽ xuất hiện ở đây.",
+  "empty_column.lists": "Bạn không có bất kỳ danh sách được nêu ra. Khi bạn tạo một, nó sẽ hiển thị ở đây.",
+  "empty_column.mutes": "Bạn chưa tắt bất kỳ người dùng nào.",
+  "empty_column.notifications": "Bạn không có thông báo nào. Tương tác với những người khác để bắt đầu trao đổi.",
+  "empty_column.public": "Chưa có gì cả, bạn hãy đăng gì đó đi đừng để nó trắng trơn",
+  "error.unexpected_crash.explanation": "Do lỗi trong mã của chúng tôi hoặc sự cố tương thích trình duyệt, trang này không thể được hiển thị chính xác.",
+  "error.unexpected_crash.next_steps": "Hãy thử làm mới trang. Nếu điều đó không có ích, bạn vẫn có thể sử dụng Mastodon thông qua một trình duyệt hoặc ứng dụng gốc khác.",
+  "errors.unexpected_crash.copy_stacktrace": "Sao chép stacktrace vào clipboard",
+  "errors.unexpected_crash.report_issue": "Báo cáo phát hành",
+  "follow_request.authorize": "Cho phép",
+  "follow_request.reject": "Từ chối",
+  "getting_started.developers": "Nhà phát triển",
+  "getting_started.directory": "Danh mục thành viên",
+  "getting_started.documentation": "Tài liệu",
+  "getting_started.heading": "Bắt đầu",
+  "getting_started.invite": "Mời bạn bè",
+  "getting_started.open_source_notice": "Mastodon là phần mềm mã nguồn mở. Bạn có thể đóng góp hoặc báo cáo vấn đề gặp phải trên GitHub tại {github}.",
+  "getting_started.security": "Bảo mật",
+  "getting_started.terms": "Điều khoản dịch vụ",
+  "hashtag.column_header.tag_mode.all": "Và {additional} thêm",
+  "hashtag.column_header.tag_mode.any": "Hoặc {additional} thêm",
+  "hashtag.column_header.tag_mode.none": "Mà không {additional} thêm",
+  "hashtag.column_settings.select.no_options_message": "Không tìm thấy đề xuất nào",
+  "hashtag.column_settings.select.placeholder": "Nhập hashtags ...",
+  "hashtag.column_settings.tag_mode.all": "Tất cả những cái này",
+  "hashtag.column_settings.tag_mode.any": "Bất kỳ trong số này",
+  "hashtag.column_settings.tag_mode.none": "Không ai trong số những",
+  "hashtag.column_settings.tag_toggle": "Bao gồm thêm thẻ cho cột này",
+  "home.column_settings.basic": "Cơ bản",
+  "home.column_settings.show_reblogs": "Hiện tút được loan tin",
+  "home.column_settings.show_replies": "Hiện trả lời",
+  "intervals.full.days": "{number} days",
+  "intervals.full.hours": "{number} hours",
+  "intervals.full.minutes": "{number} minutes",
+  "introduction.federation.action": "Tiếp",
+  "introduction.federation.federated.headline": "Miền liên kết",
+  "introduction.federation.federated.text": "Bài đăng công khai từ các miền khác có liên hệ với miền của bạn sẽ xuất hiện ở Dòng thời gian liên kết",
+  "introduction.federation.home.headline": "Bảng tin",
+  "introduction.federation.home.text": "Bài đăng từ những người bạn quan tâm sẽ xuất hiện ở bảng tin của bạn. Bạn có thể quan tâm bất cứ ai trên bất cứ miền nào!",
+  "introduction.federation.local.headline": "Cùng miền",
+  "introduction.federation.local.text": "Bài đăng công khai từ những người trên cùng một miền như bạn sẽ xuất hiện trong Dòng thời gian cùng miền.",
+  "introduction.interactions.action": "Hoàn thành hướng dẫn!",
+  "introduction.interactions.favourite.headline": "Thích",
+  "introduction.interactions.favourite.text": "Bạn có thể lưu tút để sau này xem lại, và để cho các tác giả biết rằng bạn thích tút của họ bằng cách bấm thích tút đó.",
+  "introduction.interactions.reblog.headline": "Loan tin",
+  "introduction.interactions.reblog.text": "Bạn có thể chia sẻ Tút của người khác với những người quan tâm bạn bằng cách loan tin.",
+  "introduction.interactions.reply.headline": "Trả lời",
+  "introduction.interactions.reply.text": "Bạn có thể trả lời tút của những người khác và tút riêng của bạn, từ đó tạo nên những cuộc hội thoại.",
+  "introduction.welcome.action": "Ok đi thôi!",
+  "introduction.welcome.headline": "Bước đầu tiên",
+  "introduction.welcome.text": "Chào mừng bạn đến mạng xã hội đa liên kết! Trong vài phút tới, bạn sẽ có thể phát đi những nội dung và trao đổi với bạn bè của bạn trên một loạt các trang hay còn gọi là các miền. Đặc biệt ở là {domain}, là nơi lưu trữ hồ sơ của bạn, vì vậy đừng quên miền này.",
+  "keyboard_shortcuts.back": "để trở lại",
+  "keyboard_shortcuts.blocked": "để mở danh sách người dùng bị chặn",
+  "keyboard_shortcuts.boost": "để loan tin",
+  "keyboard_shortcuts.column": "để tập trung một tút cụ thể từ một trong các cột",
+  "keyboard_shortcuts.compose": "để vào khung soạn tút",
+  "keyboard_shortcuts.description": "Mô tả",
+  "keyboard_shortcuts.direct": "để mở thư cột trực tiếp",
+  "keyboard_shortcuts.down": "để di chuyển xuống trong danh sách",
+  "keyboard_shortcuts.enter": "để mở trạng thái",
+  "keyboard_shortcuts.favourite": "để thích",
+  "keyboard_shortcuts.favourites": "để mở danh sách yêu thích",
+  "keyboard_shortcuts.federated": "để mở dòng thời gian liên kết",
+  "keyboard_shortcuts.heading": "Các phím tắt",
+  "keyboard_shortcuts.home": "để mở bảng tin",
+  "keyboard_shortcuts.hotkey": "Phím tắt",
+  "keyboard_shortcuts.legend": "để hiển thị bảng hướng dẫn này",
+  "keyboard_shortcuts.local": "để mở dòng thời gian của miền",
+  "keyboard_shortcuts.mention": "để nhắc đến tác giả",
+  "keyboard_shortcuts.muted": "để mở danh sách người dùng tắt tiếng",
+  "keyboard_shortcuts.my_profile": "để mở hồ sơ của bạn",
+  "keyboard_shortcuts.notifications": "để mở thông báo cột",
+  "keyboard_shortcuts.open_media": "để mở media",
+  "keyboard_shortcuts.pinned": "để mở danh sách tút được ghim",
+  "keyboard_shortcuts.profile": "để mở hồ sơ của tác giả",
+  "keyboard_shortcuts.reply": "để trả lời",
+  "keyboard_shortcuts.requests": "để mở danh sách các yêu cầu",
+  "keyboard_shortcuts.search": "để vào ô tìm kiếm",
+  "keyboard_shortcuts.start": "để mở cột \"Bắt đầu\"",
+  "keyboard_shortcuts.toggle_hidden": "để ẩn/hiện đằng sau văn bản CW",
+  "keyboard_shortcuts.toggle_sensitivity": "để hiển thị / ẩn media",
+  "keyboard_shortcuts.toot": "để viết tút mới",
+  "keyboard_shortcuts.unfocus": "để đưa con trỏ ra khỏi ô soạn thảo hoặc ô tìm kiếm",
+  "keyboard_shortcuts.up": "để di chuyển lên trong danh sách",
+  "lightbox.close": "Tắt",
+  "lightbox.next": "Tiếp",
+  "lightbox.previous": "Trước",
+  "lightbox.view_context": "Xem bối cảnh",
+  "lists.account.add": "Thêm vào danh sách",
+  "lists.account.remove": "Xóa khỏi danh sách",
+  "lists.delete": "Xóa danh sách",
+  "lists.edit": "Sửa danh sách",
+  "lists.edit.submit": "Thay đổi tiêu đề",
+  "lists.new.create": "Thêm vào danh sách",
+  "lists.new.title_placeholder": "Tên danh sách mới",
+  "lists.search": "Tìm kiếm trong số những người bạn quan tâm",
+  "lists.subheading": "Danh sách của bạn",
+  "load_pending": "{count, plural, other {}}",
+  "loading_indicator.label": "Đang tải...",
+  "media_gallery.toggle_visible": "Bật/tắt",
+  "missing_indicator.label": "Không tìm thấy",
+  "missing_indicator.sublabel": "Tài liệu này không thể được tìm thấy",
+  "mute_modal.hide_notifications": "Ẩn thông báo từ thành viên này?",
+  "navigation_bar.apps": "Ứng dụng di động",
+  "navigation_bar.blocks": "Người dùng bị chặn",
+  "navigation_bar.bookmarks": "Đã lưu",
+  "navigation_bar.community_timeline": "Dòng thời gian của miền",
+  "navigation_bar.compose": "Soạn tút mới",
+  "navigation_bar.direct": "Tin nhắn trực tiếp",
+  "navigation_bar.discover": "Khám phá",
+  "navigation_bar.domain_blocks": "Miền đã ẩn",
+  "navigation_bar.edit_profile": "Sửa hồ sơ cá nhân",
+  "navigation_bar.favourites": "Đã thích",
+  "navigation_bar.filters": "Nói cách tắt tiếng",
+  "navigation_bar.follow_requests": "Yêu cầu quan tâm",
+  "navigation_bar.follows_and_followers": "Đang quan tâm và được quan tâm",
+  "navigation_bar.info": "Giới thiệu",
+  "navigation_bar.keyboard_shortcuts": "Phím tắt",
+  "navigation_bar.lists": "Danh sách",
+  "navigation_bar.logout": "Đăng xuất",
+  "navigation_bar.mutes": "Người sử dụng tắt tiếng",
+  "navigation_bar.personal": "Cá nhân",
+  "navigation_bar.pins": "Tút được ghim",
+  "navigation_bar.preferences": "Tùy chỉnh",
+  "navigation_bar.public_timeline": "Dòng thời gian liên kết",
+  "navigation_bar.security": "Bảo mật",
+  "notification.favourite": "{name} favourited trạng thái của bạn",
+  "notification.follow": "{name} đã quan tâm bạn",
+  "notification.follow_request": "{name} đã yêu cầu theo dõi bạn",
+  "notification.mention": "{name} nhắc đến bạn",
+  "notification.own_poll": "Cuộc thăm dò của bạn đã kết thúc",
+  "notification.poll": "Một cuộc thăm dò bạn đã bỏ phiếu tại đã kết thúc",
+  "notification.reblog": "{name} loan tin tút của bạn",
+  "notifications.clear": "Xóa thông báo",
+  "notifications.clear_confirmation": "Bạn có chắc chắn muốn xóa vĩnh viễn tất cả các thông báo của mình?",
+  "notifications.column_settings.alert": "Thông báo trên máy tính",
+  "notifications.column_settings.favourite": "Đã thích:",
+  "notifications.column_settings.filter_bar.advanced": "Hiển thị tất cả các loại",
+  "notifications.column_settings.filter_bar.category": "Lọc nhanh",
+  "notifications.column_settings.filter_bar.show": "Hiện",
+  "notifications.column_settings.follow": "Người mới quan tâm :",
+  "notifications.column_settings.follow_request": "Yêu cầu theo dõi mới:",
+  "notifications.column_settings.mention": "Nhắc đến:",
+  "notifications.column_settings.poll": "Kết quả cuộc thăm dò:",
+  "notifications.column_settings.push": "Thông báo đẩy",
+  "notifications.column_settings.reblog": "Loan tin:",
+  "notifications.column_settings.show": "Hiện trong cột",
+  "notifications.column_settings.sound": "Mở tiếng",
+  "notifications.filter.all": "Toàn bộ",
+  "notifications.filter.boosts": "Loan tin",
+  "notifications.filter.favourites": "Đã thích",
+  "notifications.filter.follows": "Quan tâm",
+  "notifications.filter.mentions": "Nhắc đến",
+  "notifications.filter.polls": "Kết quả cuộc thăm dò",
+  "notifications.group": "{count} thông báo",
+  "poll.closed": "Đóng",
+  "poll.refresh": "Làm mới",
+  "poll.total_people": "{count, plural, other {}}",
+  "poll.total_votes": "{count, plural, other {}}",
+  "poll.vote": "Bỏ phiếu",
+  "poll.voted": "Bạn đã bình chọn cho câu trả lời này",
+  "poll_button.add_poll": "Tạo thăm dò",
+  "poll_button.remove_poll": "Bỏ thăm dò",
+  "privacy.change": "Điều chỉnh chế độ của tút",
+  "privacy.direct.long": "Chỉ gửi cho người được nhắc đến",
+  "privacy.direct.short": "Nhắn riêng",
+  "privacy.private.long": "Chỉ người quan tâm mới xem được",
+  "privacy.private.short": "Chỉ người quan tâm",
+  "privacy.public.long": "Đăng lên các dòng thời gian công khai",
+  "privacy.public.short": "Công khai",
+  "privacy.unlisted.long": "Không đăng lên các dòng thời gian công khai",
+  "privacy.unlisted.short": "Không công khai",
+  "refresh": "Làm tươi",
+  "regeneration_indicator.label": "Đang tải…",
+  "regeneration_indicator.sublabel": "Bảng tin của bạn đang được cập nhật!",
+  "relative_time.days": "{number} ngày",
+  "relative_time.hours": "{number} giờ",
+  "relative_time.just_now": "vừa xong",
+  "relative_time.minutes": "{number} phút",
+  "relative_time.seconds": "{number} giây",
+  "reply_indicator.cancel": "Hủy bỏ",
+  "report.forward": "Chuyển đến {target}",
+  "report.forward_hint": "Tài khoản này từ miền khác. Gửi thêm một báo cáo ẩn danh tới miền của tài khoản này?",
+  "report.hint": "Báo cáo này sẽ được gửi đến điều hành viên miền của bạn. Bạn có thể cung cấp lý do tại sao bạn lại báo cáo tài khoản vào này dưới đây:",
+  "report.placeholder": "Ý kiến ​​khác",
+  "report.submit": "Gửi đi",
+  "report.target": "Báo xấu {target}",
+  "search.placeholder": "Tìm kiếm",
+  "search_popout.search_format": "Tìm kiếm nâng cao theo cú pháp",
+  "search_popout.tips.full_text": "Không dùng ký tự đặc biệt sẽ trả về tút của bạn, tút bạn thích, tút bạn loan tin hoặc bạn được nhắc đến, kể cả tên tài khoản, tên hiển thị và hashtag.",
+  "search_popout.tips.hashtag": "hashtag",
+  "search_popout.tips.status": "tút",
+  "search_popout.tips.text": "không dùng ký tự đặc biệt sẽ trả về nội dung tương ứng, tên tài khoản và các hashtag.",
+  "search_popout.tips.user": "thành viên",
+  "search_results.accounts": "Người",
+  "search_results.hashtags": "Hashtags",
+  "search_results.statuses": "Tút",
+  "search_results.statuses_fts_disabled": "Tìm kiếm các tút theo nội dung của chúng không được kích hoạt trên trang Mastodon này.",
+  "search_results.total": "{đếm, số} {đếm, số nhiều, một {kết quả} khác {kết quả}}",
+  "status.admin_account": "Mở giao diện quản trị @{name}",
+  "status.admin_status": "Mở tút này trong giao diện quản trị",
+  "status.block": "Chặn @{name}",
+  "status.bookmark": "Lưu",
+  "status.cancel_reblog_private": "Hủy loan tin",
+  "status.cannot_reblog": "Không thể loan tin tút này",
+  "status.copy": "Sao chép liên kết đến tút",
+  "status.delete": "Xóa bỏ",
+  "status.detailed_status": "Chi tiết xem cuộc hội thoại",
+  "status.direct": "Nhắn riêng @{name}",
+  "status.embed": "Nhúng",
+  "status.favourite": "Thích",
+  "status.filtered": "Lọc qua",
+  "status.load_more": "Tải thêm",
+  "status.media_hidden": "Media đã ẩn",
+  "status.mention": "Nhắc đến @{name}",
+  "status.more": "Thêm nữa",
+  "status.mute": "Ẩn @{name}",
+  "status.mute_conversation": "Ẩn trao đổi",
+  "status.open": "Mở rộng tút này",
+  "status.pin": "Ghim trong hồ sơ",
+  "status.pinned": "Tút được ghim",
+  "status.read_more": "Đọc thêm",
+  "status.reblog": "Loan tin",
+  "status.reblog_private": "Loan tin đến những người có thể xem",
+  "status.reblogged_by": "{name} loan tin",
+  "status.reblogs.empty": "Chưa ai loan tin tút này, nếu có bạn sẽ thấy ở đây.",
+  "status.redraft": "Xóa & nháp lại",
+  "status.remove_bookmark": "Xóa",
+  "status.reply": "Trả lời",
+  "status.replyAll": "Trả lời chủ đề",
+  "status.report": "Báo cáo @{name}",
+  "status.sensitive_warning": "Nội dung nhạy cảm",
+  "status.share": "Chia sẻ",
+  "status.show_less": "Hiện ít hơn",
+  "status.show_less_all": "Hiển thị ít hơn cho tất cả",
+  "status.show_more": "Xem nhiều hơn",
+  "status.show_more_all": "Hiển thị thêm cho tất cả",
+  "status.show_thread": "Xem chủ đề",
+  "status.uncached_media_warning": "Không có sẵn",
+  "status.unmute_conversation": "Bỏ ẩn hội thoại",
+  "status.unpin": "Bỏ ghim khỏi hồ sơ",
+  "suggestions.dismiss": "Tắt đề xuất",
+  "suggestions.header": "Có thể bạn quan tâm…",
+  "tabs_bar.federated_timeline": "Miền liên kết",
+  "tabs_bar.home": "Bảng tin",
+  "tabs_bar.local_timeline": "Cùng miền",
+  "tabs_bar.notifications": "Thông báo",
+  "tabs_bar.search": "Tìm kiếm",
+  "time_remaining.days": "{number, plural, other {}} left",
+  "time_remaining.hours": "{number, plural, other {}} left",
+  "time_remaining.minutes": "{number, plural, other {}} left",
+  "time_remaining.moments": "Còn lại",
+  "time_remaining.seconds": "{number, plural, other {}} left",
+  "trends.count_by_accounts": "{Count} {rawCount, số nhiều, một {người} khác {người}} nói chuyện",
+  "trends.trending_now": "Đang là xu hướng",
+  "ui.beforeunload": "Bản nháp của bạn sẽ bị mất nếu bạn rời của Mastodon.",
+  "upload_area.title": "Kéo và thả để tải lên",
+  "upload_button.label": "Thêm media (JPEG, PNG, GIF, WebM, MP4, MOV)",
+  "upload_error.limit": "Giới hạn tải lên tập tin vượt quá.",
+  "upload_error.poll": "Không thể tải file lên cùng với thăm dò",
+  "upload_form.audio_description": "Mô tả cho người có vấn đề về thính giác",
+  "upload_form.description": "Mô tả cho người khiếm thị",
+  "upload_form.edit": "Biên tập",
+  "upload_form.undo": "Xóa bỏ",
+  "upload_form.video_description": "Mô tả cho người có vấn đề về thính giác",
+  "upload_modal.analyzing_picture": "Phân tích hình ảnh",
+  "upload_modal.apply": "Ứng dụng",
+  "upload_modal.description_placeholder": "Một con cáo nâu nhanh nhẹn nhảy qua con chó lười",
+  "upload_modal.detect_text": "Phát hiện văn bản từ hình ảnh",
+  "upload_modal.edit_media": "Chỉnh sửa phương tiện truyền thông",
+  "upload_modal.hint": "Nhấp hoặc kéo vòng tròn trên bản xem trước để chọn tiêu điểm sẽ luôn được xem trên tất cả các hình thu nhỏ.",
+  "upload_modal.preview_label": "Xem trước ({tỷ lệ})",
+  "upload_progress.label": "Đang tải lên ...",
+  "video.close": "Đóng video",
+  "video.download": "Download file",
+  "video.exit_fullscreen": "Thoát toàn màn hình",
+  "video.expand": "Mở rộng video",
+  "video.fullscreen": "Toàn màn hình",
+  "video.hide": "Ẩn video",
+  "video.mute": "Tắt tiếng",
+  "video.pause": "Pause",
+  "video.play": "Play",
+  "video.unmute": "Mở tiếng"
+}
diff --git a/app/javascript/mastodon/locales/whitelist_is.json b/app/javascript/mastodon/locales/whitelist_is.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_is.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/mastodon/locales/whitelist_kab.json b/app/javascript/mastodon/locales/whitelist_kab.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_kab.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/mastodon/locales/whitelist_vi.json b/app/javascript/mastodon/locales/whitelist_vi.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_vi.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json
index 550810680..62a86e75b 100644
--- a/app/javascript/mastodon/locales/zh-CN.json
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "从列表中添加或删除",
   "account.badges.bot": "机器人",
+  "account.badges.group": "Group",
   "account.block": "屏蔽 @{name}",
   "account.block_domain": "隐藏来自 {domain} 的内容",
   "account.blocked": "已屏蔽",
@@ -38,10 +39,11 @@
   "account.unfollow": "取消关注",
   "account.unmute": "不再隐藏 @{name}",
   "account.unmute_notifications": "不再隐藏来自 @{name} 的通知",
-  "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": "哎呀!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "每星期 {count} 条",
   "boost_modal.combo": "下次按住 {combo} 即可跳过此提示",
   "bundle_column_error.body": "载入这个组件时发生了错误。",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "载入这个组件时发生了错误。",
   "bundle_modal_error.retry": "重试",
   "column.blocks": "已屏蔽的用户",
+  "column.bookmarks": "书签",
   "column.community": "本站时间轴",
   "column.direct": "私信",
   "column.directory": "浏览用户资料",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "投票持续时间",
   "compose_form.poll.option_placeholder": "选项 {number}",
   "compose_form.poll.remove_option": "移除这个选项",
+  "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": "嘟嘟",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "标记媒体为敏感内容",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "这里没有嘟文!",
   "empty_column.account_unavailable": "个人资料不可用",
   "empty_column.blocks": "你目前没有屏蔽任何用户。",
+  "empty_column.bookmarked_statuses": "你还没有给任何嘟文添加过书签。在你添加书签后,嘟文就会显示在这里。",
   "empty_column.community": "本站时间轴暂时没有内容,快写点什么让它动起来吧!",
   "empty_column.direct": "你还没有使用过私信。当你发出或者收到私信时,它会在这里显示。",
   "empty_column.domain_blocks": "目前没有被隐藏的站点。",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "打开隐藏用户列表",
   "keyboard_shortcuts.my_profile": "打开你的个人资料",
   "keyboard_shortcuts.notifications": "打开通知栏",
+  "keyboard_shortcuts.open_media": "打开媒体",
   "keyboard_shortcuts.pinned": "打开置顶嘟文列表",
   "keyboard_shortcuts.profile": "打开作者的个人资料",
   "keyboard_shortcuts.reply": "回复嘟文",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "同时隐藏来自这个用户的通知?",
   "navigation_bar.apps": "移动应用",
   "navigation_bar.blocks": "已屏蔽的用户",
+  "navigation_bar.bookmarks": "书签",
   "navigation_bar.community_timeline": "本站时间轴",
   "navigation_bar.compose": "撰写新嘟文",
   "navigation_bar.direct": "私信",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "安全",
   "notification.favourite": "{name} 喜欢了你的嘟文",
   "notification.follow": "{name} 开始关注你",
+  "notification.follow_request": "{name} 向你发送了关注请求",
   "notification.mention": "{name} 提及了你",
+  "notification.own_poll": "您的投票已经结束",
   "notification.poll": "你参与的一个投票已经结束",
   "notification.reblog": "{name} 转嘟了你的嘟文",
   "notifications.clear": "清空通知列表",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "快速过滤栏",
   "notifications.column_settings.filter_bar.show": "显示",
   "notifications.column_settings.follow": "当有人关注你时:",
+  "notifications.column_settings.follow_request": "新的关注请求:",
   "notifications.column_settings.mention": "当有人在嘟文中提及你时:",
   "notifications.column_settings.poll": "投票结果:",
   "notifications.column_settings.push": "推送通知",
@@ -299,7 +310,7 @@
   "notifications.group": "{count} 条通知",
   "poll.closed": "已关闭",
   "poll.refresh": "刷新",
-  "poll.total_people": "{count, plural, one {# 人} other {# 人}}",
+  "poll.total_people": "{count}人",
   "poll.total_votes": "{count} 票",
   "poll.vote": "投票",
   "poll.voted": "您已经对这个答案投过票了",
@@ -316,14 +327,14 @@
   "privacy.unlisted.short": "不公开",
   "refresh": "刷新",
   "regeneration_indicator.label": "加载中……",
-  "regeneration_indicator.sublabel": "你的主页时间轴正在准备中!",
+  "regeneration_indicator.sublabel": "你的主页动态正在准备中!",
   "relative_time.days": "{number}天",
   "relative_time.hours": "{number}时",
   "relative_time.just_now": "刚刚",
   "relative_time.minutes": "{number}分",
   "relative_time.seconds": "{number}秒",
   "reply_indicator.cancel": "取消",
-  "report.forward": "发送举报至 {target}",
+  "report.forward": "转发举报至 {target}",
   "report.forward_hint": "这名用户来自另一个服务器。是否要向那个服务器发送一条匿名的举报?",
   "report.hint": "举报将会发送给你所在服务器的监察员。你可以在下面填写举报该用户的理由:",
   "report.placeholder": "备注",
@@ -344,6 +355,7 @@
   "status.admin_account": "打开 @{name} 的管理界面",
   "status.admin_status": "打开这条嘟文的管理界面",
   "status.block": "屏蔽 @{name}",
+  "status.bookmark": "添加到书签",
   "status.cancel_reblog_private": "取消转嘟",
   "status.cannot_reblog": "这条嘟文不允许被转嘟",
   "status.copy": "复制嘟文链接",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} 转嘟了",
   "status.reblogs.empty": "没有人转嘟过此条嘟文。如果有人转嘟了,就会显示在这里。",
   "status.redraft": "删除并重新编辑",
+  "status.remove_bookmark": "移除书签",
   "status.reply": "回复",
   "status.replyAll": "回复所有人",
   "status.report": "举报 @{name}",
@@ -400,9 +413,11 @@
   "upload_button.label": "上传媒体文件 (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_error.limit": "文件大小超过限制。",
   "upload_error.poll": "投票中不允许上传文件。",
+  "upload_form.audio_description": "为听障人士添加文字描述",
   "upload_form.description": "为视觉障碍人士添加文字说明",
   "upload_form.edit": "编辑",
   "upload_form.undo": "删除",
+  "upload_form.video_description": "为听障人士和视障人士添加文字描述",
   "upload_modal.analyzing_picture": "分析图片…",
   "upload_modal.apply": "应用",
   "upload_modal.description_placeholder": "天地玄黄 宇宙洪荒 日月盈仄 辰宿列张",
@@ -412,6 +427,7 @@
   "upload_modal.preview_label": "预览 ({ratio})",
   "upload_progress.label": "上传中……",
   "video.close": "关闭视频",
+  "video.download": "下载文件",
   "video.exit_fullscreen": "退出全屏",
   "video.expand": "展开视频",
   "video.fullscreen": "全屏",
diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json
index 3ca9f0e2e..4dcf70e6b 100644
--- a/app/javascript/mastodon/locales/zh-HK.json
+++ b/app/javascript/mastodon/locales/zh-HK.json
@@ -1,31 +1,32 @@
 {
-  "account.add_or_remove_from_list": "Add or Remove from lists",
+  "account.add_or_remove_from_list": "從名單中新增或移除",
   "account.badges.bot": "機械人",
+  "account.badges.group": "Group",
   "account.block": "封鎖 @{name}",
   "account.block_domain": "隱藏來自 {domain} 的一切文章",
   "account.blocked": "封鎖",
-  "account.cancel_follow_request": "Cancel follow request",
+  "account.cancel_follow_request": "取消關注請求",
   "account.direct": "私訊 @{name}",
   "account.domain_blocked": "服務站被隱藏",
   "account.edit_profile": "修改個人資料",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "在個人資料推薦對方",
   "account.follow": "關注",
   "account.followers": "關注的人",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers.empty": "尚沒有人關注這位使用者。",
   "account.follows": "正關注",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "這位使用者尚未關注任何使用者。",
   "account.follows_you": "關注你",
   "account.hide_reblogs": "隱藏 @{name} 的轉推",
-  "account.last_status": "Last active",
-  "account.link_verified_on": "Ownership of this link was checked on {date}",
-  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
+  "account.last_status": "上次活躍時間",
+  "account.link_verified_on": "此連結的所有權已在 {date} 檢查過",
+  "account.locked_info": "這隻帳戶的隱私狀態被設成鎖定。該擁有者會手動審核能關注這隻帳號的人。",
   "account.media": "媒體",
   "account.mention": "提及 @{name}",
   "account.moved_to": "{name} 已經遷移到:",
   "account.mute": "將 @{name} 靜音",
   "account.mute_notifications": "將來自 @{name} 的通知靜音",
   "account.muted": "靜音",
-  "account.never_active": "Never",
+  "account.never_active": "永不",
   "account.posts": "文章",
   "account.posts_with_replies": "包含回覆的文章",
   "account.report": "舉報 @{name}",
@@ -34,15 +35,16 @@
   "account.show_reblogs": "顯示 @{name} 的推文",
   "account.unblock": "解除對 @{name} 的封鎖",
   "account.unblock_domain": "不再隱藏 {domain}",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "不再於個人資料頁面推薦對方",
   "account.unfollow": "取消關注",
   "account.unmute": "取消 @{name} 的靜音",
   "account.unmute_notifications": "取消來自 @{name} 通知的靜音",
-  "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.",
-  "alert.rate_limited.title": "Rate limited",
+  "alert.rate_limited.message": "請在 {retry_time, time, medium} 過後重試",
+  "alert.rate_limited.title": "已限速",
   "alert.unexpected.message": "發生不可預期的錯誤。",
   "alert.unexpected.title": "噢!",
-  "autosuggest_hashtag.per_week": "{count} per week",
+  "announcement.announcement": "Announcement",
+  "autosuggest_hashtag.per_week": "{count} / 週",
   "boost_modal.combo": "如你想在下次路過這顯示,請按{combo},",
   "bundle_column_error.body": "加載本組件出錯。",
   "bundle_column_error.retry": "重試",
@@ -51,9 +53,10 @@
   "bundle_modal_error.message": "加載本組件出錯。",
   "bundle_modal_error.retry": "重試",
   "column.blocks": "封鎖用戶",
+  "column.bookmarks": "書籤",
   "column.community": "本站時間軸",
   "column.direct": "個人訊息",
-  "column.directory": "Browse profiles",
+  "column.directory": "瀏覽個人資料",
   "column.domain_blocks": "隱藏的服務站",
   "column.favourites": "最愛的文章",
   "column.follow_requests": "關注請求",
@@ -78,20 +81,22 @@
   "compose_form.lock_disclaimer": "你的用戶狀態為「{locked}」,任何人都能立即關注你,然後看到「只有關注者能看」的文章。",
   "compose_form.lock_disclaimer.lock": "公共",
   "compose_form.placeholder": "你在想甚麼?",
-  "compose_form.poll.add_option": "Add a choice",
-  "compose_form.poll.duration": "Poll duration",
-  "compose_form.poll.option_placeholder": "Choice {number}",
-  "compose_form.poll.remove_option": "Remove this choice",
+  "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": "Change poll to allow multiple choices",
+  "compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
   "compose_form.publish": "發文",
   "compose_form.publish_loud": "{publish}!",
-  "compose_form.sensitive.hide": "Mark media as sensitive",
+  "compose_form.sensitive.hide": "標記媒體為敏感內容",
   "compose_form.sensitive.marked": "媒體被標示為敏感",
   "compose_form.sensitive.unmarked": "媒體沒有被標示為敏感",
   "compose_form.spoiler.marked": "文字被警告隱藏",
   "compose_form.spoiler.unmarked": "文字沒有被隱藏",
   "compose_form.spoiler_placeholder": "敏感警告訊息",
   "confirmation_modal.cancel": "取消",
-  "confirmations.block.block_and_report": "Block & Report",
+  "confirmations.block.block_and_report": "封鎖並檢舉",
   "confirmations.block.confirm": "封鎖",
   "confirmations.block.message": "你確定要封鎖{name}嗎?",
   "confirmations.delete.confirm": "刪除",
@@ -100,25 +105,25 @@
   "confirmations.delete_list.message": "你確定要永久刪除這列表嗎?",
   "confirmations.domain_block.confirm": "隱藏整個網站",
   "confirmations.domain_block.message": "你真的真的確定要隱藏整個 {domain} ?多數情況下,比較推薦封鎖或靜音幾個特定目標就好。你從此將不會再看到該站的內容和通知。來自該站的關注者亦會被移除。",
-  "confirmations.logout.confirm": "Log out",
-  "confirmations.logout.message": "Are you sure you want to log out?",
+  "confirmations.logout.confirm": "登出",
+  "confirmations.logout.message": "確定要登出嗎?",
   "confirmations.mute.confirm": "靜音",
-  "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.",
+  "confirmations.mute.explanation": "這將會隱藏來自他們的貼文與通知,但是他們還是可以查閱你的貼文與關注你。",
   "confirmations.mute.message": "你確定要將{name}靜音嗎?",
   "confirmations.redraft.confirm": "刪除並編輯",
   "confirmations.redraft.message": "你確定要刪除並重新編輯嗎?所有相關的回覆、轉推與最愛都會被刪除。",
-  "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.reply.confirm": "回覆",
+  "confirmations.reply.message": "現在回覆將蓋掉您目前正在撰寫的訊息。是否仍要回覆?",
   "confirmations.unfollow.confirm": "取消關注",
   "confirmations.unfollow.message": "真的不要繼續關注 {name} 了嗎?",
-  "conversation.delete": "Delete conversation",
-  "conversation.mark_as_read": "Mark as read",
-  "conversation.open": "View conversation",
-  "conversation.with": "With {names}",
-  "directory.federated": "From known fediverse",
-  "directory.local": "From {domain} only",
-  "directory.new_arrivals": "New arrivals",
-  "directory.recently_active": "Recently active",
+  "conversation.delete": "刪除對話",
+  "conversation.mark_as_read": "標為已讀",
+  "conversation.open": "檢視對話",
+  "conversation.with": "與 {names}",
+  "directory.federated": "來自已知聯邦宇宙",
+  "directory.local": "僅來自 {domain}",
+  "directory.new_arrivals": "新貨",
+  "directory.recently_active": "最近活躍",
   "embed.instructions": "要內嵌此文章,請將以下代碼貼進你的網站。",
   "embed.preview": "看上去會是這樣:",
   "emoji_button.activity": "活動",
@@ -135,176 +140,182 @@
   "emoji_button.search_results": "搜尋結果",
   "emoji_button.symbols": "符號",
   "emoji_button.travel": "旅遊景物",
-  "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.account_timeline": "這裡還沒有嘟文!",
+  "empty_column.account_unavailable": "無法取得個人資料",
+  "empty_column.blocks": "你還沒有封鎖任何使用者。",
+  "empty_column.bookmarked_statuses": "你還沒建立任何書籤。這裡將會顯示你建立的書籤。",
   "empty_column.community": "本站時間軸暫時未有內容,快寫一點東西來搶頭香啊!",
   "empty_column.direct": "你沒有個人訊息。當你發出或接收個人訊息,就會在這裡出現。",
-  "empty_column.domain_blocks": "There are no hidden domains yet.",
-  "empty_column.favourited_statuses": "You don't have any favourite toots yet. When you favourite one, it will show up here.",
-  "empty_column.favourites": "No one has favourited this toot yet. When someone does, they will show up here.",
-  "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.domain_blocks": "尚未隱藏任何網域。",
+  "empty_column.favourited_statuses": "你還沒收藏任何嘟文。這裡將會顯示你收藏的嘟文。",
+  "empty_column.favourites": "還沒有人收藏這則嘟文。這裡將會顯示被收藏的嘟文。",
+  "empty_column.follow_requests": "您尚未收到任何關注請求。這裡將會顯示收到的關注請求。",
   "empty_column.hashtag": "這個標籤暫時未有內容。",
   "empty_column.home": "你還沒有關注任何用戶。快看看{public},向其他用戶搭訕吧。",
   "empty_column.home.public_timeline": "公共時間軸",
   "empty_column.list": "這個列表暫時未有內容。",
-  "empty_column.lists": "You don't have any lists yet. When you create one, it will show up here.",
-  "empty_column.mutes": "You haven't muted any users yet.",
+  "empty_column.lists": "你還沒有建立任何名單。這裡將會顯示你所建立的名單。",
+  "empty_column.mutes": "你尚未靜音任何使用者。",
   "empty_column.notifications": "你沒有任何通知紀錄,快向其他用戶搭訕吧。",
   "empty_column.public": "跨站時間軸暫時沒有內容!快寫一些公共的文章,或者關注另一些服務站的用戶吧!你和本站、友站的交流,將決定這裏出現的內容。",
-  "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.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.",
-  "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard",
-  "errors.unexpected_crash.report_issue": "Report issue",
+  "error.unexpected_crash.explanation": "由於發生系統故障或瀏覽器相容性問題,故無法正常顯示頁面。",
+  "error.unexpected_crash.next_steps": "請嘗試重新整理頁面。如果狀況沒有進展,你可以使用不同的瀏覽器或 Mastodon 應用程式來檢視。",
+  "errors.unexpected_crash.copy_stacktrace": "複製到剪貼簿",
+  "errors.unexpected_crash.report_issue": "舉報問題",
   "follow_request.authorize": "批准",
   "follow_request.reject": "拒絕",
   "getting_started.developers": "開發者",
-  "getting_started.directory": "Profile directory",
-  "getting_started.documentation": "Documentation",
+  "getting_started.directory": "個人資料目錄",
+  "getting_started.documentation": "文件",
   "getting_started.heading": "開始使用",
   "getting_started.invite": "邀請使用者",
   "getting_started.open_source_notice": "Mastodon(萬象)是一個開放源碼的軟件。你可以在官方 GitHub ({github}) 貢獻或者回報問題。",
   "getting_started.security": "帳戶安全",
   "getting_started.terms": "服務條款",
-  "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_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",
   "home.column_settings.basic": "基本",
   "home.column_settings.show_reblogs": "顯示被轉推的文章",
   "home.column_settings.show_replies": "顯示回應文章",
-  "intervals.full.days": "{number, plural, one {# day} other {# days}}",
-  "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
-  "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}",
-  "introduction.federation.action": "Next",
-  "introduction.federation.federated.headline": "Federated",
-  "introduction.federation.federated.text": "Public posts from other servers of the fediverse will appear in the federated timeline.",
-  "introduction.federation.home.headline": "Home",
-  "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!",
-  "introduction.federation.local.headline": "Local",
-  "introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
+  "intervals.full.days": "{number, plural, one {# 天} other {# 天}}",
+  "intervals.full.hours": "{number, plural, one {# 小時} other {# 小時}}",
+  "intervals.full.minutes": "{number, plural, one {# 分鐘} other {# 分鐘}}",
+  "introduction.federation.action": "下一步",
+  "introduction.federation.federated.headline": "站台聯盟",
+  "introduction.federation.federated.text": "來自聯盟宇宙中其他站台的公開嘟文將會在站點聯盟時間軸中顯示。",
+  "introduction.federation.home.headline": "首頁",
+  "introduction.federation.home.text": "你關注使用者的嘟文將會在首頁動態中顯示。你可以關注任何伺服器上的任何人!",
+  "introduction.federation.local.headline": "本機",
+  "introduction.federation.local.text": "跟您同伺服器之使用者所發的公開嘟文將會顯示在本機時間軸中。",
   "introduction.interactions.action": "Finish toot-orial!",
-  "introduction.interactions.favourite.headline": "Favourite",
-  "introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
-  "introduction.interactions.reblog.headline": "Boost",
-  "introduction.interactions.reblog.text": "You can share other people's toots with your followers by boosting them.",
-  "introduction.interactions.reply.headline": "Reply",
-  "introduction.interactions.reply.text": "You can reply to other people's and your own toots, which will chain them together in a conversation.",
-  "introduction.welcome.action": "Let's go!",
-  "introduction.welcome.headline": "First steps",
-  "introduction.welcome.text": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.",
+  "introduction.interactions.favourite.headline": "關注",
+  "introduction.interactions.favourite.text": "您能儲存嘟文供稍候觀看,或者收藏嘟文,讓作者知道您喜歡這則嘟文。",
+  "introduction.interactions.reblog.headline": "轉嘟",
+  "introduction.interactions.reblog.text": "您能藉由轉嘟他人嘟文來分享給您的關注者。",
+  "introduction.interactions.reply.headline": "回覆",
+  "introduction.interactions.reply.text": "您能回覆其他人或自己的嘟文,這麼做會把這些回覆串成一串對話。",
+  "introduction.welcome.action": "開始旅程吧!",
+  "introduction.welcome.headline": "第一步",
+  "introduction.welcome.text": "歡迎來到聯盟宇宙!等等你就可以廣播訊息及跨越各種各式各樣的伺服器與朋友聊天。但這台伺服器,{domain},非常特別 - 它寄管了你的個人資料,所以請記住它的名字。",
   "keyboard_shortcuts.back": "後退",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "開啟「封鎖使用者」名單",
   "keyboard_shortcuts.boost": "轉推",
   "keyboard_shortcuts.column": "把標示移動到其中一列",
   "keyboard_shortcuts.compose": "把標示移動到文字輸入區",
   "keyboard_shortcuts.description": "描述",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "開啟私訊欄",
   "keyboard_shortcuts.down": "在列表往下移動",
   "keyboard_shortcuts.enter": "打開文章",
   "keyboard_shortcuts.favourite": "收藏",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "開啟收藏名單",
+  "keyboard_shortcuts.federated": "開啟站點聯盟時間軸",
   "keyboard_shortcuts.heading": "鍵盤快速鍵",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "開啟首頁時間軸",
   "keyboard_shortcuts.hotkey": "快速鍵",
   "keyboard_shortcuts.legend": "顯示這個說明",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "開啟本機時間軸",
   "keyboard_shortcuts.mention": "提及作者",
-  "keyboard_shortcuts.muted": "to open muted users list",
-  "keyboard_shortcuts.my_profile": "to open your profile",
-  "keyboard_shortcuts.notifications": "to open notifications column",
-  "keyboard_shortcuts.pinned": "to open pinned toots list",
-  "keyboard_shortcuts.profile": "to open author's profile",
+  "keyboard_shortcuts.muted": "開啟靜音使用者名單",
+  "keyboard_shortcuts.my_profile": "開啟個人資料頁面",
+  "keyboard_shortcuts.notifications": "開啟通知欄",
+  "keyboard_shortcuts.open_media": "開啟媒體",
+  "keyboard_shortcuts.pinned": "開啟釘選的嘟文名單",
+  "keyboard_shortcuts.profile": "開啟作者的個人資料頁面",
   "keyboard_shortcuts.reply": "回覆",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "開啟關注請求名單",
   "keyboard_shortcuts.search": "把標示移動到搜索",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
+  "keyboard_shortcuts.start": "開啟「開始使用」欄位",
   "keyboard_shortcuts.toggle_hidden": "顯示或隱藏被標為敏感的文字",
-  "keyboard_shortcuts.toggle_sensitivity": "to show/hide media",
+  "keyboard_shortcuts.toggle_sensitivity": "顯示 / 隱藏媒體",
   "keyboard_shortcuts.toot": "新的推文",
   "keyboard_shortcuts.unfocus": "把標示移離文字輸入和搜索",
   "keyboard_shortcuts.up": "在列表往上移動",
   "lightbox.close": "關閉",
   "lightbox.next": "繼續",
   "lightbox.previous": "回退",
-  "lightbox.view_context": "View context",
+  "lightbox.view_context": "檢視內文",
   "lists.account.add": "新增到列表",
   "lists.account.remove": "從列表刪除",
   "lists.delete": "刪除列表",
   "lists.edit": "編輯列表",
-  "lists.edit.submit": "Change title",
+  "lists.edit.submit": "變更標題",
   "lists.new.create": "新增列表",
   "lists.new.title_placeholder": "新列表標題",
   "lists.search": "從你關注的用戶中搜索",
   "lists.subheading": "列表",
-  "load_pending": "{count, plural, one {# new item} other {# new items}}",
+  "load_pending": "{count, plural, other {# 個新項目}}",
   "loading_indicator.label": "載入中...",
   "media_gallery.toggle_visible": "打開或關上",
   "missing_indicator.label": "找不到內容",
   "missing_indicator.sublabel": "無法找到內容",
   "mute_modal.hide_notifications": "隱藏來自這用戶的通知嗎?",
-  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.apps": "封鎖的使用者",
   "navigation_bar.blocks": "被你封鎖的用戶",
+  "navigation_bar.bookmarks": "書籤",
   "navigation_bar.community_timeline": "本站時間軸",
-  "navigation_bar.compose": "Compose new toot",
+  "navigation_bar.compose": "撰寫新嘟文",
   "navigation_bar.direct": "個人訊息",
   "navigation_bar.discover": "探索",
   "navigation_bar.domain_blocks": "隱藏的服務站",
   "navigation_bar.edit_profile": "修改個人資料",
   "navigation_bar.favourites": "最愛的內容",
-  "navigation_bar.filters": "Muted words",
+  "navigation_bar.filters": "靜音詞彙",
   "navigation_bar.follow_requests": "關注請求",
-  "navigation_bar.follows_and_followers": "Follows and followers",
+  "navigation_bar.follows_and_followers": "關注及關注者",
   "navigation_bar.info": "關於本服務站",
   "navigation_bar.keyboard_shortcuts": "鍵盤快速鍵",
   "navigation_bar.lists": "列表",
   "navigation_bar.logout": "登出",
   "navigation_bar.mutes": "被你靜音的用戶",
-  "navigation_bar.personal": "Personal",
+  "navigation_bar.personal": "個人",
   "navigation_bar.pins": "置頂文章",
   "navigation_bar.preferences": "偏好設定",
   "navigation_bar.public_timeline": "跨站時間軸",
   "navigation_bar.security": "安全",
   "notification.favourite": "{name} 收藏了你的文章",
   "notification.follow": "{name} 開始關注你",
+  "notification.follow_request": "{name} 要求關注你",
   "notification.mention": "{name} 提及你",
-  "notification.poll": "A poll you have voted in has ended",
+  "notification.own_poll": "您的投票已結束",
+  "notification.poll": "您投過的投票已經結束",
   "notification.reblog": "{name} 轉推你的文章",
   "notifications.clear": "清空通知紀錄",
   "notifications.clear_confirmation": "你確定要清空通知紀錄嗎?",
   "notifications.column_settings.alert": "顯示桌面通知",
   "notifications.column_settings.favourite": "收藏了你的文章:",
-  "notifications.column_settings.filter_bar.advanced": "Display all categories",
-  "notifications.column_settings.filter_bar.category": "Quick filter bar",
-  "notifications.column_settings.filter_bar.show": "Show",
+  "notifications.column_settings.filter_bar.advanced": "顯示所有分類",
+  "notifications.column_settings.filter_bar.category": "快速過濾欄",
+  "notifications.column_settings.filter_bar.show": "顯示",
   "notifications.column_settings.follow": "關注你:",
+  "notifications.column_settings.follow_request": "新的關注請求:",
   "notifications.column_settings.mention": "提及你:",
-  "notifications.column_settings.poll": "Poll results:",
+  "notifications.column_settings.poll": "投票結果:",
   "notifications.column_settings.push": "推送通知",
   "notifications.column_settings.reblog": "轉推你的文章:",
   "notifications.column_settings.show": "在通知欄顯示",
   "notifications.column_settings.sound": "播放音效",
-  "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.all": "全部",
+  "notifications.filter.boosts": "轉嘟",
+  "notifications.filter.favourites": "最愛",
+  "notifications.filter.follows": "關注的使用者",
+  "notifications.filter.mentions": "提及",
+  "notifications.filter.polls": "投票結果",
   "notifications.group": "{count} 條通知",
-  "poll.closed": "Closed",
-  "poll.refresh": "Refresh",
-  "poll.total_people": "{count, plural, one {# person} other {# people}}",
-  "poll.total_votes": "{count, plural, one {# vote} other {# votes}}",
-  "poll.vote": "Vote",
-  "poll.voted": "You voted for this answer",
-  "poll_button.add_poll": "Add a poll",
-  "poll_button.remove_poll": "Remove poll",
+  "poll.closed": "已關閉",
+  "poll.refresh": "重新整理",
+  "poll.total_people": "{count, plural, one {# 個投票} other {# 個投票}}",
+  "poll.total_votes": "{count, plural, one {# 個投票} other {# 個投票}}",
+  "poll.vote": "投票",
+  "poll.voted": "你已對此問題投票",
+  "poll_button.add_poll": "建立投票",
+  "poll_button.remove_poll": "移除投票",
   "privacy.change": "調整私隱設定",
   "privacy.direct.long": "只有提及的用戶能看到",
   "privacy.direct.short": "私人訊息",
@@ -314,7 +325,7 @@
   "privacy.public.short": "公共",
   "privacy.unlisted.long": "公開,但不在公共時間軸顯示",
   "privacy.unlisted.short": "公開",
-  "refresh": "Refresh",
+  "refresh": "重新整理",
   "regeneration_indicator.label": "載入中……",
   "regeneration_indicator.sublabel": "你的主頁時間軸正在準備中!",
   "relative_time.days": "{number}日",
@@ -339,20 +350,21 @@
   "search_results.accounts": "使用者",
   "search_results.hashtags": "標籤",
   "search_results.statuses": "文章",
-  "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.",
+  "search_results.statuses_fts_disabled": "「依內容搜尋嘟文」未在此 Mastodon 伺服器啟用。",
   "search_results.total": "{count, number} 項結果",
-  "status.admin_account": "Open moderation interface for @{name}",
-  "status.admin_status": "Open this status in the moderation interface",
+  "status.admin_account": "開啟 @{name} 的管理介面",
+  "status.admin_status": "在管理介面開啟此嘟文",
   "status.block": "封鎖 @{name}",
+  "status.bookmark": "書籤",
   "status.cancel_reblog_private": "取消轉推",
   "status.cannot_reblog": "這篇文章無法被轉推",
-  "status.copy": "Copy link to status",
+  "status.copy": "將連結複製到嘟文中",
   "status.delete": "刪除",
-  "status.detailed_status": "Detailed conversation view",
+  "status.detailed_status": "對話的詳細內容",
   "status.direct": "私訊 @{name}",
   "status.embed": "鑲嵌",
   "status.favourite": "收藏",
-  "status.filtered": "Filtered",
+  "status.filtered": "已過濾",
   "status.load_more": "載入更多",
   "status.media_hidden": "隱藏媒體內容",
   "status.mention": "提及 @{name}",
@@ -362,12 +374,13 @@
   "status.open": "展開文章",
   "status.pin": "置頂到資料頁",
   "status.pinned": "置頂文章",
-  "status.read_more": "Read more",
+  "status.read_more": "閱讀更多",
   "status.reblog": "轉推",
   "status.reblog_private": "轉推到原讀者",
   "status.reblogged_by": "{name} 轉推",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
+  "status.reblogs.empty": "還沒有人轉嘟。如果有,會顯示在這裡。",
   "status.redraft": "刪除並編輯",
+  "status.remove_bookmark": "移除書籤",
   "status.reply": "回應",
   "status.replyAll": "回應所有人",
   "status.report": "舉報 @{name}",
@@ -377,41 +390,44 @@
   "status.show_less_all": "減少顯示這類文章",
   "status.show_more": "顯示更多",
   "status.show_more_all": "顯示更多這類文章",
-  "status.show_thread": "Show thread",
-  "status.uncached_media_warning": "Not available",
+  "status.show_thread": "顯示討論串",
+  "status.uncached_media_warning": "無法使用",
   "status.unmute_conversation": "解禁對話",
   "status.unpin": "解除置頂",
-  "suggestions.dismiss": "Dismiss suggestion",
-  "suggestions.header": "You might be interested in…",
+  "suggestions.dismiss": "關閉建議",
+  "suggestions.header": "您可能對這些東西有興趣…",
   "tabs_bar.federated_timeline": "跨站",
   "tabs_bar.home": "主頁",
   "tabs_bar.local_timeline": "本站",
   "tabs_bar.notifications": "通知",
   "tabs_bar.search": "搜尋",
-  "time_remaining.days": "{number, plural, one {# day} other {# days}} left",
-  "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left",
-  "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left",
-  "time_remaining.moments": "Moments remaining",
-  "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left",
+  "time_remaining.days": "剩餘{number, plural, one {# 天數} other {# 天數}}",
+  "time_remaining.hours": "剩餘{number, plural, one {# 小時} other {# 小時}}",
+  "time_remaining.minutes": "剩餘{number, plural, one {# 分鐘} other {# 分鐘}}",
+  "time_remaining.moments": "剩餘時間",
+  "time_remaining.seconds": "剩餘 {number, plural, one {# 秒} other {# 秒}}",
   "trends.count_by_accounts": "{count} 位用戶在討論",
-  "trends.trending_now": "Trending now",
+  "trends.trending_now": "目前趨勢",
   "ui.beforeunload": "如果你現在離開 Mastodon,你的草稿內容將會被丟棄。",
   "upload_area.title": "將檔案拖放至此上載",
   "upload_button.label": "上載媒體檔案",
-  "upload_error.limit": "File upload limit exceeded.",
-  "upload_error.poll": "File upload not allowed with polls.",
+  "upload_error.limit": "已達到檔案上傳限制。",
+  "upload_error.poll": "不允許在投票上傳檔案。",
+  "upload_form.audio_description": "簡單描述內容給聽障人士",
   "upload_form.description": "為視覺障礙人士添加文字說明",
-  "upload_form.edit": "Edit",
+  "upload_form.edit": "編輯",
   "upload_form.undo": "刪除",
-  "upload_modal.analyzing_picture": "Analyzing picture…",
-  "upload_modal.apply": "Apply",
-  "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.preview_label": "Preview ({ratio})",
+  "upload_form.video_description": "簡單描述給聽障或視障人士",
+  "upload_modal.analyzing_picture": "正在分析圖片…",
+  "upload_modal.apply": "套用",
+  "upload_modal.description_placeholder": "A quick brown fox 跳過那隻懶狗",
+  "upload_modal.detect_text": "從圖片偵測文字",
+  "upload_modal.edit_media": "編輯媒體",
+  "upload_modal.hint": "點擊或拖曳圓圈以選擇預覽縮圖。",
+  "upload_modal.preview_label": "預覽 ({ratio})",
   "upload_progress.label": "上載中……",
   "video.close": "關閉影片",
+  "video.download": "下載檔案",
   "video.exit_fullscreen": "退出全熒幕",
   "video.expand": "展開影片",
   "video.fullscreen": "全熒幕",
diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json
index 136573381..ab680223e 100644
--- a/app/javascript/mastodon/locales/zh-TW.json
+++ b/app/javascript/mastodon/locales/zh-TW.json
@@ -1,6 +1,7 @@
 {
   "account.add_or_remove_from_list": "從名單中新增或移除",
   "account.badges.bot": "機器人",
+  "account.badges.group": "Group",
   "account.block": "封鎖 @{name}",
   "account.block_domain": "隱藏來自 {domain} 的所有內容",
   "account.blocked": "已封鎖",
@@ -42,6 +43,7 @@
   "alert.rate_limited.title": "已限速",
   "alert.unexpected.message": "發生了非預期的錯誤。",
   "alert.unexpected.title": "哎呀!",
+  "announcement.announcement": "Announcement",
   "autosuggest_hashtag.per_week": "{count} / 週",
   "boost_modal.combo": "下次您可以按 {combo} 跳過",
   "bundle_column_error.body": "載入此元件時發生錯誤。",
@@ -51,6 +53,7 @@
   "bundle_modal_error.message": "載入此元件時發生錯誤。",
   "bundle_modal_error.retry": "重試",
   "column.blocks": "封鎖的使用者",
+  "column.bookmarks": "書籤",
   "column.community": "本機時間軸",
   "column.direct": "私訊",
   "column.directory": "瀏覽個人資料",
@@ -82,6 +85,8 @@
   "compose_form.poll.duration": "投票期限",
   "compose_form.poll.option_placeholder": "第 {number} 個選擇",
   "compose_form.poll.remove_option": "移除此選擇",
+  "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": "嘟出去",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.hide": "標記媒體為敏感內容",
@@ -103,7 +108,7 @@
   "confirmations.logout.confirm": "登出",
   "confirmations.logout.message": "確定要登出嗎?",
   "confirmations.mute.confirm": "靜音",
-  "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.",
+  "confirmations.mute.explanation": "這將會隱藏來自他們的貼文與通知,但是他們還是可以查閱你的貼文與關注你。",
   "confirmations.mute.message": "確定靜音 {name} ?",
   "confirmations.redraft.confirm": "刪除並重新編輯",
   "confirmations.redraft.message": "確定刪掉這則嘟文並重新編輯嗎?將會失去這則嘟文的轉嘟及收藏,且回覆這則的嘟文將會變成獨立的嘟文。",
@@ -138,6 +143,7 @@
   "empty_column.account_timeline": "這裡還沒有嘟文!",
   "empty_column.account_unavailable": "無法取得個人資料",
   "empty_column.blocks": "你還沒有封鎖任何使用者。",
+  "empty_column.bookmarked_statuses": "你還沒建立任何書籤。這裡將會顯示你建立的書籤。",
   "empty_column.community": "本地時間軸是空的。快公開嘟些文搶頭香啊!",
   "empty_column.direct": "您還沒有任何私訊。當您私訊別人或收到私訊時,它將於此顯示。",
   "empty_column.domain_blocks": "尚未隱藏任何網域。",
@@ -152,10 +158,10 @@
   "empty_column.mutes": "你尚未靜音任何使用者。",
   "empty_column.notifications": "您尚未收到任何通知,和別人互動開啟對話吧。",
   "empty_column.public": "這裡什麼都沒有!嘗試寫些公開的嘟文,或著自己關注其他伺服器的使用者後就會有嘟文出現了",
-  "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.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.",
-  "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard",
-  "errors.unexpected_crash.report_issue": "Report issue",
+  "error.unexpected_crash.explanation": "由於發生系統故障或瀏覽器相容性問題,故無法正常顯示頁面。",
+  "error.unexpected_crash.next_steps": "請嘗試重新整理頁面。如果狀況沒有進展,你可以使用不同的瀏覽器或 Mastodon 應用程式來檢視。",
+  "errors.unexpected_crash.copy_stacktrace": "複製到剪貼簿",
+  "errors.unexpected_crash.report_issue": "舉報問題",
   "follow_request.authorize": "授權",
   "follow_request.reject": "拒絕",
   "getting_started.developers": "開發者",
@@ -219,6 +225,7 @@
   "keyboard_shortcuts.muted": "開啟靜音使用者名單",
   "keyboard_shortcuts.my_profile": "開啟個人資料頁面",
   "keyboard_shortcuts.notifications": "開啟通知欄",
+  "keyboard_shortcuts.open_media": "開啟媒體",
   "keyboard_shortcuts.pinned": "開啟釘選的嘟文名單",
   "keyboard_shortcuts.profile": "開啟作者的個人資料頁面",
   "keyboard_shortcuts.reply": "回覆",
@@ -251,6 +258,7 @@
   "mute_modal.hide_notifications": "隱藏來自這位使用者的通知?",
   "navigation_bar.apps": "封鎖的使用者",
   "navigation_bar.blocks": "封鎖使用者",
+  "navigation_bar.bookmarks": "書籤",
   "navigation_bar.community_timeline": "本機時間軸",
   "navigation_bar.compose": "撰寫新嘟文",
   "navigation_bar.direct": "私訊",
@@ -273,7 +281,9 @@
   "navigation_bar.security": "安全性",
   "notification.favourite": "{name} 把你的嘟文加入了最愛",
   "notification.follow": "{name} 關注了你",
+  "notification.follow_request": "{name} 要求關注你",
   "notification.mention": "{name} 提到了你",
+  "notification.own_poll": "您的投票已結束",
   "notification.poll": "您投過的投票已經結束",
   "notification.reblog": "{name}轉嘟了你的嘟文",
   "notifications.clear": "清除通知",
@@ -284,6 +294,7 @@
   "notifications.column_settings.filter_bar.category": "快速過濾欄",
   "notifications.column_settings.filter_bar.show": "顯示",
   "notifications.column_settings.follow": "新關注者:",
+  "notifications.column_settings.follow_request": "新的關注請求:",
   "notifications.column_settings.mention": "提及:",
   "notifications.column_settings.poll": "投票結果:",
   "notifications.column_settings.push": "推送通知",
@@ -299,7 +310,7 @@
   "notifications.group": "{count} 條通知",
   "poll.closed": "已關閉",
   "poll.refresh": "重新整理",
-  "poll.total_people": "{count, plural, one {# person} other {# people}}",
+  "poll.total_people": "{count, plural, one {# 個投票} other {# 個投票}}",
   "poll.total_votes": "{count, plural, one {# 個投票} other {# 個投票}}",
   "poll.vote": "投票",
   "poll.voted": "你已對此問題投票",
@@ -314,7 +325,7 @@
   "privacy.public.short": "公開",
   "privacy.unlisted.long": "公開,但不會顯示在公開時間軸",
   "privacy.unlisted.short": "不公開",
-  "refresh": "Refresh",
+  "refresh": "重新整理",
   "regeneration_indicator.label": "載入中…",
   "regeneration_indicator.sublabel": "你的主頁時間軸正在準備中!",
   "relative_time.days": "{number} 天",
@@ -344,6 +355,7 @@
   "status.admin_account": "開啟 @{name} 的管理介面",
   "status.admin_status": "在管理介面開啟此嘟文",
   "status.block": "封鎖 @{name}",
+  "status.bookmark": "書籤",
   "status.cancel_reblog_private": "取消轉嘟",
   "status.cannot_reblog": "這篇嘟文無法被轉嘟",
   "status.copy": "將連結複製到嘟文中",
@@ -368,6 +380,7 @@
   "status.reblogged_by": "{name} 轉嘟了",
   "status.reblogs.empty": "還沒有人轉嘟。如果有,會顯示在這裡。",
   "status.redraft": "刪除 & 編輯",
+  "status.remove_bookmark": "移除書籤",
   "status.reply": "回覆",
   "status.replyAll": "回覆所有人",
   "status.report": "檢舉 @{name}",
@@ -400,18 +413,21 @@
   "upload_button.label": "上傳媒體檔案 (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_error.limit": "已達到檔案上傳限制。",
   "upload_error.poll": "不允許在投票上傳檔案。",
+  "upload_form.audio_description": "簡單描述內容給聽障人士",
   "upload_form.description": "為視障人士增加文字說明",
   "upload_form.edit": "編輯",
   "upload_form.undo": "刪除",
+  "upload_form.video_description": "簡單描述給聽障或視障人士",
   "upload_modal.analyzing_picture": "正在分析圖片…",
   "upload_modal.apply": "套用",
   "upload_modal.description_placeholder": "A quick brown fox 跳過那隻懶狗",
   "upload_modal.detect_text": "從圖片偵測文字",
   "upload_modal.edit_media": "編輯媒體",
-  "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.",
+  "upload_modal.hint": "點擊或拖曳圓圈以選擇預覽縮圖。",
   "upload_modal.preview_label": "預覽 ({ratio})",
   "upload_progress.label": "上傳中...",
   "video.close": "關閉影片",
+  "video.download": "下載檔案",
   "video.exit_fullscreen": "退出全螢幕",
   "video.expand": "展開影片",
   "video.fullscreen": "全螢幕",
diff --git a/app/javascript/mastodon/reducers/announcements.js b/app/javascript/mastodon/reducers/announcements.js
new file mode 100644
index 000000000..34e08eac8
--- /dev/null
+++ b/app/javascript/mastodon/reducers/announcements.js
@@ -0,0 +1,102 @@
+import {
+  ANNOUNCEMENTS_FETCH_REQUEST,
+  ANNOUNCEMENTS_FETCH_SUCCESS,
+  ANNOUNCEMENTS_FETCH_FAIL,
+  ANNOUNCEMENTS_UPDATE,
+  ANNOUNCEMENTS_REACTION_UPDATE,
+  ANNOUNCEMENTS_REACTION_ADD_REQUEST,
+  ANNOUNCEMENTS_REACTION_ADD_FAIL,
+  ANNOUNCEMENTS_REACTION_REMOVE_REQUEST,
+  ANNOUNCEMENTS_REACTION_REMOVE_FAIL,
+  ANNOUNCEMENTS_TOGGLE_SHOW,
+  ANNOUNCEMENTS_DELETE,
+  ANNOUNCEMENTS_DISMISS_SUCCESS,
+} from '../actions/announcements';
+import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
+
+const initialState = ImmutableMap({
+  items: ImmutableList(),
+  isLoading: false,
+  show: false,
+});
+
+const updateReaction = (state, id, name, updater) => state.update('items', list => list.map(announcement => {
+  if (announcement.get('id') === id) {
+    return announcement.update('reactions', reactions => {
+      const idx = reactions.findIndex(reaction => reaction.get('name') === name);
+
+      if (idx > -1) {
+        return reactions.update(idx, reaction => updater(reaction));
+      }
+
+      return reactions.push(updater(fromJS({ name, count: 0 })));
+    });
+  }
+
+  return announcement;
+}));
+
+const updateReactionCount = (state, reaction) => updateReaction(state, reaction.announcement_id, reaction.name, x => x.set('count', reaction.count));
+
+const addReaction = (state, id, name) => updateReaction(state, id, name, x => x.set('me', true).update('count', y => y + 1));
+
+const removeReaction = (state, id, name) => updateReaction(state, id, name, x => x.set('me', false).update('count', y => y - 1));
+
+const sortAnnouncements = list => list.sortBy(x => x.get('starts_at') || x.get('published_at'));
+
+const updateAnnouncement = (state, announcement) => {
+  const idx = state.get('items').findIndex(x => x.get('id') === announcement.get('id'));
+
+  if (idx > -1) {
+    // Deep merge is used because announcements from the streaming API do not contain
+    // personalized data about which reactions have been selected by the given user,
+    // and that is information we want to preserve
+    return state.update('items', list => sortAnnouncements(list.update(idx, x => x.mergeDeep(announcement))));
+  }
+
+  return state.update('items', list => sortAnnouncements(list.unshift(announcement)));
+};
+
+export default function announcementsReducer(state = initialState, action) {
+  switch(action.type) {
+  case ANNOUNCEMENTS_TOGGLE_SHOW:
+    return state.withMutations(map => {
+      map.set('show', !map.get('show'));
+    });
+  case ANNOUNCEMENTS_FETCH_REQUEST:
+    return state.set('isLoading', true);
+  case ANNOUNCEMENTS_FETCH_SUCCESS:
+    return state.withMutations(map => {
+      const items = fromJS(action.announcements);
+
+      map.set('items', items);
+      map.set('isLoading', false);
+    });
+  case ANNOUNCEMENTS_FETCH_FAIL:
+    return state.set('isLoading', false);
+  case ANNOUNCEMENTS_UPDATE:
+    return updateAnnouncement(state, fromJS(action.announcement));
+  case ANNOUNCEMENTS_REACTION_UPDATE:
+    return updateReactionCount(state, action.reaction);
+  case ANNOUNCEMENTS_REACTION_ADD_REQUEST:
+  case ANNOUNCEMENTS_REACTION_REMOVE_FAIL:
+    return addReaction(state, action.id, action.name);
+  case ANNOUNCEMENTS_REACTION_REMOVE_REQUEST:
+  case ANNOUNCEMENTS_REACTION_ADD_FAIL:
+    return removeReaction(state, action.id, action.name);
+  case ANNOUNCEMENTS_DISMISS_SUCCESS:
+    return updateAnnouncement(state, fromJS({ 'id': action.id, 'read': true }));
+  case ANNOUNCEMENTS_DELETE:
+    return state.update('items', list => {
+      const idx = list.findIndex(x => x.get('id') === action.id);
+
+      if (idx > -1) {
+        return list.delete(idx);
+      }
+
+      return list;
+    });
+  default:
+    return state;
+  }
+};
diff --git a/app/javascript/mastodon/reducers/index.js b/app/javascript/mastodon/reducers/index.js
index b8d608888..b9817cd38 100644
--- a/app/javascript/mastodon/reducers/index.js
+++ b/app/javascript/mastodon/reducers/index.js
@@ -34,8 +34,10 @@ import polls from './polls';
 import identity_proofs from './identity_proofs';
 import trends from './trends';
 import missed_updates from './missed_updates';
+import announcements from './announcements';
 
 const reducers = {
+  announcements,
   dropdown_menu,
   timelines,
   meta,
diff --git a/app/javascript/mastodon/reducers/statuses.js b/app/javascript/mastodon/reducers/statuses.js
index 772f98bcb..2554c008d 100644
--- a/app/javascript/mastodon/reducers/statuses.js
+++ b/app/javascript/mastodon/reducers/statuses.js
@@ -12,6 +12,7 @@ import {
   STATUS_UNMUTE_SUCCESS,
   STATUS_REVEAL,
   STATUS_HIDE,
+  STATUS_COLLAPSE,
 } from '../actions/statuses';
 import { TIMELINE_DELETE } from '../actions/timelines';
 import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer';
@@ -41,8 +42,7 @@ export default function statuses(state = initialState, action) {
   case FAVOURITE_REQUEST:
     return state.setIn([action.status.get('id'), 'favourited'], true);
   case UNFAVOURITE_SUCCESS:
-    const favouritesCount = action.status.get('favourites_count');
-    return state.setIn([action.status.get('id'), 'favourites_count'], favouritesCount - 1);
+    return state.updateIn([action.status.get('id'), 'favourites_count'], x => Math.max(0, x - 1));
   case FAVOURITE_FAIL:
     return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'favourited'], false);
   case BOOKMARK_REQUEST:
@@ -73,6 +73,8 @@ export default function statuses(state = initialState, action) {
         }
       });
     });
+  case STATUS_COLLAPSE:
+    return state.setIn([action.id, 'collapsed'], action.isCollapsed);
   case TIMELINE_DELETE:
     return deleteStatus(state, action.id, action.references);
   default:
diff --git a/app/javascript/mastodon/selectors/index.js b/app/javascript/mastodon/selectors/index.js
index 6f1ce9602..6a48f3b3f 100644
--- a/app/javascript/mastodon/selectors/index.js
+++ b/app/javascript/mastodon/selectors/index.js
@@ -26,6 +26,7 @@ const toServerSideType = columnType => {
   case 'notifications':
   case 'public':
   case 'thread':
+  case 'account':
     return columnType;
   default:
     if (columnType.indexOf('list:') > -1) {
diff --git a/app/javascript/mastodon/stream.js b/app/javascript/mastodon/stream.js
index 50f90d44c..0cb2b228f 100644
--- a/app/javascript/mastodon/stream.js
+++ b/app/javascript/mastodon/stream.js
@@ -2,6 +2,14 @@ import WebSocketClient from '@gamestdio/websocket';
 
 const randomIntUpTo = max => Math.floor(Math.random() * Math.floor(max));
 
+const knownEventTypes = [
+  'update',
+  'delete',
+  'notification',
+  'conversation',
+  'filters_changed',
+];
+
 export function connectStream(path, pollingRefresh = null, callbacks = () => ({ onConnect() {}, onDisconnect() {}, onReceive() {} })) {
   return (dispatch, getState) => {
     const streamingAPIBaseURL = getState().getIn(['meta', 'streaming_api_base_url']);
@@ -69,14 +77,43 @@ export function connectStream(path, pollingRefresh = null, callbacks = () => ({
 
 
 export default function getStream(streamingAPIBaseURL, accessToken, stream, { connected, received, disconnected, reconnected }) {
-  const params = [ `stream=${stream}` ];
-
-  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;
-  ws.onreconnect = reconnected;
+  const params = stream.split('&');
+  stream = params.shift();
+
+  if (streamingAPIBaseURL.startsWith('ws')) {
+    params.unshift(`stream=${stream}`);
+    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;
+    ws.onreconnect = reconnected;
+
+    return ws;
+  }
+
+  stream = stream.replace(/:/g, '/');
+  params.push(`access_token=${accessToken}`);
+  const es = new EventSource(`${streamingAPIBaseURL}/api/v1/streaming/${stream}?${params.join('&')}`);
+
+  let firstConnect = true;
+  es.onopen = () => {
+    if (firstConnect) {
+      firstConnect = false;
+      connected();
+    } else {
+      reconnected();
+    }
+  };
+  for (let type of knownEventTypes) {
+    es.addEventListener(type, (e) => {
+      received({
+        event: e.type,
+        payload: e.data,
+      });
+    });
+  }
+  es.onerror = disconnected;
 
-  return ws;
+  return es;
 };
diff --git a/app/javascript/mastodon/utils/numbers.js b/app/javascript/mastodon/utils/numbers.js
index f7e4ceb93..af18dcfdd 100644
--- a/app/javascript/mastodon/utils/numbers.js
+++ b/app/javascript/mastodon/utils/numbers.js
@@ -4,9 +4,13 @@ import { FormattedNumber } from 'react-intl';
 export const shortNumberFormat = number => {
   if (number < 1000) {
     return <FormattedNumber value={number} />;
-  } else if (number < 1000000) {
+  } else if (number < 10000) {
     return <Fragment><FormattedNumber value={number / 1000} maximumFractionDigits={1} />K</Fragment>;
-  } else {
+  } else if (number < 1000000) {
+    return <Fragment><FormattedNumber value={number / 1000} maximumFractionDigits={0} />K</Fragment>;
+  } else if (number < 10000000) {
     return <Fragment><FormattedNumber value={number / 1000000} maximumFractionDigits={1} />M</Fragment>;
+  } else {
+    return <Fragment><FormattedNumber value={number / 1000000} maximumFractionDigits={0} />M</Fragment>;
   }
 };
diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js
index 3eae1a457..640f0af0a 100644
--- a/app/javascript/packs/public.js
+++ b/app/javascript/packs/public.js
@@ -101,15 +101,6 @@ function main() {
 
     delegate(document, '.custom-emoji', 'mouseover', getEmojiAnimationHandler('data-original'));
     delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static'));
-
-    delegate(document, '.blocks-table button.icon-button', 'click', function(e) {
-      e.preventDefault();
-
-      const classList = this.firstElementChild.classList;
-      classList.toggle('fa-chevron-down');
-      classList.toggle('fa-chevron-up');
-      this.parentElement.parentElement.nextElementSibling.classList.toggle('hidden');
-    });
   });
 
   delegate(document, '.sidebar__toggle__icon', 'click', () => {
diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss
index cba552433..fb136d1a3 100644
--- a/app/javascript/styles/mastodon/admin.scss
+++ b/app/javascript/styles/mastodon/admin.scss
@@ -186,17 +186,22 @@ $content-width: 840px;
 
       padding-bottom: 40px;
       border-bottom: 1px solid lighten($ui-base-color, 8%);
-      margin-bottom: 40px;
+
+      margin: -15px -15px 40px 0;
 
       flex-wrap: wrap;
       align-items: center;
-
       justify-content: space-between;
 
+      & > * {
+        margin-top: 15px;
+        margin-right: 15px;
+      }
+
       &-actions {
         display: inline-flex;
 
-        & > * {
+        & > :not(:first-child) {
           margin-left: 5px;
         }
       }
@@ -910,3 +915,50 @@ a.name-tag,
     color: $primary-text-color;
   }
 }
+
+.center-text {
+  text-align: center;
+}
+
+.announcements-list {
+  border: 1px solid lighten($ui-base-color, 4%);
+  border-radius: 4px;
+
+  &__item {
+    padding: 15px 0;
+    background: $ui-base-color;
+    border-bottom: 1px solid lighten($ui-base-color, 4%);
+
+    &__title {
+      padding: 0 15px;
+      display: block;
+      font-weight: 500;
+      font-size: 18px;
+      line-height: 1.5;
+      color: $secondary-text-color;
+      text-decoration: none;
+      margin-bottom: 10px;
+
+      &:hover,
+      &:focus,
+      &:active {
+        color: $primary-text-color;
+      }
+    }
+
+    &__meta {
+      padding: 0 15px;
+      color: $dark-text-color;
+    }
+
+    &__action-bar {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+    }
+
+    &:last-child {
+      border-bottom: 0;
+    }
+  }
+}
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 01a633c5f..54372022a 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -3,6 +3,14 @@
   -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;
@@ -860,6 +868,48 @@
   }
 }
 
+.announcements__item__content {
+  word-wrap: break-word;
+
+  .emojione {
+    width: 20px;
+    height: 20px;
+    margin: -3px 0 0;
+  }
+
+  p {
+    margin-bottom: 10px;
+    white-space: pre-wrap;
+
+    &:last-child {
+      margin-bottom: 0;
+    }
+  }
+
+  a {
+    color: $secondary-text-color;
+    text-decoration: none;
+
+    &:hover {
+      text-decoration: underline;
+    }
+
+    &.mention {
+      &:hover {
+        text-decoration: none;
+
+        span {
+          text-decoration: underline;
+        }
+      }
+    }
+
+    &.unhandled-link {
+      color: lighten($ui-highlight-color, 8%);
+    }
+  }
+}
+
 .status__content.status__content--collapsed {
   max-height: 20px * 15; // 15 lines is roughly above 500 characters
 }
@@ -873,6 +923,7 @@
   background: transparent;
   padding: 0;
   padding-top: 8px;
+  text-decoration: none;
 
   &:hover,
   &:active {
@@ -1042,6 +1093,7 @@
   .status-check-box__status {
     margin: 10px 0 10px 10px;
     flex: 1;
+    overflow: hidden;
 
     .media-gallery {
       max-width: 250px;
@@ -2471,7 +2523,7 @@ a.account__display-name {
     display: block;
     object-fit: contain;
     object-position: bottom left;
-    width: 100%;
+    width: 85%;
     height: 100%;
     pointer-events: none;
     user-drag: none;
@@ -3183,13 +3235,16 @@ a.status-card.compact:hover {
 .column-header__wrapper {
   position: relative;
   flex: 0 0 auto;
+  z-index: 1;
 
   &.active {
+    box-shadow: 0 1px 0 rgba($highlight-text-color, 0.3);
+
     &::before {
       display: block;
       content: "";
       position: absolute;
-      top: 35px;
+      bottom: -13px;
       left: 0;
       right: 0;
       margin: 0 auto;
@@ -3200,6 +3255,11 @@ a.status-card.compact:hover {
       background: radial-gradient(ellipse, rgba($ui-highlight-color, 0.23) 0%, rgba($ui-highlight-color, 0) 60%);
     }
   }
+
+  .announcements {
+    z-index: 1;
+    position: relative;
+  }
 }
 
 .column-header {
@@ -3232,8 +3292,6 @@ a.status-card.compact:hover {
   }
 
   &.active {
-    box-shadow: 0 1px 0 rgba($highlight-text-color, 0.3);
-
     .column-header__icon {
       color: $highlight-text-color;
       text-shadow: 0 0 10px rgba($highlight-text-color, 0.4);
@@ -3289,6 +3347,8 @@ a.status-card.compact:hover {
   color: $darker-text-color;
   transition: max-height 150ms ease-in-out, opacity 300ms linear;
   opacity: 1;
+  z-index: 1;
+  position: relative;
 
   &.collapsed {
     max-height: 0;
@@ -5702,6 +5762,8 @@ a.status-card.compact:hover {
     text-align: center;
     text-decoration: none;
     position: relative;
+    width: 100%;
+    white-space: nowrap;
 
     &.active {
       color: $secondary-text-color;
@@ -6399,13 +6461,13 @@ noscript {
     &__links {
       font-size: 14px;
       color: $darker-text-color;
+      padding: 10px 0;
 
       a {
         display: inline-block;
         color: $darker-text-color;
         text-decoration: none;
-        padding: 10px;
-        padding-top: 20px;
+        padding: 5px 10px;
         font-weight: 500;
 
         strong {
@@ -6590,3 +6652,187 @@ noscript {
     }
   }
 }
+
+.announcements {
+  background: lighten($ui-base-color, 8%);
+  font-size: 13px;
+  display: flex;
+  align-items: flex-end;
+
+  &__mastodon {
+    width: 124px;
+    flex: 0 0 auto;
+
+    @media screen and (max-width: 124px + 300px) {
+      display: none;
+    }
+  }
+
+  &__container {
+    width: calc(100% - 124px);
+    flex: 0 0 auto;
+    position: relative;
+
+    @media screen and (max-width: 124px + 300px) {
+      width: 100%;
+    }
+  }
+
+  &__item {
+    box-sizing: border-box;
+    width: 100%;
+    padding: 15px;
+    padding-right: 15px + 18px;
+    position: relative;
+    font-size: 15px;
+    line-height: 20px;
+    word-wrap: break-word;
+    font-weight: 400;
+
+    &__range {
+      display: block;
+      font-weight: 500;
+      margin-bottom: 10px;
+    }
+
+    &__unread {
+      position: absolute;
+      top: 15px;
+      right: 15px;
+      display: inline-block;
+      background: $highlight-text-color;
+      border-radius: 50%;
+      width: 0.625rem;
+      height: 0.625rem;
+      margin: 0 .15em;
+    }
+  }
+
+  &__pagination {
+    padding: 15px;
+    color: $darker-text-color;
+    position: absolute;
+    bottom: 3px;
+    right: 0;
+  }
+}
+
+.layout-multiple-columns .announcements__mastodon {
+  display: none;
+}
+
+.layout-multiple-columns .announcements__container {
+  width: 100%;
+}
+
+.reactions-bar {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  margin-top: 15px;
+  margin-left: -2px;
+  width: calc(100% - (90px - 33px));
+
+  &__item {
+    flex-shrink: 0;
+    background: lighten($ui-base-color, 12%);
+    border: 0;
+    border-radius: 3px;
+    margin: 2px;
+    cursor: pointer;
+    user-select: none;
+    padding: 0 6px;
+    display: flex;
+    align-items: center;
+    transition: all 100ms ease-in;
+    transition-property: background-color, color;
+
+    &__emoji {
+      display: block;
+      margin: 3px 0;
+      width: 16px;
+      height: 16px;
+
+      img {
+        display: block;
+        margin: 0;
+        width: 100%;
+        height: 100%;
+        min-width: auto;
+        min-height: auto;
+        vertical-align: bottom;
+        object-fit: contain;
+      }
+    }
+
+    &__count {
+      display: block;
+      min-width: 9px;
+      font-size: 13px;
+      font-weight: 500;
+      text-align: center;
+      margin-left: 6px;
+      color: $darker-text-color;
+    }
+
+    &:hover,
+    &:focus,
+    &:active {
+      background: lighten($ui-base-color, 16%);
+      transition: all 200ms ease-out;
+      transition-property: background-color, color;
+
+      &__count {
+        color: lighten($darker-text-color, 4%);
+      }
+    }
+
+    &.active {
+      transition: all 100ms ease-in;
+      transition-property: background-color, color;
+      background-color: mix(lighten($ui-base-color, 12%), $ui-highlight-color, 80%);
+
+      .reactions-bar__item__count {
+        color: lighten($highlight-text-color, 8%);
+      }
+    }
+  }
+
+  .emoji-picker-dropdown {
+    margin: 2px;
+  }
+
+  &:hover .emoji-button {
+    opacity: 0.85;
+  }
+
+  .emoji-button {
+    color: $darker-text-color;
+    margin: 0;
+    font-size: 16px;
+    width: auto;
+    flex-shrink: 0;
+    padding: 0 6px;
+    height: 22px;
+    display: flex;
+    align-items: center;
+    opacity: 0.5;
+    transition: all 100ms ease-in;
+    transition-property: background-color, color;
+
+    &:hover,
+    &:active,
+    &:focus {
+      opacity: 1;
+      color: lighten($darker-text-color, 4%);
+      transition: all 200ms ease-out;
+      transition-property: background-color, color;
+    }
+  }
+
+  &--empty {
+    .emoji-button {
+      padding: 0;
+    }
+  }
+}
diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss
index a0478bf7f..c9ad68f94 100644
--- a/app/javascript/styles/mastodon/forms.scss
+++ b/app/javascript/styles/mastodon/forms.scss
@@ -222,6 +222,12 @@ code {
     }
   }
 
+  .input.datetime .label_input select {
+    display: inline-block;
+    width: auto;
+    flex: 0;
+  }
+
   .required abbr {
     text-decoration: none;
     color: lighten($error-value-color, 12%);
diff --git a/app/lib/activitypub/activity.rb b/app/lib/activitypub/activity.rb
index 49b1dc9cd..ee35e1e8d 100644
--- a/app/lib/activitypub/activity.rb
+++ b/app/lib/activitypub/activity.rb
@@ -21,7 +21,7 @@ class ActivityPub::Activity
   class << self
     def factory(json, account, **options)
       @json = json
-      klass&.new(json, account, options)
+      klass&.new(json, account, **options)
     end
 
     private
diff --git a/app/lib/entity_cache.rb b/app/lib/entity_cache.rb
index 8fff544a0..35a3773d2 100644
--- a/app/lib/entity_cache.rb
+++ b/app/lib/entity_cache.rb
@@ -8,7 +8,7 @@ class EntityCache
   MAX_EXPIRATION = 7.days.freeze
 
   def mention(username, domain)
-    Rails.cache.fetch(to_key(:mention, username, domain), expires_in: MAX_EXPIRATION) { Account.select(:username, :domain, :url).find_remote(username, domain) }
+    Rails.cache.fetch(to_key(:mention, username, domain), expires_in: MAX_EXPIRATION) { Account.select(:id, :username, :domain, :url).find_remote(username, domain) }
   end
 
   def emoji(shortcodes, domain)
diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb
index cdb8383df..3ce182809 100644
--- a/app/lib/feed_manager.rb
+++ b/app/lib/feed_manager.rb
@@ -11,6 +11,10 @@ class FeedManager
   # Must be <= MAX_ITEMS or the tracking sets will grow forever
   REBLOG_FALLOFF = 40
 
+  def with_active_accounts(&block)
+    Account.joins(:user).where('users.current_sign_in_at > ?', User::ACTIVE_DURATION.ago).find_each(&block)
+  end
+
   def key(type, id, subtype = nil)
     return "feed:#{type}:#{id}" unless subtype
 
diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb
index 9159db2a1..f1a751f84 100644
--- a/app/lib/formatter.rb
+++ b/app/lib/formatter.rb
@@ -308,8 +308,9 @@ class Formatter
     end
 
     standard = Extractor.extract_entities_with_indices(text, options)
+    extra = Extractor.extract_extra_uris_with_indices(text, options)
 
-    Extractor.remove_overlapping_entities(special + standard)
+    Extractor.remove_overlapping_entities(special + standard + extra)
   end
 
   def html_friendly_extractor(html, options = {})
@@ -370,7 +371,7 @@ class Formatter
 
   def link_html(url)
     url    = Addressable::URI.parse(url).to_s
-    prefix = url.match(/\Ahttps?:\/\/(www\.)?/).to_s
+    prefix = url.match(/\A(https?:\/\/(www\.)?|xmpp:)/).to_s
     text   = url[prefix.length, 30]
     suffix = url[prefix.length + 30..-1]
     cutoff = url[prefix.length..-1].length > 30
diff --git a/app/lib/inline_renderer.rb b/app/lib/inline_renderer.rb
index 761a8822d..27e334a4d 100644
--- a/app/lib/inline_renderer.rb
+++ b/app/lib/inline_renderer.rb
@@ -15,6 +15,10 @@ class InlineRenderer
       serializer = REST::NotificationSerializer
     when :conversation
       serializer = REST::ConversationSerializer
+    when :announcement
+      serializer = REST::AnnouncementSerializer
+    when :reaction
+      serializer = REST::ReactionSerializer
     else
       return
     end
diff --git a/app/lib/request.rb b/app/lib/request.rb
index 42ccc6513..c476e7785 100644
--- a/app/lib/request.rb
+++ b/app/lib/request.rb
@@ -59,7 +59,7 @@ class Request
     begin
       response = http_client.public_send(@verb, @url.to_s, @options.merge(headers: headers))
     rescue => e
-      raise e.class, "#{e.message} on #{@url}", e.backtrace
+      raise e.class, "#{e.message} on #{@url}", e.backtrace[0]
     end
 
     begin
@@ -73,6 +73,8 @@ class Request
       response.body_with_limit if http_client.persistent?
 
       yield response if block_given?
+    rescue => e
+      raise e.class, e.message, e.backtrace[0]
     ensure
       http_client.close unless http_client.persistent?
     end
@@ -94,7 +96,7 @@ class Request
     end
 
     def http_client
-      HTTP.use(:auto_inflate).timeout(:per_operation, TIMEOUT.dup).follow(max_hops: 2)
+      HTTP.use(:auto_inflate).timeout(TIMEOUT.dup).follow(max_hops: 2)
     end
   end
 
diff --git a/app/lib/sanitize_config.rb b/app/lib/sanitize_config.rb
index 9f5bf0125..2b5d554b5 100644
--- a/app/lib/sanitize_config.rb
+++ b/app/lib/sanitize_config.rb
@@ -2,7 +2,7 @@
 
 class Sanitize
   module Config
-    HTTP_PROTOCOLS ||= ['http', 'https', 'dat', 'dweb', 'ipfs', 'ipns', 'ssb', 'gopher', :relative].freeze
+    HTTP_PROTOCOLS ||= ['http', 'https', 'dat', 'dweb', 'ipfs', 'ipns', 'ssb', 'gopher', 'xmpp', 'magnet', :relative].freeze
 
     CLASS_WHITELIST_TRANSFORMER = lambda do |env|
       node = env[:node]
diff --git a/app/lib/search_query_transformer.rb b/app/lib/search_query_transformer.rb
index 6a299f59d..e07ebfffe 100644
--- a/app/lib/search_query_transformer.rb
+++ b/app/lib/search_query_transformer.rb
@@ -78,7 +78,7 @@ class SearchQueryTransformer < Parslet::Transform
     elsif clause[:shortcode]
       TermClause.new(prefix, operator, ":#{clause[:term]}:")
     elsif clause[:phrase]
-      PhraseClause.new(prefix, operator, clause[:phrase].map { |p| p[:term].to_s }.join(' '))
+      PhraseClause.new(prefix, operator, clause[:phrase].is_a?(Array) ? clause[:phrase].map { |p| p[:term].to_s }.join(' ') : clause[:phrase].to_s)
     else
       raise "Unexpected clause type: #{clause}"
     end
diff --git a/app/lib/spam_check.rb b/app/lib/spam_check.rb
index 5b40514fd..652d03615 100644
--- a/app/lib/spam_check.rb
+++ b/app/lib/spam_check.rb
@@ -143,7 +143,7 @@ class SpamCheck
   end
 
   def trusted?
-    @account.trust_level > Account::TRUST_LEVELS[:untrusted]
+    @account.trust_level > Account::TRUST_LEVELS[:untrusted] || (@account.local? && @account.user_staff?)
   end
 
   def no_unsolicited_mentions?
diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb
index c30bec80b..88a11f761 100644
--- a/app/mailers/user_mailer.rb
+++ b/app/mailers/user_mailer.rb
@@ -6,6 +6,7 @@ class UserMailer < Devise::Mailer
   helper :accounts
   helper :application
   helper :instance
+  helper :statuses
 
   add_template_helper RoutingHelper
 
diff --git a/app/models/account.rb b/app/models/account.rb
index 25cde6d6c..e46888415 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -50,7 +50,7 @@
 
 class Account < ApplicationRecord
   USERNAME_RE = /[a-z0-9_]+([a-z0-9_\.-]+[a-z0-9_]+)?/i
-  MENTION_RE  = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE})(?:@[a-z0-9\.\-]+[a-z0-9]+)?)/i
+  MENTION_RE  = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE})(?:@[[:word:]\.\-]+[a-z0-9]+)?)/i
 
   include AccountAssociations
   include AccountAvatar
@@ -74,14 +74,13 @@ class Account < ApplicationRecord
   enum protocol: [:ostatus, :activitypub]
 
   validates :username, presence: true
+  validates_with UniqueUsernameValidator, if: -> { will_save_change_to_username? }
 
   # Remote user validations
-  validates :username, uniqueness: { scope: :domain, case_sensitive: true }, if: -> { !local? && will_save_change_to_username? }
   validates :username, format: { with: /\A#{USERNAME_RE}\z/i }, if: -> { !local? && 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' }
-  validates_with UniqueUsernameValidator, if: -> { local? && will_save_change_to_username? }
   validates_with UnreservedUsernameValidator, if: -> { local? && will_save_change_to_username? }
   validates :display_name, length: { maximum: MAX_DISPLAY_NAME_LENGTH }, if: -> { local? && will_save_change_to_display_name? }
   validates :note, note_length: { maximum: MAX_NOTE_LENGTH }, if: -> { local? && will_save_change_to_note? }
@@ -168,6 +167,10 @@ class Account < ApplicationRecord
     local? ? username : "#{username}@#{domain}"
   end
 
+  def pretty_acct
+    local? ? username : "#{username}@#{Addressable::IDNA.to_unicode(domain)}"
+  end
+
   def local_username_and_domain
     "#{username}@#{Rails.configuration.x.local_domain}"
   end
@@ -312,10 +315,6 @@ class Account < ApplicationRecord
     self.fields = tmp
   end
 
-  def subscription(webhook_url)
-    @subscription ||= OStatus2::Subscription.new(remote_url, secret: secret, webhook: webhook_url, hub: hub_url)
-  end
-
   def save_with_optional_media!
     save!
   rescue ActiveRecord::RecordInvalid
@@ -478,6 +477,12 @@ class Account < ApplicationRecord
       records
     end
 
+    def from_text(text)
+      return [] if text.blank?
+
+      text.scan(MENTION_RE).map { |match| match.first.split('@', 2) }.uniq.map { |(username, domain)| EntityCache.instance.mention(username, domain) }
+    end
+
     private
 
     def generate_query_for_search(terms)
diff --git a/app/models/account_filter.rb b/app/models/account_filter.rb
index c3b1fe08d..c7bf07787 100644
--- a/app/models/account_filter.rb
+++ b/app/models/account_filter.rb
@@ -1,6 +1,21 @@
 # frozen_string_literal: true
 
 class AccountFilter
+  KEYS = %i(
+    local
+    remote
+    by_domain
+    active
+    pending
+    silenced
+    suspended
+    username
+    display_name
+    email
+    ip
+    staff
+  ).freeze
+
   attr_reader :params
 
   def initialize(params)
@@ -50,7 +65,7 @@ class AccountFilter
     when 'email'
       accounts_with_users.merge User.matches_email(value)
     when 'ip'
-      valid_ip?(value) ? accounts_with_users.where('users.current_sign_in_ip <<= ?', value) : Account.none
+      valid_ip?(value) ? accounts_with_users.merge(User.matches_ip(value)) : Account.none
     when 'staff'
       accounts_with_users.merge User.staff
     else
diff --git a/app/models/announcement.rb b/app/models/announcement.rb
new file mode 100644
index 000000000..d99502f44
--- /dev/null
+++ b/app/models/announcement.rb
@@ -0,0 +1,86 @@
+# frozen_string_literal: true
+
+# == Schema Information
+#
+# Table name: announcements
+#
+#  id           :bigint(8)        not null, primary key
+#  text         :text             default(""), not null
+#  published    :boolean          default(FALSE), not null
+#  all_day      :boolean          default(FALSE), not null
+#  scheduled_at :datetime
+#  starts_at    :datetime
+#  ends_at      :datetime
+#  created_at   :datetime         not null
+#  updated_at   :datetime         not null
+#  published_at :datetime
+#
+
+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 :chronological, -> { order(Arel.sql('COALESCE(announcements.starts_at, announcements.scheduled_at, announcements.published_at, announcements.created_at) ASC')) }
+
+  has_many :announcement_mutes, dependent: :destroy
+  has_many :announcement_reactions, dependent: :destroy
+
+  validates :text, presence: true
+  validates :starts_at, presence: true, if: -> { ends_at.present? }
+  validates :ends_at, presence: true, if: -> { starts_at.present? }
+
+  before_validation :set_all_day
+  before_validation :set_published, on: :create
+
+  def publish!
+    update!(published: true, published_at: Time.now.utc, scheduled_at: nil)
+  end
+
+  def unpublish!
+    update!(published: false, scheduled_at: nil)
+  end
+
+  def time_range?
+    starts_at.present? && ends_at.present?
+  end
+
+  def mentions
+    @mentions ||= Account.from_text(text)
+  end
+
+  def tags
+    @tags ||= Tag.find_or_create_by_names(Extractor.extract_hashtags(text))
+  end
+
+  def emojis
+    @emojis ||= CustomEmoji.from_text(text)
+  end
+
+  def reactions(account = nil)
+    records = begin
+      scope = announcement_reactions.group(:announcement_id, :name, :custom_emoji_id).order(Arel.sql('MIN(created_at) ASC'))
+
+      if account.nil?
+        scope.select('name, custom_emoji_id, count(*) as count, false as me')
+      else
+        scope.select("name, custom_emoji_id, count(*) as count, exists(select 1 from announcement_reactions r where r.account_id = #{account.id} and r.announcement_id = announcement_reactions.announcement_id and r.name = announcement_reactions.name) as me")
+      end
+    end
+
+    ActiveRecord::Associations::Preloader.new.preload(records, :custom_emoji)
+    records
+  end
+
+  private
+
+  def set_all_day
+    self.all_day = false if starts_at.blank? || ends_at.blank?
+  end
+
+  def set_published
+    return unless scheduled_at.blank? || scheduled_at.past?
+
+    self.published = true
+    self.published_at = Time.now.utc
+  end
+end
diff --git a/app/models/announcement_filter.rb b/app/models/announcement_filter.rb
new file mode 100644
index 000000000..950852460
--- /dev/null
+++ b/app/models/announcement_filter.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+class AnnouncementFilter
+  KEYS = %i(
+    published
+    unpublished
+  ).freeze
+
+  attr_reader :params
+
+  def initialize(params)
+    @params = params
+  end
+
+  def results
+    scope = Announcement.unscoped
+
+    params.each do |key, value|
+      next if key.to_s == 'page'
+
+      scope.merge!(scope_for(key, value.to_s.strip)) if value.present?
+    end
+
+    scope.chronological
+  end
+
+  private
+
+  def scope_for(key, _value)
+    case key.to_s
+    when 'published'
+      Announcement.published
+    when 'unpublished'
+      Announcement.unpublished
+    else
+      raise "Unknown filter: #{key}"
+    end
+  end
+end
diff --git a/app/models/announcement_mute.rb b/app/models/announcement_mute.rb
new file mode 100644
index 000000000..46fda2f5d
--- /dev/null
+++ b/app/models/announcement_mute.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+# == Schema Information
+#
+# Table name: announcement_mutes
+#
+#  id              :bigint(8)        not null, primary key
+#  account_id      :bigint(8)
+#  announcement_id :bigint(8)
+#  created_at      :datetime         not null
+#  updated_at      :datetime         not null
+#
+
+class AnnouncementMute < ApplicationRecord
+  belongs_to :account
+  belongs_to :announcement, inverse_of: :announcement_mutes
+
+  validates :account_id, uniqueness: { scope: :announcement_id }
+end
diff --git a/app/models/announcement_reaction.rb b/app/models/announcement_reaction.rb
new file mode 100644
index 000000000..d22771034
--- /dev/null
+++ b/app/models/announcement_reaction.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+# == Schema Information
+#
+# Table name: announcement_reactions
+#
+#  id              :bigint(8)        not null, primary key
+#  account_id      :bigint(8)
+#  announcement_id :bigint(8)
+#  name            :string           default(""), not null
+#  custom_emoji_id :bigint(8)
+#  created_at      :datetime         not null
+#  updated_at      :datetime         not null
+#
+
+class AnnouncementReaction < ApplicationRecord
+  after_commit :queue_publish
+
+  belongs_to :account
+  belongs_to :announcement, inverse_of: :announcement_reactions
+  belongs_to :custom_emoji, optional: true
+
+  validates :name, presence: true
+  validates_with ReactionValidator
+
+  before_validation :set_custom_emoji
+
+  private
+
+  def set_custom_emoji
+    self.custom_emoji = CustomEmoji.local.find_by(disabled: false, shortcode: name) if name.present?
+  end
+
+  def queue_publish
+    PublishAnnouncementReactionWorker.perform_async(announcement_id, name) unless announcement.destroyed?
+  end
+end
diff --git a/app/models/backup.rb b/app/models/backup.rb
index 8eeb1748a..d242fd62c 100644
--- a/app/models/backup.rb
+++ b/app/models/backup.rb
@@ -7,11 +7,11 @@
 #  user_id           :bigint(8)
 #  dump_file_name    :string
 #  dump_content_type :string
-#  dump_file_size    :bigint
 #  dump_updated_at   :datetime
 #  processed         :boolean          default(FALSE), not null
 #  created_at        :datetime         not null
 #  updated_at        :datetime         not null
+#  dump_file_size    :bigint(8)
 #
 
 class Backup < ApplicationRecord
diff --git a/app/models/bookmark.rb b/app/models/bookmark.rb
index 01dc48ee7..916261a17 100644
--- a/app/models/bookmark.rb
+++ b/app/models/bookmark.rb
@@ -3,11 +3,11 @@
 #
 # Table name: bookmarks
 #
-#  id         :integer          not null, primary key
+#  id         :bigint(8)        not null, primary key
+#  account_id :bigint(8)        not null
+#  status_id  :bigint(8)        not null
 #  created_at :datetime         not null
 #  updated_at :datetime         not null
-#  account_id :integer          not null
-#  status_id  :integer          not null
 #
 
 class Bookmark < ApplicationRecord
diff --git a/app/models/concerns/account_finder_concern.rb b/app/models/concerns/account_finder_concern.rb
index a54c2174d..04b2c981b 100644
--- a/app/models/concerns/account_finder_concern.rb
+++ b/app/models/concerns/account_finder_concern.rb
@@ -48,7 +48,7 @@ module AccountFinderConcern
     end
 
     def with_usernames
-      Account.where.not(username: '')
+      Account.where.not(Account.arel_table[:username].lower.eq '')
     end
 
     def matching_username
@@ -56,11 +56,7 @@ module AccountFinderConcern
     end
 
     def matching_domain
-      if domain.nil?
-        Account.where(domain: nil)
-      else
-        Account.where(Account.arel_table[:domain].lower.eq domain.to_s.downcase)
-      end
+      Account.where(Account.arel_table[:domain].lower.eq(domain.nil? ? nil : domain.to_s.downcase))
     end
   end
 end
diff --git a/app/models/concerns/account_interactions.rb b/app/models/concerns/account_interactions.rb
index f27d39483..14bcf7bb1 100644
--- a/app/models/concerns/account_interactions.rb
+++ b/app/models/concerns/account_interactions.rb
@@ -84,6 +84,7 @@ module AccountInteractions
     has_many :muted_by, -> { order('mutes.id desc') }, through: :muted_by_relationships, source: :account
     has_many :conversation_mutes, dependent: :destroy
     has_many :domain_blocks, class_name: 'AccountDomainBlock', dependent: :destroy
+    has_many :announcement_mutes, dependent: :destroy
   end
 
   def follow!(other_account, reblogs: nil, uri: nil)
diff --git a/app/models/concerns/attachmentable.rb b/app/models/concerns/attachmentable.rb
index 3bbc6453c..43ff8ac12 100644
--- a/app/models/concerns/attachmentable.rb
+++ b/app/models/concerns/attachmentable.rb
@@ -9,6 +9,7 @@ module Attachmentable
   GIF_MATRIX_LIMIT = 921_600    # 1280x720px
 
   included do
+    before_post_process :obfuscate_file_name
     before_post_process :set_file_extensions
     before_post_process :check_image_dimensions
     before_post_process :set_file_content_type
@@ -68,4 +69,14 @@ module Attachmentable
   rescue Terrapin::CommandLineError
     ''
   end
+
+  def obfuscate_file_name
+    self.class.attachment_definitions.each_key do |attachment_name|
+      attachment = send(attachment_name)
+
+      next if attachment.blank? || attachment.queued_for_write[:original].blank?
+
+      attachment.instance_write :file_name, SecureRandom.hex(8) + File.extname(attachment.instance_read(:file_name))
+    end
+  end
 end
diff --git a/app/models/concerns/status_threading_concern.rb b/app/models/concerns/status_threading_concern.rb
index 15eb695cd..a0ead1995 100644
--- a/app/models/concerns/status_threading_concern.rb
+++ b/app/models/concerns/status_threading_concern.rb
@@ -81,12 +81,12 @@ module StatusThreadingConcern
   end
 
   def find_statuses_from_tree_path(ids, account, promote: false)
-    statuses    = statuses_with_accounts(ids).to_a
+    statuses    = Status.with_accounts(ids).to_a
     account_ids = statuses.map(&:account_id).uniq
     domains     = statuses.map(&:account_domain).compact.uniq
     relations   = relations_map_for_account(account, account_ids, domains)
 
-    statuses.reject! { |status| filter_from_context?(status, account, relations) }
+    statuses.reject! { |status| StatusFilter.new(status, account, relations).filtered? }
 
     # Order ancestors/descendants by tree path
     statuses.sort_by! { |status| ids.index(status.id) }
@@ -125,12 +125,4 @@ module StatusThreadingConcern
       domain_blocking_by_domain: Account.domain_blocking_map_by_domain(domains, account.id),
     }
   end
-
-  def statuses_with_accounts(ids)
-    Status.where(id: ids).includes(:account)
-  end
-
-  def filter_from_context?(status, account, relations)
-    StatusFilter.new(status, account, relations).filtered?
-  end
 end
diff --git a/app/models/custom_emoji.rb b/app/models/custom_emoji.rb
index 0dacaf654..d177cf281 100644
--- a/app/models/custom_emoji.rb
+++ b/app/models/custom_emoji.rb
@@ -67,7 +67,7 @@ class CustomEmoji < ApplicationRecord
   end
 
   class << self
-    def from_text(text, domain)
+    def from_text(text, domain = nil)
       return [] if text.blank?
 
       shortcodes = text.scan(SCAN_RE).map(&:first).uniq
diff --git a/app/models/custom_emoji_filter.rb b/app/models/custom_emoji_filter.rb
index 15b8da1d1..414e1fcdd 100644
--- a/app/models/custom_emoji_filter.rb
+++ b/app/models/custom_emoji_filter.rb
@@ -1,6 +1,13 @@
 # frozen_string_literal: true
 
 class CustomEmojiFilter
+  KEYS = %i(
+    local
+    remote
+    by_domain
+    shortcode
+  ).freeze
+
   attr_reader :params
 
   def initialize(params)
diff --git a/app/models/custom_filter.rb b/app/models/custom_filter.rb
index 382562fb8..8df8a4fbf 100644
--- a/app/models/custom_filter.rb
+++ b/app/models/custom_filter.rb
@@ -20,6 +20,7 @@ class CustomFilter < ApplicationRecord
     notifications
     public
     thread
+    account
   ).freeze
 
   include Expireable
diff --git a/app/models/domain_block.rb b/app/models/domain_block.rb
index 4e865b850..f0a5bd296 100644
--- a/app/models/domain_block.rb
+++ b/app/models/domain_block.rb
@@ -54,7 +54,7 @@ class DomainBlock < ApplicationRecord
       segments = uri.normalized_host.split('.')
       variants = segments.map.with_index { |_, i| segments[i..-1].join('.') }
 
-      where(domain: variants[0..-2]).order(Arel.sql('char_length(domain) desc')).first
+      where(domain: variants).order(Arel.sql('char_length(domain) desc')).first
     end
   end
 
diff --git a/app/models/instance_filter.rb b/app/models/instance_filter.rb
index 8bfab826d..9c467bc27 100644
--- a/app/models/instance_filter.rb
+++ b/app/models/instance_filter.rb
@@ -1,6 +1,11 @@
 # frozen_string_literal: true
 
 class InstanceFilter
+  KEYS = %i(
+    limited
+    by_domain
+  ).freeze
+
   attr_reader :params
 
   def initialize(params)
diff --git a/app/models/invite_filter.rb b/app/models/invite_filter.rb
index 7d89bad4a..9685d4abb 100644
--- a/app/models/invite_filter.rb
+++ b/app/models/invite_filter.rb
@@ -1,6 +1,11 @@
 # frozen_string_literal: true
 
 class InviteFilter
+  KEYS = %i(
+    available
+    expired
+  ).freeze
+
   attr_reader :params
 
   def initialize(params)
diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb
index 880599028..6a0b892f6 100644
--- a/app/models/media_attachment.rb
+++ b/app/models/media_attachment.rb
@@ -142,6 +142,7 @@ class MediaAttachment < ApplicationRecord
 
   validates :account, presence: true
   validates :description, length: { maximum: MAX_DESCRIPTION_LENGTH }, if: :local?
+  validates :file, presence: true, if: :local?
 
   scope :attached,   -> { where.not(status_id: nil).or(where.not(scheduled_status_id: nil)) }
   scope :unattached, -> { where(status_id: nil, scheduled_status_id: nil) }
@@ -202,9 +203,12 @@ class MediaAttachment < ApplicationRecord
   end
 
   after_commit :reset_parent_cache, on: :update
+
   before_create :prepare_description, unless: :local?
   before_create :set_shortcode
+
   before_post_process :set_type_and_extension
+
   before_save :set_meta
 
   class << self
diff --git a/app/models/relationship_filter.rb b/app/models/relationship_filter.rb
new file mode 100644
index 000000000..e6859bf3d
--- /dev/null
+++ b/app/models/relationship_filter.rb
@@ -0,0 +1,120 @@
+# frozen_string_literal: true
+
+class RelationshipFilter
+  KEYS = %i(
+    relationship
+    status
+    by_domain
+    activity
+    order
+    location
+  ).freeze
+
+  attr_reader :params, :account
+
+  def initialize(account, params)
+    @account = account
+    @params  = params
+
+    set_defaults!
+  end
+
+  def results
+    scope = scope_for('relationship', params['relationship'].to_s.strip)
+
+    params.each do |key, value|
+      next if key.to_s == 'page'
+
+      scope.merge!(scope_for(key.to_s, value.to_s.strip)) if value.present?
+    end
+
+    scope
+  end
+
+  private
+
+  def set_defaults!
+    params['relationship'] = 'following' if params['relationship'].blank?
+    params['order']        = 'recent' if params['order'].blank?
+  end
+
+  def scope_for(key, value)
+    case key
+    when 'relationship'
+      relationship_scope(value)
+    when 'by_domain'
+      by_domain_scope(value)
+    when 'location'
+      location_scope(value)
+    when 'status'
+      status_scope(value)
+    when 'order'
+      order_scope(value)
+    when 'activity'
+      activity_scope(value)
+    else
+      raise "Unknown filter: #{key}"
+    end
+  end
+
+  def relationship_scope(value)
+    case value
+    when 'following'
+      account.following.eager_load(:account_stat).reorder(nil)
+    when 'followed_by'
+      account.followers.eager_load(:account_stat).reorder(nil)
+    when 'mutual'
+      account.followers.eager_load(:account_stat).reorder(nil).merge(Account.where(id: account.following))
+    when 'invited'
+      Account.joins(user: :invite).merge(Invite.where(user: account.user)).eager_load(:account_stat).reorder(nil)
+    else
+      raise "Unknown relationship: #{value}"
+    end
+  end
+
+  def by_domain_scope(value)
+    Account.where(domain: value)
+  end
+
+  def location_scope(value)
+    case value
+    when 'local'
+      Account.local
+    when 'remote'
+      Account.remote
+    else
+      raise "Unknown location: #{value}"
+    end
+  end
+
+  def status_scope(value)
+    case value
+    when 'moved'
+      Account.where.not(moved_to_account_id: nil)
+    when 'primary'
+      Account.where(moved_to_account_id: nil)
+    else
+      raise "Unknown status: #{value}"
+    end
+  end
+
+  def order_scope(value)
+    case value
+    when 'active'
+      Account.by_recent_status
+    when 'recent'
+      params[:relationship] == 'invited' ? Account.recent : Follow.recent
+    else
+      raise "Unknown order: #{value}"
+    end
+  end
+
+  def activity_scope(value)
+    case value
+    when 'dormant'
+      AccountStat.where(last_status_at: nil).or(AccountStat.where(AccountStat.arel_table[:last_status_at].lt(1.month.ago)))
+    else
+      raise "Unknown activity: #{value}"
+    end
+  end
+end
diff --git a/app/models/report_filter.rb b/app/models/report_filter.rb
index abf53cbab..c32d4359e 100644
--- a/app/models/report_filter.rb
+++ b/app/models/report_filter.rb
@@ -1,6 +1,13 @@
 # frozen_string_literal: true
 
 class ReportFilter
+  KEYS = %i(
+    resolved
+    account_id
+    target_account_id
+    by_target_domain
+  ).freeze
+
   attr_reader :params
 
   def initialize(params)
diff --git a/app/models/status.rb b/app/models/status.rb
index c189d19bf..f4284f771 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -87,6 +87,7 @@ class Status < ApplicationRecord
   scope :remote, -> { where(local: false).where.not(uri: nil) }
   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 :with_public_visibility, -> { where(visibility: :public) }
@@ -200,8 +201,12 @@ class Status < ApplicationRecord
   def title
     if destroyed?
       "#{account.acct} deleted status"
+    elsif reblog?
+      preview = sensitive ? '<sensitive>' : text.slice(0, 10).split("\n")[0]
+      "#{account.acct} shared #{reblog.account.acct}'s: #{preview}"
     else
-      reblog? ? "#{account.acct} shared a status by #{reblog.account.acct}" : "New status by #{account.acct}"
+      preview = sensitive ? '<sensitive>' : text.slice(0, 20).split("\n")[0]
+      "#{account.acct}: #{preview}"
     end
   end
 
diff --git a/app/models/tag_filter.rb b/app/models/tag_filter.rb
index 8921e186b..a9ff5b703 100644
--- a/app/models/tag_filter.rb
+++ b/app/models/tag_filter.rb
@@ -1,6 +1,16 @@
 # frozen_string_literal: true
 
 class TagFilter
+  KEYS = %i(
+    directory
+    reviewed
+    unreviewed
+    pending_review
+    popular
+    active
+    name
+  ).freeze
+
   attr_reader :params
 
   def initialize(params)
diff --git a/app/models/user.rb b/app/models/user.rb
index 49cfc25ca..cd8a6f273 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -93,6 +93,7 @@ class User < ApplicationRecord
   scope :inactive, -> { where(arel_table[:current_sign_in_at].lt(ACTIVE_DURATION.ago)) }
   scope :active, -> { confirmed.where(arel_table[:current_sign_in_at].gteq(ACTIVE_DURATION.ago)).joins(:account).where(accounts: { suspended_at: nil }) }
   scope :matches_email, ->(value) { where(arel_table[:email].matches("#{value}%")) }
+  scope :matches_ip, ->(value) { left_joins(:session_activations).where('users.current_sign_in_ip <<= ?', value).or(left_joins(:session_activations).where('users.last_sign_in_ip <<= ?', value)).or(left_joins(:session_activations).where('session_activations.ip <<= ?', value)) }
   scope :emailable, -> { confirmed.enabled.joins(:account).merge(Account.searchable) }
 
   before_validation :sanitize_languages
@@ -128,9 +129,7 @@ class User < ApplicationRecord
   end
 
   def disable!
-    update!(disabled: true,
-            last_sign_in_at: current_sign_in_at,
-            current_sign_in_at: nil)
+    update!(disabled: true)
   end
 
   def enable!
@@ -247,7 +246,7 @@ class User < ApplicationRecord
                                  ip: request.remote_ip).session_id
   end
 
-  def exclusive_session(id)
+  def clear_other_sessions(id)
     session_activations.exclusive(id)
   end
 
@@ -290,6 +289,21 @@ class User < ApplicationRecord
     setting_display_media == 'hide_all'
   end
 
+  def recent_ips
+    @recent_ips ||= begin
+      arr = []
+
+      session_activations.each do |session_activation|
+        arr << [session_activation.updated_at, session_activation.ip]
+      end
+
+      arr << [current_sign_in_at, current_sign_in_ip] if current_sign_in_ip.present?
+      arr << [last_sign_in_at, last_sign_in_ip] if last_sign_in_ip.present?
+
+      arr.sort_by { |pair| pair.first || Time.now.utc }.uniq(&:last).reverse!
+    end
+  end
+
   protected
 
   def send_devise_notification(notification, *args)
diff --git a/app/policies/announcement_policy.rb b/app/policies/announcement_policy.rb
new file mode 100644
index 000000000..0a4e4575c
--- /dev/null
+++ b/app/policies/announcement_policy.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AnnouncementPolicy < ApplicationPolicy
+  def index?
+    staff?
+  end
+
+  def create?
+    admin?
+  end
+
+  def update?
+    admin?
+  end
+
+  def destroy?
+    admin?
+  end
+end
diff --git a/app/serializers/rest/account_serializer.rb b/app/serializers/rest/account_serializer.rb
index 7bdb5d7ff..4e497cdbd 100644
--- a/app/serializers/rest/account_serializer.rb
+++ b/app/serializers/rest/account_serializer.rb
@@ -24,6 +24,10 @@ class REST::AccountSerializer < ActiveModel::Serializer
     object.id.to_s
   end
 
+  def acct
+    object.pretty_acct
+  end
+
   def note
     Formatter.instance.simplified_format(object)
   end
@@ -52,6 +56,10 @@ class REST::AccountSerializer < ActiveModel::Serializer
     object.moved? && object.moved_to_account.moved_to_account_id.nil?
   end
 
+  def last_status_at
+    object.last_status_at&.to_date&.iso8601
+  end
+
   def followers_count
     (Setting.hide_followers_count || object.user&.setting_hide_followers_count) ? -1 : object.followers_count
   end
diff --git a/app/serializers/rest/announcement_serializer.rb b/app/serializers/rest/announcement_serializer.rb
new file mode 100644
index 000000000..ae72f9ace
--- /dev/null
+++ b/app/serializers/rest/announcement_serializer.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+class REST::AnnouncementSerializer < ActiveModel::Serializer
+  attributes :id, :content, :starts_at, :ends_at, :all_day,
+             :published_at, :updated_at
+
+  attribute :read, if: :current_user?
+
+  has_many :mentions
+  has_many :tags, serializer: REST::StatusSerializer::TagSerializer
+  has_many :emojis, serializer: REST::CustomEmojiSerializer
+  has_many :reactions, serializer: REST::ReactionSerializer
+
+  def current_user?
+    !current_user.nil?
+  end
+
+  def id
+    object.id.to_s
+  end
+
+  def read
+    object.announcement_mutes.where(account: current_user.account).exists?
+  end
+
+  def content
+    Formatter.instance.linkify(object.text)
+  end
+
+  def reactions
+    object.reactions(current_user&.account)
+  end
+
+  class AccountSerializer < ActiveModel::Serializer
+    attributes :id, :username, :url, :acct
+
+    def id
+      object.id.to_s
+    end
+
+    def url
+      ActivityPub::TagManager.instance.url_for(object)
+    end
+  end
+end
diff --git a/app/serializers/rest/reaction_serializer.rb b/app/serializers/rest/reaction_serializer.rb
new file mode 100644
index 000000000..1a5dca018
--- /dev/null
+++ b/app/serializers/rest/reaction_serializer.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+class REST::ReactionSerializer < ActiveModel::Serializer
+  include RoutingHelper
+
+  attributes :name, :count
+
+  attribute :me, if: :current_user?
+  attribute :url, if: :custom_emoji?
+  attribute :static_url, if: :custom_emoji?
+
+  def count
+    object.respond_to?(:count) ? object.count : 0
+  end
+
+  def current_user?
+    !current_user.nil?
+  end
+
+  def custom_emoji?
+    object.custom_emoji.present?
+  end
+
+  def url
+    full_asset_url(object.custom_emoji.image.url)
+  end
+
+  def static_url
+    full_asset_url(object.custom_emoji.image.url(:static))
+  end
+end
diff --git a/app/services/activitypub/process_collection_service.rb b/app/services/activitypub/process_collection_service.rb
index a2a2e7071..e6ccaccc9 100644
--- a/app/services/activitypub/process_collection_service.rb
+++ b/app/services/activitypub/process_collection_service.rb
@@ -37,7 +37,7 @@ class ActivityPub::ProcessCollectionService < BaseService
   end
 
   def process_item(item)
-    activity = ActivityPub::Activity.factory(item, @account, @options)
+    activity = ActivityPub::Activity.factory(item, @account, **@options)
     activity&.perform
   end
 
diff --git a/app/services/backup_service.rb b/app/services/backup_service.rb
index 0b57b6d0c..ab6d090a0 100644
--- a/app/services/backup_service.rb
+++ b/app/services/backup_service.rb
@@ -166,7 +166,7 @@ class BackupService < BaseService
         io.write(buffer)
       end
     end
-  rescue Errno::ENOENT
+  rescue Errno::ENOENT, Seahorse::Client::NetworkingError
     Rails.logger.warn "Could not backup file #{filename}: file not found"
   end
 end
diff --git a/app/services/fetch_resource_service.rb b/app/services/fetch_resource_service.rb
index 34382d279..abe7766d4 100644
--- a/app/services/fetch_resource_service.rb
+++ b/app/services/fetch_resource_service.rb
@@ -3,7 +3,7 @@
 class FetchResourceService < BaseService
   include JsonLdHelper
 
-  ACCEPT_HEADER = 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams", text/html'
+  ACCEPT_HEADER = 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams", text/html;q=0.1'
 
   def call(url)
     return if url.blank?
diff --git a/app/services/process_mentions_service.rb b/app/services/process_mentions_service.rb
index 19de37717..3c257451c 100644
--- a/app/services/process_mentions_service.rb
+++ b/app/services/process_mentions_service.rb
@@ -14,7 +14,16 @@ class ProcessMentionsService < BaseService
     mentions = []
 
     status.text = status.text.gsub(Account::MENTION_RE) do |match|
-      username, domain  = Regexp.last_match(1).split('@')
+      username, domain = Regexp.last_match(1).split('@')
+
+      domain = begin
+        if TagManager.instance.local_domain?(domain)
+          nil
+        else
+          TagManager.instance.normalize_domain(domain)
+        end
+      end
+
       mentioned_account = Account.find_remote(username, domain)
 
       if mention_undeliverable?(mentioned_account)
diff --git a/app/services/resolve_url_service.rb b/app/services/resolve_url_service.rb
index 79b1bad0c..1a2b0d60c 100644
--- a/app/services/resolve_url_service.rb
+++ b/app/services/resolve_url_service.rb
@@ -12,6 +12,8 @@ class ResolveURLService < BaseService
       process_local_url
     elsif !fetched_resource.nil?
       process_url
+    elsif @on_behalf_of.present?
+      process_url_from_db
     end
   end
 
@@ -24,15 +26,19 @@ class ResolveURLService < BaseService
       status = FetchRemoteStatusService.new.call(resource_url, body)
       authorize_with @on_behalf_of, status, :show? unless status.nil?
       status
-    elsif fetched_resource.nil? && @on_behalf_of.present?
-      # It may happen that the resource is a private toot, and thus not fetchable,
-      # but we can return the toot if we already know about it.
-      status = Status.find_by(uri: @url) || Status.find_by(url: @url)
-      authorize_with @on_behalf_of, status, :show? unless status.nil?
-      status
     end
   end
 
+  def process_url_from_db
+    # It may happen that the resource is a private toot, and thus not fetchable,
+    # but we can return the toot if we already know about it.
+    status = Status.find_by(uri: @url) || Status.find_by(url: @url)
+    authorize_with @on_behalf_of, status, :show? unless status.nil?
+    status
+  rescue Mastodon::NotPermittedError
+    nil
+  end
+
   def fetched_resource
     @fetched_resource ||= FetchResourceService.new.call(@url)
   end
diff --git a/app/services/vote_service.rb b/app/services/vote_service.rb
index cb7dce6e8..19e453332 100644
--- a/app/services/vote_service.rb
+++ b/app/services/vote_service.rb
@@ -20,7 +20,7 @@ class VoteService < BaseService
 
         ApplicationRecord.transaction do
           @choices.each do |choice|
-            @votes << @poll.votes.create!(account: @account, choice: choice)
+            @votes << @poll.votes.create!(account: @account, choice: Integer(choice))
           end
         end
       else
diff --git a/app/validators/reaction_validator.rb b/app/validators/reaction_validator.rb
new file mode 100644
index 000000000..494b6041b
--- /dev/null
+++ b/app/validators/reaction_validator.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+class ReactionValidator < ActiveModel::Validator
+  SUPPORTED_EMOJIS = Oj.load(File.read(Rails.root.join('app', 'javascript', 'mastodon', 'features', 'emoji', 'emoji_map.json'))).keys.freeze
+
+  LIMIT = 8
+
+  def validate(reaction)
+    return if reaction.name.blank?
+
+    reaction.errors.add(:name, I18n.t('reactions.errors.unrecognized_emoji')) if reaction.custom_emoji_id.blank? && !unicode_emoji?(reaction.name)
+    reaction.errors.add(:base, I18n.t('reactions.errors.limit_reached')) if new_reaction?(reaction) && limit_reached?(reaction)
+  end
+
+  private
+
+  def unicode_emoji?(name)
+    SUPPORTED_EMOJIS.include?(name)
+  end
+
+  def new_reaction?(reaction)
+    !reaction.announcement.announcement_reactions.where(name: reaction.name).exists?
+  end
+
+  def limit_reached?(reaction)
+    reaction.announcement.announcement_reactions.where.not(name: reaction.name).count('distinct name') >= LIMIT
+  end
+end
diff --git a/app/validators/unique_username_validator.rb b/app/validators/unique_username_validator.rb
index 4e24e3f5f..f87eb06ba 100644
--- a/app/validators/unique_username_validator.rb
+++ b/app/validators/unique_username_validator.rb
@@ -7,8 +7,9 @@ class UniqueUsernameValidator < ActiveModel::Validator
     return if account.username.nil?
 
     normalized_username = account.username.downcase
+    normalized_domain = account.domain&.downcase
 
-    scope = Account.where(domain: nil).where('lower(username) = ?', normalized_username)
+    scope = Account.where(Account.arel_table[:username].lower.eq normalized_username).where(Account.arel_table[:domain].lower.eq normalized_domain)
     scope = scope.where.not(id: account.id) if account.persisted?
 
     account.errors.add(:username, :taken) if scope.exists?
diff --git a/app/validators/vote_validator.rb b/app/validators/vote_validator.rb
index 2e1818bdb..b1692562d 100644
--- a/app/validators/vote_validator.rb
+++ b/app/validators/vote_validator.rb
@@ -4,10 +4,18 @@ 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.invalid_choice')) if invalid_choice?(vote)
+
     if vote.poll.multiple? && vote.poll.votes.where(account: vote.account, choice: vote.choice).exists?
       vote.errors.add(:base, I18n.t('polls.errors.already_voted'))
     elsif !vote.poll.multiple? && vote.poll.votes.where(account: vote.account).exists?
       vote.errors.add(:base, I18n.t('polls.errors.already_voted'))
     end
   end
+
+  private
+
+  def invalid_choice?(vote)
+    vote.choice.negative? || vote.choice >= vote.poll.options.size
+  end
 end
diff --git a/app/views/accounts/show.html.haml b/app/views/accounts/show.html.haml
index 9c26dbabc..c312fe2bd 100644
--- a/app/views/accounts/show.html.haml
+++ b/app/views/accounts/show.html.haml
@@ -2,8 +2,6 @@
   = "#{display_name(@account)} (@#{@account.local_username_and_domain})"
 
 - content_for :header_tags do
-  %meta{ name: 'description', content: account_description(@account) }/
-
   - if @account.user&.setting_noindex
     %meta{ name: 'robots', content: 'noindex, noarchive' }/
 
diff --git a/app/views/admin/accounts/index.html.haml b/app/views/admin/accounts/index.html.haml
index 7e9adb3ff..3a85324c9 100644
--- a/app/views/admin/accounts/index.html.haml
+++ b/app/views/admin/accounts/index.html.haml
@@ -22,7 +22,7 @@
 
 = form_tag admin_accounts_url, method: 'GET', class: 'simple_form' do
   .fields-group
-    - Admin::FilterHelper::ACCOUNT_FILTERS.each do |key|
+    - AccountFilter::KEYS.each do |key|
       - if params[key].present?
         = hidden_field_tag key, params[key]
 
diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml
index 9f1e3816b..a83f77134 100644
--- a/app/views/admin/accounts/show.html.haml
+++ b/app/views/admin/accounts/show.html.haml
@@ -41,7 +41,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_followers_path(@account.id) do
+    = link_to admin_account_relationships_path(@account.id, location: 'local', relationship: 'followed_by') do
       .dashboard__counters__num= number_with_delimiter @account.local_followers_count
       .dashboard__counters__label= t 'admin.accounts.followers'
   %div
@@ -139,12 +139,12 @@
               %time.formatted{ datetime: @account.created_at.iso8601, title: l(@account.created_at) }= l @account.created_at
             %td
 
-          %tr
-            %th= t('admin.accounts.most_recent_ip')
-            %td= @account.user_current_sign_in_ip
-            %td
-              - if @account.user_current_sign_in_ip
-                = table_link_to 'search', t('admin.accounts.search_same_ip'), admin_accounts_path(ip: @account.user_current_sign_in_ip)
+          - @account.user.recent_ips.each_with_index do |(_, ip), i|
+            %tr
+              - if i.zero?
+                %th{ rowspan: @account.user.recent_ips.size }= t('admin.accounts.most_recent_ip')
+              %td= ip
+              %td= table_link_to 'search', t('admin.accounts.search_same_ip'), admin_accounts_path(ip: ip)
 
           %tr
             %th= t('admin.accounts.most_recent_activity')
diff --git a/app/views/admin/announcements/_announcement.html.haml b/app/views/admin/announcements/_announcement.html.haml
new file mode 100644
index 000000000..1c7f89ef2
--- /dev/null
+++ b/app/views/admin/announcements/_announcement.html.haml
@@ -0,0 +1,19 @@
+.announcements-list__item
+  = link_to edit_admin_announcement_path(announcement), class: 'announcements-list__item__title' do
+    = truncate(announcement.text)
+
+  .announcements-list__item__action-bar
+    .announcements-list__item__meta
+      - if announcement.scheduled_at.present?
+        = t('admin.announcements.scheduled_for', time: l(announcement.scheduled_at))
+      - else
+        = l(announcement.created_at)
+
+    %div
+      - if can?(:update, announcement)
+        - if announcement.published?
+          = table_link_to 'toggle-off', t('admin.announcements.unpublish'), unpublish_admin_announcement_path(announcement), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }
+        - else
+          = table_link_to 'toggle-on', t('admin.announcements.publish'), publish_admin_announcement_path(announcement), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }
+
+      = table_link_to 'trash', t('generic.delete'), admin_announcement_path(announcement), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') } if can?(:destroy, announcement)
diff --git a/app/views/admin/announcements/edit.html.haml b/app/views/admin/announcements/edit.html.haml
new file mode 100644
index 000000000..5f56db5e7
--- /dev/null
+++ b/app/views/admin/announcements/edit.html.haml
@@ -0,0 +1,22 @@
+- content_for :page_title do
+  = t('.title')
+
+= simple_form_for @announcement, url: admin_announcement_path(@announcement) do |f|
+  = render 'shared/error_messages', object: @announcement
+
+  .fields-group
+    = f.input :starts_at, include_blank: true, wrapper: :with_block_label
+    = f.input :ends_at, include_blank: true, wrapper: :with_block_label
+
+  .fields-group
+    = f.input :all_day, as: :boolean, wrapper: :with_label
+
+  .fields-group
+    = f.input :text, wrapper: :with_block_label
+
+  - unless @announcement.published?
+    .fields-group
+      = f.input :scheduled_at, include_blank: true, wrapper: :with_block_label
+
+  .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
new file mode 100644
index 000000000..40f02b914
--- /dev/null
+++ b/app/views/admin/announcements/index.html.haml
@@ -0,0 +1,22 @@
+- content_for :page_title do
+  = t('admin.announcements.title')
+
+- content_for :heading_actions do
+  = link_to t('admin.announcements.new.title'), new_admin_announcement_path, class: 'button'
+
+.filters
+  .filter-subset
+    %strong= t('admin.relays.status')
+    %ul
+      %li= filter_link_to t('generic.all'), published: nil, unpublished: nil
+      %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
+    = t 'admin.announcements.empty'
+- else
+  .announcements-list
+    = render partial: 'announcement', collection: @announcements
+
+= paginate @announcements
+
diff --git a/app/views/admin/announcements/new.html.haml b/app/views/admin/announcements/new.html.haml
new file mode 100644
index 000000000..a5298c5f6
--- /dev/null
+++ b/app/views/admin/announcements/new.html.haml
@@ -0,0 +1,21 @@
+- content_for :page_title do
+  = t('.title')
+
+= simple_form_for @announcement, url: admin_announcements_path do |f|
+  = render 'shared/error_messages', object: @announcement
+
+  .fields-group
+    = f.input :starts_at, include_blank: true, wrapper: :with_block_label
+    = f.input :ends_at, include_blank: true, wrapper: :with_block_label
+
+  .fields-group
+    = f.input :all_day, as: :boolean, wrapper: :with_label
+
+  .fields-group
+    = f.input :text, wrapper: :with_block_label
+
+  .fields-group
+    = f.input :scheduled_at, include_blank: true, wrapper: :with_block_label
+
+  .actions
+    = f.button :button, t('.create'), type: :submit
diff --git a/app/views/admin/custom_emojis/index.html.haml b/app/views/admin/custom_emojis/index.html.haml
index 4fbadee90..d3705a36e 100644
--- a/app/views/admin/custom_emojis/index.html.haml
+++ b/app/views/admin/custom_emojis/index.html.haml
@@ -1,6 +1,9 @@
 - content_for :page_title do
   = t('admin.custom_emojis.title')
 
+- content_for :heading_actions do
+  = link_to t('admin.custom_emojis.upload'), new_admin_custom_emoji_path, class: 'button'
+
 .filters
   .filter-subset
     %strong= t('admin.accounts.location.title')
@@ -19,7 +22,7 @@
 
 = form_tag admin_custom_emojis_url, method: 'GET', class: 'simple_form' do
   .fields-group
-    - Admin::FilterHelper::CUSTOM_EMOJI_FILTERS.each do |key|
+    - CustomEmojiFilter::KEYS.each do |key|
       = hidden_field_tag key, params[key] if params[key].present?
 
     - %i(shortcode by_domain).each do |key|
@@ -33,7 +36,7 @@
 = form_for(@form, url: batch_admin_custom_emojis_path) do |f|
   = hidden_field_tag :page, params[:page] || 1
 
-  - Admin::FilterHelper::CUSTOM_EMOJI_FILTERS.each do |key|
+  - CustomEmojiFilter::KEYS.each do |key|
     = hidden_field_tag key, params[key] if params[key].present?
 
   .batch-table
@@ -78,6 +81,3 @@
 
 = paginate @custom_emojis
 
-%hr.spacer/
-
-= link_to t('admin.custom_emojis.upload'), new_admin_custom_emoji_path, class: 'button'
diff --git a/app/views/admin/email_domain_blocks/index.html.haml b/app/views/admin/email_domain_blocks/index.html.haml
index 7bb204e52..6015cfac0 100644
--- a/app/views/admin/email_domain_blocks/index.html.haml
+++ b/app/views/admin/email_domain_blocks/index.html.haml
@@ -1,14 +1,19 @@
 - content_for :page_title do
   = t('admin.email_domain_blocks.title')
 
-.table-wrapper
-  %table.table
-    %thead
-      %tr
-        %th= t('admin.email_domain_blocks.domain')
-        %th
-    %tbody
-      = render @email_domain_blocks
+- content_for :heading_actions do
+  = link_to t('admin.email_domain_blocks.add_new'), new_admin_email_domain_block_path, class: 'button'
+
+- if @email_domain_blocks.empty?
+  %div.muted-hint.center-text=t 'admin.email_domain_blocks.empty'
+- else
+  .table-wrapper
+    %table.table
+      %thead
+        %tr
+          %th= t('admin.email_domain_blocks.domain')
+          %th
+      %tbody
+        = render @email_domain_blocks
 
 = paginate @email_domain_blocks
-= link_to t('admin.email_domain_blocks.add_new'), new_admin_email_domain_block_path, class: 'button'
diff --git a/app/views/admin/followers/index.html.haml b/app/views/admin/followers/index.html.haml
deleted file mode 100644
index 25f1f290f..000000000
--- a/app/views/admin/followers/index.html.haml
+++ /dev/null
@@ -1,28 +0,0 @@
-- content_for :page_title do
-  = t('admin.followers.title', acct: @account.acct)
-
-.filters
-  .filter-subset
-    %strong= t('admin.accounts.location.title')
-    %ul
-      %li= link_to t('admin.accounts.location.local'), admin_account_followers_path(@account.id), class: 'selected'
-  .back-link{ style: 'flex: 1 1 auto; text-align: right' }
-    = link_to admin_account_path(@account.id) do
-      = fa_icon 'chevron-left fw'
-      = t('admin.followers.back_to_account')
-
-%hr.spacer/
-
-.table-wrapper
-  %table.table
-    %thead
-      %tr
-        %th= t('admin.accounts.username')
-        %th= t('admin.accounts.role')
-        %th= t('admin.accounts.most_recent_ip')
-        %th= t('admin.accounts.most_recent_activity')
-        %th
-    %tbody
-      = render partial: 'admin/accounts/account', collection: @followers
-
-= paginate @followers
diff --git a/app/views/admin/instances/index.html.haml b/app/views/admin/instances/index.html.haml
index 1d85aa75e..0b299acc5 100644
--- a/app/views/admin/instances/index.html.haml
+++ b/app/views/admin/instances/index.html.haml
@@ -19,9 +19,8 @@
 - unless whitelist_mode?
   = form_tag admin_instances_url, method: 'GET', class: 'simple_form' do
     .fields-group
-      - Admin::FilterHelper::INSTANCES_FILTERS.each do |key|
-        - if params[key].present?
-          = hidden_field_tag key, params[key]
+      - InstanceFilter::KEYS.each do |key|
+        = hidden_field_tag key, params[key] if params[key].present?
 
       - %i(by_domain).each do |key|
         .input.string.optional
diff --git a/app/views/admin/relationships/index.html.haml b/app/views/admin/relationships/index.html.haml
new file mode 100644
index 000000000..3afaff615
--- /dev/null
+++ b/app/views/admin/relationships/index.html.haml
@@ -0,0 +1,39 @@
+- content_for :page_title do
+  = t('admin.relationships.title', acct: @account.acct)
+
+.filters
+  .filter-subset
+    %strong= t 'relationships.relationship'
+    %ul
+      %li= filter_link_to t('relationships.following'), relationship: nil
+      %li= filter_link_to t('relationships.followers'), relationship: 'followed_by'
+      %li= filter_link_to t('relationships.mutual'), relationship: 'mutual'
+      %li= filter_link_to t('relationships.invited'), relationship: 'invited'
+
+  .filter-subset
+    %strong= t('admin.accounts.location.title')
+    %ul
+      %li= filter_link_to t('admin.accounts.moderation.all'), location: nil
+      %li= filter_link_to t('admin.accounts.location.local'), location: 'local'
+      %li= filter_link_to t('admin.accounts.location.remote'), location: 'remote'
+
+  .back-link{ style: 'flex: 1 1 auto; text-align: right' }
+    = link_to admin_account_path(@account.id) do
+      = fa_icon 'chevron-left fw'
+      = t('admin.statuses.back_to_account')
+
+%hr.spacer/
+
+.table-wrapper
+  %table.table
+    %thead
+      %tr
+        %th= t('admin.accounts.username')
+        %th= t('admin.accounts.role')
+        %th= t('admin.accounts.most_recent_ip')
+        %th= t('admin.accounts.most_recent_activity')
+        %th
+    %tbody
+      = render partial: 'admin/accounts/account', collection: @accounts
+
+= paginate @accounts
diff --git a/app/views/admin/reports/_status.html.haml b/app/views/admin/reports/_status.html.haml
index 425d315e1..fa15796d2 100644
--- a/app/views/admin/reports/_status.html.haml
+++ b/app/views/admin/reports/_status.html.haml
@@ -14,9 +14,12 @@
     - unless status.proper.media_attachments.empty?
       - if status.proper.media_attachments.first.video?
         - video = status.proper.media_attachments.first
-        = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: !current_account&.user&.show_all_media? && status.proper.sensitive? || current_account&.user&.hide_all_media?, width: 610, height: 343, inline: true, alt: video.description
+        = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), blurhash: video.blurhash, sensitive: status.proper.sensitive?, visible: false, width: 610, height: 343, inline: true, alt: video.description
+      - elsif status.proper.media_attachments.first.audio?
+        - audio = status.proper.media_attachments.first
+        = react_component :audio, src: audio.file.url(:original), height: 110, alt: audio.description, duration: audio.file.meta.dig(:original, :duration)
       - else
-        = react_component :media_gallery, height: 343, sensitive: !current_account&.user&.show_all_media? && status.proper.sensitive? || current_account&.user&.hide_all_media?, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, media: status.proper.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }
+        = react_component :media_gallery, height: 343, sensitive: status.proper.sensitive?, visible: false, media: status.proper.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }
 
     .detailed-status__meta
       = link_to ActivityPub::TagManager.instance.url_for(status), class: 'detailed-status__datetime', target: stream_link_target, rel: 'noopener noreferrer' do
diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml
index 30c7549b0..0263b80fb 100644
--- a/app/views/admin/reports/index.html.haml
+++ b/app/views/admin/reports/index.html.haml
@@ -10,9 +10,8 @@
 
 = form_tag admin_reports_url, method: 'GET', class: 'simple_form' do
   .fields-group
-    - Admin::FilterHelper::REPORT_FILTERS.each do |key|
-      - if params[key].present?
-        = hidden_field_tag key, params[key]
+    - ReportFilter::KEYS.each do |key|
+      = hidden_field_tag key, params[key] if params[key].present?
 
     - %i(by_target_domain).each do |key|
       .input.string.optional
diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml
index 4321bb199..b12ea4270 100644
--- a/app/views/admin/reports/show.html.haml
+++ b/app/views/admin/reports/show.html.haml
@@ -1,7 +1,7 @@
 - content_for :page_title do
   = t('admin.reports.report', id: @report.id)
 
-- content_for :page_heading_actions do
+- content_for :heading_actions do
   - if @report.unresolved?
     = link_to t('admin.reports.mark_as_resolved'), resolve_admin_report_path(@report), method: :post, class: 'button'
   - else
diff --git a/app/views/admin/tags/_tag.html.haml b/app/views/admin/tags/_tag.html.haml
index 670f3bc05..287d28e53 100644
--- a/app/views/admin/tags/_tag.html.haml
+++ b/app/views/admin/tags/_tag.html.haml
@@ -1,6 +1,7 @@
 .batch-table__row
-  %label.batch-table__row__select.batch-table__row__select--aligned.batch-checkbox
-    = f.check_box :tag_ids, { multiple: true, include_hidden: false }, tag.id
+  - if batch_available
+    %label.batch-table__row__select.batch-table__row__select--aligned.batch-checkbox
+      = f.check_box :tag_ids, { multiple: true, include_hidden: false }, tag.id
 
   .directory__tag
     = link_to admin_tag_path(tag.id) do
diff --git a/app/views/admin/tags/index.html.haml b/app/views/admin/tags/index.html.haml
index 298ac59e9..d20ed80f8 100644
--- a/app/views/admin/tags/index.html.haml
+++ b/app/views/admin/tags/index.html.haml
@@ -25,7 +25,7 @@
 
 = form_tag admin_tags_url, method: 'GET', class: 'simple_form' do
   .fields-group
-    - Admin::FilterHelper::TAGS_FILTERS.each do |key|
+    - TagFilter::KEYS.each do |key|
       = hidden_field_tag key, params[key] if params[key].present?
 
     - %i(name).each do |key|
@@ -40,32 +40,32 @@
 
 = form_for(@form, url: batch_admin_tags_path) do |f|
   = hidden_field_tag :page, params[:page] || 1
-  = hidden_field_tag :name, params[:name] if params[:name].present?
 
-  - Admin::FilterHelper::TAGS_FILTERS.each do |key|
+  - TagFilter::KEYS.each do |key|
     = hidden_field_tag key, params[key] if params[key].present?
 
   .batch-table.optional
     .batch-table__toolbar
-      %label.batch-table__toolbar__select.batch-checkbox-all
-        = check_box_tag :batch_checkbox_all, nil, false
-      .batch-table__toolbar__actions
-        - if params[:pending_review] == '1'
+      - if params[:pending_review] == '1' || params[:unreviewed] == '1'
+        %label.batch-table__toolbar__select.batch-checkbox-all
+          = check_box_tag :batch_checkbox_all, nil, false
+        .batch-table__toolbar__actions
           = f.button safe_join([fa_icon('check'), t('admin.accounts.approve')]), name: :approve, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
 
           = f.button safe_join([fa_icon('times'), t('admin.accounts.reject')]), name: :reject, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
-        - else
+      - else
+        .batch-table__toolbar__actions
           %span.neutral-hint= t('generic.no_batch_actions_available')
 
     .batch-table__body
       - if @tags.empty?
         = nothing_here 'nothing-here--under-tabs'
       - else
-        = render partial: 'tag', collection: @tags, locals: { f: f }
+        = render partial: 'tag', collection: @tags, locals: { f: f, batch_available: params[:pending_review] == '1' || params[:unreviewed] == '1' }
 
 = paginate @tags
 
-- if params[:pending_review] == '1'
+- if params[:pending_review] == '1' || params[:unreviewed] == '1'
   %hr.spacer/
 
   %div{ style: 'overflow: hidden' }
diff --git a/app/views/directories/index.html.haml b/app/views/directories/index.html.haml
index dee99475a..ecf12b649 100644
--- a/app/views/directories/index.html.haml
+++ b/app/views/directories/index.html.haml
@@ -47,7 +47,7 @@
             %small= t('accounts.followers', count: account.followers_count).downcase
           .accounts-table__count
             - if account.last_status_at.present?
-              %time.time-ago{ datetime: account.last_status_at.iso8601, title: l(account.last_status_at) }= l account.last_status_at
+              %time.time-ago{ datetime: account.last_status_at.to_date.iso8601, title: l(account.last_status_at.to_date) }= l account.last_status_at.to_date
             - else
               = t('accounts.never_active')
 
diff --git a/app/views/filters/index.html.haml b/app/views/filters/index.html.haml
index 18ebee570..b4d5333aa 100644
--- a/app/views/filters/index.html.haml
+++ b/app/views/filters/index.html.haml
@@ -1,20 +1,24 @@
 - content_for :page_title do
   = t('filters.index.title')
 
-.table-wrapper
-  %table.table
-    %thead
-      %tr
-        %th= t('simple_form.labels.defaults.phrase')
-        %th= t('simple_form.labels.defaults.context')
-        %th
-    %tbody
-      - @filters.each do |filter|
-        %tr
-          %td= filter.phrase
-          %td= filter.context.map { |context| I18n.t("filters.contexts.#{context}") }.join(', ')
-          %td
-            = table_link_to 'pencil', t('filters.edit.title'), edit_filter_path(filter)
-            = table_link_to 'times', t('filters.index.delete'), filter_path(filter), method: :delete
+- content_for :heading_actions do
+  = link_to t('filters.new.title'), new_filter_path, class: 'button'
 
-= link_to t('filters.new.title'), new_filter_path, class: 'button'
+- if @filters.empty?
+  %div.muted-hint.center-text= t 'filters.index.empty'
+- else
+  .table-wrapper
+    %table.table
+      %thead
+        %tr
+          %th= t('simple_form.labels.defaults.phrase')
+          %th= t('simple_form.labels.defaults.context')
+          %th
+      %tbody
+        - @filters.each do |filter|
+          %tr
+            %td= filter.phrase
+            %td= filter.context.map { |context| I18n.t("filters.contexts.#{context}") }.join(', ')
+            %td
+              = table_link_to 'pencil', t('filters.edit.title'), edit_filter_path(filter)
+              = table_link_to 'times', t('filters.index.delete'), filter_path(filter), method: :delete
diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml
index 9fceb54eb..ec3629dd8 100644
--- a/app/views/layouts/admin.html.haml
+++ b/app/views/layouts/admin.html.haml
@@ -1,3 +1,6 @@
+- content_for :header_tags do
+  = render_initial_state
+
 - content_for :content do
   .admin-wrapper
     .sidebar-wrapper
@@ -21,9 +24,9 @@
         .content-heading
           %h2= yield :page_title
 
-          - if :page_heading_actions
+          - if :heading_actions
             .content-heading-actions
-              = yield :page_heading_actions
+              = yield :heading_actions
 
         = render 'application/flashes'
 
diff --git a/app/views/layouts/embedded.html.haml b/app/views/layouts/embedded.html.haml
index 33e3714f8..6695b12dd 100644
--- a/app/views/layouts/embedded.html.haml
+++ b/app/views/layouts/embedded.html.haml
@@ -10,6 +10,7 @@
     - if storage_host?
       %link{ rel: 'dns-prefetch', href: storage_host }/
 
+    = render_initial_state
     = javascript_pack_tag "locales", integrity: true, crossorigin: 'anonymous'
     - if @theme
       - if @theme[:supported_locales].include? I18n.locale.to_s
@@ -21,3 +22,6 @@
 
   %body.embed
     = yield
+
+    %div{ style: 'display: none'}
+      = render file: Rails.root.join('app', 'javascript', 'images', 'logo_transparent.svg')
diff --git a/app/views/relationships/_account.html.haml b/app/views/relationships/_account.html.haml
index 6c22deb51..af5a4aaf7 100644
--- a/app/views/relationships/_account.html.haml
+++ b/app/views/relationships/_account.html.haml
@@ -14,7 +14,7 @@
             %small= t('accounts.followers', count: account.followers_count).downcase
           %td.accounts-table__count
             - if account.last_status_at.present?
-              %time.time-ago{ datetime: account.last_status_at.iso8601, title: l(account.last_status_at) }= l account.last_status_at
+              %time.time-ago{ datetime: account.last_status_at.to_date.iso8601, title: l(account.last_status_at.to_date) }= l account.last_status_at
             - else
               \-
             %small= t('accounts.last_active')
diff --git a/app/views/relationships/show.html.haml b/app/views/relationships/show.html.haml
index 408390a35..a6f8cdc15 100644
--- a/app/views/relationships/show.html.haml
+++ b/app/views/relationships/show.html.haml
@@ -30,10 +30,9 @@
 
 = form_for(@form, url: relationships_path, method: :patch) do |f|
   = hidden_field_tag :page, params[:page] || 1
-  = hidden_field_tag :relationship, params[:relationship]
-  = hidden_field_tag :status, params[:status]
-  = hidden_field_tag :activity, params[:activity]
-  = hidden_field_tag :order, params[:order]
+
+  - RelationshipFilter::KEYS.each do |key|
+    = hidden_field_tag key, params[key] if params[key].present?
 
   .batch-table
     .batch-table__toolbar
diff --git a/app/views/settings/applications/index.html.haml b/app/views/settings/applications/index.html.haml
index 919472c2e..a1f904a3a 100644
--- a/app/views/settings/applications/index.html.haml
+++ b/app/views/settings/applications/index.html.haml
@@ -1,20 +1,25 @@
 - content_for :page_title do
   = t('doorkeeper.applications.index.title')
 
-.table-wrapper
-  %table.table
-    %thead
-      %tr
-        %th= t('doorkeeper.applications.index.application')
-        %th= t('doorkeeper.applications.index.scopes')
-        %th
-    %tbody
-      - @applications.each do |application|
+- content_for :heading_actions do
+  = 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'
+- else
+  .table-wrapper
+    %table.table
+      %thead
         %tr
-          %td= link_to application.name, settings_application_path(application)
-          %th= application.scopes
-          %td
-            = table_link_to 'times', t('doorkeeper.applications.index.delete'), settings_application_path(application), method: :delete, data: { confirm: t('doorkeeper.applications.confirmations.destroy') }
+          %th= t('doorkeeper.applications.index.application')
+          %th= t('doorkeeper.applications.index.scopes')
+          %th
+      %tbody
+        - @applications.each do |application|
+          %tr
+            %td= link_to application.name, settings_application_path(application)
+            %th= application.scopes
+            %td
+              = table_link_to 'times', t('doorkeeper.applications.index.delete'), settings_application_path(application), method: :delete, data: { confirm: t('doorkeeper.applications.confirmations.destroy') }
 
 = paginate @applications
-= link_to t('doorkeeper.applications.index.new'), new_settings_application_path, class: 'button'
diff --git a/app/views/settings/preferences/appearance/show.html.haml b/app/views/settings/preferences/appearance/show.html.haml
index f1e3d2e97..f460cebba 100644
--- a/app/views/settings/preferences/appearance/show.html.haml
+++ b/app/views/settings/preferences/appearance/show.html.haml
@@ -5,6 +5,10 @@
   .fields-group
     = f.input :locale, collection: I18n.available_locales, wrapper: :with_label, include_blank: false, label_method: lambda { |locale| human_locale(locale) }, selected: I18n.locale, hint: false
 
+  - unless I18n.locale == :en
+    .flash-message{ style: "text-align: unset; color: unset" }
+      #{t 'appearance.localization.body'} #{content_tag(:a, t('appearance.localization.guide_link_text'), href: t('appearance.localization.guide_link'), target: "_blank", rel: "noopener", style: "text-decoration: underline")}
+
   %h4= t 'appearance.advanced_web_interface'
 
   %p.hint= t 'appearance.advanced_web_interface_hint'
diff --git a/app/views/statuses/_detailed_status.html.haml b/app/views/statuses/_detailed_status.html.haml
index 3fa52d3f2..021390e47 100644
--- a/app/views/statuses/_detailed_status.html.haml
+++ b/app/views/statuses/_detailed_status.html.haml
@@ -29,14 +29,14 @@
   - if !status.media_attachments.empty?
     - if status.media_attachments.first.video?
       - video = status.media_attachments.first
-      = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), blurhash: video.blurhash, sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, width: 670, height: 380, detailed: true, inline: true, alt: video.description do
+      = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), blurhash: video.blurhash, sensitive: status.sensitive?, width: 670, height: 380, detailed: true, inline: true, alt: video.description do
         = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments }
     - elsif status.media_attachments.first.audio?
       - audio = status.media_attachments.first
       = react_component :audio, src: audio.file.url(:original), height: 130, alt: audio.description, preload: true, duration: audio.file.meta.dig(:original, :duration) do
         = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments }
     - else
-      = react_component :media_gallery, height: 380, sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, standalone: true, 'autoPlayGif': current_account&.user&.setting_auto_play_gif || autoplay, 'reduceMotion': current_account&.user&.setting_reduce_motion, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do
+      = react_component :media_gallery, height: 380, sensitive: status.sensitive?, standalone: true, autoplay: autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do
         = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments }
   - elsif status.preview_card
     = react_component :card, 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json
diff --git a/app/views/statuses/_simple_status.html.haml b/app/views/statuses/_simple_status.html.haml
index e9c22d9ba..8a418a1d5 100644
--- a/app/views/statuses/_simple_status.html.haml
+++ b/app/views/statuses/_simple_status.html.haml
@@ -33,18 +33,22 @@
   - if !status.media_attachments.empty?
     - if status.media_attachments.first.video?
       - video = status.media_attachments.first
-      = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), blurhash: video.blurhash, sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, width: 610, height: 343, inline: true, alt: video.description do
+      = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), blurhash: video.blurhash, sensitive: status.sensitive?, width: 610, height: 343, inline: true, alt: video.description do
         = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments }
     - elsif status.media_attachments.first.audio?
       - audio = status.media_attachments.first
       = react_component :audio, src: audio.file.url(:original), height: 110, alt: audio.description, duration: audio.file.meta.dig(:original, :duration) do
         = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments }
     - else
-      = react_component :media_gallery, height: 343, sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, 'autoPlayGif': current_account&.user&.setting_auto_play_gif || autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do
+      = react_component :media_gallery, height: 343, sensitive: status.sensitive?, autoplay: autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do
         = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments }
   - elsif status.preview_card
     = react_component :card, 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json
 
+  - if !status.in_reply_to_id.nil? && status.in_reply_to_account_id == status.account.id
+    = link_to ActivityPub::TagManager.instance.url_for(status), class: 'status__content__read-more-button', target: stream_link_target, rel: 'noopener noreferrer' do
+      = t 'statuses.show_thread'
+
   .status__action-bar
     .status__action-bar__counter
       = link_to remote_interaction_path(status, type: :reply), class: 'status__action-bar-button icon-button modal-button', style: 'font-size: 18px; width: 23.1429px; height: 23.1429px; line-height: 23.15px;' do
diff --git a/app/views/statuses/embed.html.haml b/app/views/statuses/embed.html.haml
index 6f2ec646f..2f111f53f 100644
--- a/app/views/statuses/embed.html.haml
+++ b/app/views/statuses/embed.html.haml
@@ -1,3 +1,2 @@
-- cache @status do
-  .activity-stream.activity-stream--headless
-    = render 'status', status: @status, centered: true, autoplay: @autoplay
+.activity-stream.activity-stream--headless
+  = render 'status', status: @status, centered: true, autoplay: @autoplay
diff --git a/app/workers/activitypub/delivery_worker.rb b/app/workers/activitypub/delivery_worker.rb
index 5457d9d4b..196af4af1 100644
--- a/app/workers/activitypub/delivery_worker.rb
+++ b/app/workers/activitypub/delivery_worker.rb
@@ -50,9 +50,13 @@ class ActivityPub::DeliveryWorker
       end
     end
 
-    light.with_threshold(STOPLIGHT_FAILURE_THRESHOLD)
-         .with_cool_off_time(STOPLIGHT_COOLDOWN)
-         .run
+    begin
+      light.with_threshold(STOPLIGHT_FAILURE_THRESHOLD)
+           .with_cool_off_time(STOPLIGHT_COOLDOWN)
+           .run
+    rescue Stoplight::Error::RedLight => e
+      raise e.class, e.message, e.backtrace.first(3)
+    end
   end
 
   def failure_tracker
diff --git a/app/workers/publish_announcement_reaction_worker.rb b/app/workers/publish_announcement_reaction_worker.rb
new file mode 100644
index 000000000..418dc7127
--- /dev/null
+++ b/app/workers/publish_announcement_reaction_worker.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class PublishAnnouncementReactionWorker
+  include Sidekiq::Worker
+  include Redisable
+
+  def perform(announcement_id, name)
+    announcement = Announcement.find(announcement_id)
+
+    reaction,  = announcement.announcement_reactions.where(name: name).group(:announcement_id, :name, :custom_emoji_id).select('name, custom_emoji_id, count(*) as count, false as me')
+    reaction ||= announcement.announcement_reactions.new(name: name)
+
+    payload = InlineRenderer.render(reaction, nil, :reaction).tap { |h| h[:announcement_id] = announcement_id.to_s }
+    payload = Oj.dump(event: :'announcement.reaction', payload: payload)
+
+    FeedManager.instance.with_active_accounts do |account|
+      redis.publish("timeline:#{account.id}", payload) if redis.exists("subscribed:timeline:#{account.id}")
+    end
+  rescue ActiveRecord::RecordNotFound
+    true
+  end
+end
diff --git a/app/workers/publish_scheduled_announcement_worker.rb b/app/workers/publish_scheduled_announcement_worker.rb
new file mode 100644
index 000000000..efca39d3d
--- /dev/null
+++ b/app/workers/publish_scheduled_announcement_worker.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class PublishScheduledAnnouncementWorker
+  include Sidekiq::Worker
+  include Redisable
+
+  def perform(announcement_id)
+    announcement = Announcement.find(announcement_id)
+
+    announcement.publish! unless announcement.published?
+
+    payload = InlineRenderer.render(announcement, nil, :announcement)
+    payload = Oj.dump(event: :announcement, payload: payload)
+
+    FeedManager.instance.with_active_accounts do |account|
+      redis.publish("timeline:#{account.id}", payload) if redis.exists("subscribed:timeline:#{account.id}")
+    end
+  end
+end
diff --git a/app/workers/refollow_worker.rb b/app/workers/refollow_worker.rb
index 12f2bf671..9b07ce1b5 100644
--- a/app/workers/refollow_worker.rb
+++ b/app/workers/refollow_worker.rb
@@ -7,15 +7,18 @@ class RefollowWorker
 
   def perform(target_account_id)
     target_account = Account.find(target_account_id)
-    return unless target_account.protocol == :activitypub
+    return unless target_account.activitypub?
+
+    target_account.passive_relationships.where(account: Account.where(domain: nil)).includes(:account).reorder(nil).find_each do |follow|
+      reblogs = follow.show_reblogs?
 
-    target_account.followers.where(domain: nil).reorder(nil).find_each do |follower|
       # Locally unfollow remote account
+      follower = follow.account
       follower.unfollow!(target_account)
 
       # Schedule re-follow
       begin
-        FollowService.new.call(follower, target_account)
+        FollowService.new.call(follower, target_account, reblogs: reblogs)
       rescue Mastodon::NotPermittedError, ActiveRecord::RecordNotFound, Mastodon::UnexpectedResponseError, HTTP::Error, OpenSSL::SSL::SSLError
         next
       end
diff --git a/app/workers/scheduler/scheduled_statuses_scheduler.rb b/app/workers/scheduler/scheduled_statuses_scheduler.rb
index 1772a246b..9cfe949de 100644
--- a/app/workers/scheduler/scheduled_statuses_scheduler.rb
+++ b/app/workers/scheduler/scheduled_statuses_scheduler.rb
@@ -6,14 +6,38 @@ class Scheduler::ScheduledStatusesScheduler
   sidekiq_options unique: :until_executed, retry: 0
 
   def perform
+    publish_scheduled_statuses!
+    publish_scheduled_announcements!
+    unpublish_expired_announcements!
+  end
+
+  private
+
+  def publish_scheduled_statuses!
     due_statuses.find_each do |scheduled_status|
       PublishScheduledStatusWorker.perform_at(scheduled_status.scheduled_at, scheduled_status.id)
     end
   end
 
-  private
-
   def due_statuses
     ScheduledStatus.where('scheduled_at <= ?', Time.now.utc + PostStatusService::MIN_SCHEDULE_OFFSET)
   end
+
+  def publish_scheduled_announcements!
+    due_announcements.find_each do |announcement|
+      PublishScheduledAnnouncementWorker.perform_at(announcement.scheduled_at, announcement.id)
+    end
+  end
+
+  def due_announcements
+    Announcement.unpublished.where('scheduled_at IS NOT NULL AND scheduled_at <= ?', Time.now.utc + PostStatusService::MIN_SCHEDULE_OFFSET)
+  end
+
+  def unpublish_expired_announcements!
+    expired_announcements.in_batches.update_all(published: false, scheduled_at: nil)
+  end
+
+  def expired_announcements
+    Announcement.published.where('ends_at IS NOT NULL AND ends_at <= ?', Time.now.utc)
+  end
 end
diff --git a/app/workers/unpublish_announcement_worker.rb b/app/workers/unpublish_announcement_worker.rb
new file mode 100644
index 000000000..e99d70cf8
--- /dev/null
+++ b/app/workers/unpublish_announcement_worker.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class UnpublishAnnouncementWorker
+  include Sidekiq::Worker
+  include Redisable
+
+  def perform(announcement_id)
+    payload = Oj.dump(event: :'announcement.delete', payload: announcement_id.to_s)
+
+    FeedManager.instance.with_active_accounts do |account|
+      redis.publish("timeline:#{account.id}", payload) if redis.exists("subscribed:timeline:#{account.id}")
+    end
+  end
+end