about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.env.nanobox4
-rw-r--r--.env.production.sample4
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock6
-rw-r--r--app/helpers/application_helper.rb15
-rw-r--r--app/javascript/core/admin.js7
-rw-r--r--app/javascript/mastodon/components/extended_video_player.js1
-rw-r--r--app/javascript/mastodon/components/media_gallery.js1
-rw-r--r--app/javascript/mastodon/components/status.js1
-rw-r--r--app/javascript/mastodon/containers/status_container.js2
-rw-r--r--app/javascript/mastodon/features/account/components/header.js4
-rw-r--r--app/javascript/mastodon/features/compose/components/upload_button.js2
-rw-r--r--app/javascript/mastodon/features/report/components/status_check_box.js1
-rw-r--r--app/javascript/mastodon/features/status/components/detailed_status.js1
-rw-r--r--app/javascript/mastodon/features/status/index.js2
-rw-r--r--app/javascript/mastodon/features/video/index.js1
-rw-r--r--app/javascript/mastodon/locales/ar.json2
-rw-r--r--app/javascript/mastodon/locales/ast.json2
-rw-r--r--app/javascript/mastodon/locales/bg.json2
-rw-r--r--app/javascript/mastodon/locales/ca.json2
-rw-r--r--app/javascript/mastodon/locales/co.json2
-rw-r--r--app/javascript/mastodon/locales/cs.json4
-rw-r--r--app/javascript/mastodon/locales/da.json16
-rw-r--r--app/javascript/mastodon/locales/de.json2
-rw-r--r--app/javascript/mastodon/locales/defaultMessages.json12
-rw-r--r--app/javascript/mastodon/locales/el.json2
-rw-r--r--app/javascript/mastodon/locales/en.json6
-rw-r--r--app/javascript/mastodon/locales/eo.json2
-rw-r--r--app/javascript/mastodon/locales/es.json2
-rw-r--r--app/javascript/mastodon/locales/eu.json2
-rw-r--r--app/javascript/mastodon/locales/fa.json2
-rw-r--r--app/javascript/mastodon/locales/fi.json2
-rw-r--r--app/javascript/mastodon/locales/fr.json6
-rw-r--r--app/javascript/mastodon/locales/gl.json2
-rw-r--r--app/javascript/mastodon/locales/he.json2
-rw-r--r--app/javascript/mastodon/locales/hr.json2
-rw-r--r--app/javascript/mastodon/locales/hu.json2
-rw-r--r--app/javascript/mastodon/locales/hy.json2
-rw-r--r--app/javascript/mastodon/locales/id.json2
-rw-r--r--app/javascript/mastodon/locales/io.json2
-rw-r--r--app/javascript/mastodon/locales/it.json2
-rw-r--r--app/javascript/mastodon/locales/ja.json4
-rw-r--r--app/javascript/mastodon/locales/ka.json2
-rw-r--r--app/javascript/mastodon/locales/ko.json8
-rw-r--r--app/javascript/mastodon/locales/nl.json2
-rw-r--r--app/javascript/mastodon/locales/no.json2
-rw-r--r--app/javascript/mastodon/locales/oc.json10
-rw-r--r--app/javascript/mastodon/locales/pl.json2
-rw-r--r--app/javascript/mastodon/locales/pt-BR.json8
-rw-r--r--app/javascript/mastodon/locales/pt.json2
-rw-r--r--app/javascript/mastodon/locales/ru.json2
-rw-r--r--app/javascript/mastodon/locales/sk.json2
-rw-r--r--app/javascript/mastodon/locales/sl.json2
-rw-r--r--app/javascript/mastodon/locales/sr-Latn.json2
-rw-r--r--app/javascript/mastodon/locales/sr.json2
-rw-r--r--app/javascript/mastodon/locales/sv.json2
-rw-r--r--app/javascript/mastodon/locales/te.json2
-rw-r--r--app/javascript/mastodon/locales/th.json2
-rw-r--r--app/javascript/mastodon/locales/tr.json2
-rw-r--r--app/javascript/mastodon/locales/uk.json2
-rw-r--r--app/javascript/mastodon/locales/zh-CN.json2
-rw-r--r--app/javascript/mastodon/locales/zh-HK.json2
-rw-r--r--app/javascript/mastodon/locales/zh-TW.json2
-rw-r--r--app/javascript/packs/public.js5
-rw-r--r--app/lib/activitypub/activity.rb4
-rw-r--r--app/lib/activitypub/activity/create.rb16
-rw-r--r--app/views/admin/reports/_status.html.haml2
-rwxr-xr-xapp/views/layouts/application.html.haml6
-rw-r--r--app/views/stream_entries/_detailed_status.html.haml2
-rw-r--r--app/views/stream_entries/_simple_status.html.haml2
-rw-r--r--app/workers/maintenance/destroy_media_worker.rb2
-rw-r--r--app/workers/maintenance/redownload_account_media_worker.rb2
-rw-r--r--app/workers/maintenance/uncache_media_worker.rb2
-rw-r--r--app/workers/scheduler/backup_cleanup_scheduler.rb2
-rw-r--r--app/workers/scheduler/doorkeeper_cleanup_scheduler.rb2
-rw-r--r--app/workers/scheduler/email_scheduler.rb2
-rw-r--r--app/workers/scheduler/feed_cleanup_scheduler.rb2
-rw-r--r--app/workers/scheduler/ip_cleanup_scheduler.rb2
-rw-r--r--app/workers/scheduler/media_cleanup_scheduler.rb2
-rw-r--r--app/workers/scheduler/subscriptions_cleanup_scheduler.rb2
-rw-r--r--app/workers/scheduler/subscriptions_scheduler.rb2
-rw-r--r--app/workers/scheduler/user_cleanup_scheduler.rb2
-rwxr-xr-xbin/tootctl4
-rw-r--r--config/initializers/paperclip.rb4
-rw-r--r--config/locales/cs.yml3
-rw-r--r--config/locales/doorkeeper.da.yml2
-rw-r--r--config/locales/en.yml3
-rw-r--r--config/locales/ja.yml21
-rw-r--r--config/locales/ka.yml6
-rw-r--r--config/locales/ko.yml2
-rw-r--r--config/locales/simple_form.fr.yml2
-rw-r--r--config/webpack/production.js4
-rw-r--r--lib/cli.rb11
-rw-r--r--lib/mastodon/media_cli.rb47
-rw-r--r--lib/tasks/mastodon.rake13
-rw-r--r--spec/helpers/application_helper_spec.rb10
-rw-r--r--streaming/index.js23
97 files changed, 326 insertions, 94 deletions
diff --git a/.env.nanobox b/.env.nanobox
index 8e0af6a8a..b60b6ee68 100644
--- a/.env.nanobox
+++ b/.env.nanobox
@@ -136,8 +136,8 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io
 # Defaults to 60 seconds. Set to 0 to disable
 # SWIFT_CACHE_TTL=
 
-# Optional alias for S3 if you want to use Cloudfront or Cloudflare in front
-# S3_CLOUDFRONT_HOST=
+# Optional alias for S3 (e.g. to serve files on a custom domain, possibly using Cloudfront or Cloudflare)
+# S3_ALIAS_HOST=
 
 # Streaming API integration
 # STREAMING_API_BASE_URL=
diff --git a/.env.production.sample b/.env.production.sample
index 235b19207..efb5661fd 100644
--- a/.env.production.sample
+++ b/.env.production.sample
@@ -134,8 +134,8 @@ SMTP_FROM_ADDRESS=notifications@example.com
 # Defaults to 60 seconds. Set to 0 to disable
 # SWIFT_CACHE_TTL=
 
-# Optional alias for S3 if you want to use Cloudfront or Cloudflare in front
-# S3_CLOUDFRONT_HOST=
+# Optional alias for S3 (e.g. to serve files on a custom domain, possibly using Cloudfront or Cloudflare)
+# S3_ALIAS_HOST=
 
 # Streaming API integration
 # STREAMING_API_BASE_URL=
diff --git a/Gemfile b/Gemfile
index 760ecbc7c..e781da8ae 100644
--- a/Gemfile
+++ b/Gemfile
@@ -41,7 +41,7 @@ gem 'omniauth-cas', '~> 1.1'
 gem 'omniauth-saml', '~> 1.10'
 gem 'omniauth', '~> 1.2'
 
-gem 'doorkeeper', '~> 4.4'
+gem 'doorkeeper', '~> 5.0'
 gem 'fast_blank', '~> 1.0'
 gem 'fastimage'
 gem 'goldfinger', '~> 2.1'
diff --git a/Gemfile.lock b/Gemfile.lock
index 4c03ffc5e..6d48bf83f 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -174,14 +174,14 @@ GEM
       devise (~> 4.0)
       railties (< 5.3)
       rotp (~> 2.0)
-    devise_pam_authenticatable2 (9.1.0)
+    devise_pam_authenticatable2 (9.1.1)
       devise (>= 4.0.0)
       rpam2 (~> 4.0)
     diff-lcs (1.3)
     docile (1.3.0)
     domain_name (0.5.20180417)
       unf (>= 0.0.5, < 1.0.0)
-    doorkeeper (4.4.2)
+    doorkeeper (5.0.0)
       railties (>= 4.2)
     dotenv (2.2.2)
     dotenv-rails (2.2.2)
@@ -672,7 +672,7 @@ DEPENDENCIES
   devise (~> 4.4)
   devise-two-factor (~> 3.0)
   devise_pam_authenticatable2 (~> 9.1)
-  doorkeeper (~> 4.4)
+  doorkeeper (~> 5.0)
   dotenv-rails (~> 2.2, < 2.3)
   fabrication (~> 2.20)
   faker (~> 1.8)
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 327901e4e..cf6f8aa70 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -27,11 +27,6 @@ module ApplicationHelper
     Setting.open_deletion
   end
 
-  def add_rtl_body_class(other_classes)
-    other_classes = "#{other_classes} rtl" if locale_direction == 'rtl'
-    other_classes
-  end
-
   def locale_direction
     if [:ar, :fa, :he].include?(I18n.locale)
       'rtl'
@@ -77,4 +72,14 @@ module ApplicationHelper
   def react_component(name, props = {})
     content_tag(:div, nil, data: { component: name.to_s.camelcase, props: Oj.dump(props) })
   end
+
+  def body_classes
+    output = (@body_classes || '').split(' ')
+    output << "flavour-#{current_flavour.parameterize}"
+    output << "skin-#{current_skin.parameterize}"
+    output << 'system-font' if current_account&.user&.setting_system_font_ui
+    output << current_account&.user&.setting_reduce_motion ? 'reduce-motion' : 'no-reduce-motion'
+    output << 'rtl' if locale_direction == 'rtl'
+    output.reject(&:blank?).join(' ')
+  end
 end
diff --git a/app/javascript/core/admin.js b/app/javascript/core/admin.js
index 28f27fbc6..0c26dc18d 100644
--- a/app/javascript/core/admin.js
+++ b/app/javascript/core/admin.js
@@ -41,3 +41,10 @@ delegate(document, '.media-spoiler-hide-button', 'click', () => {
     element.click();
   });
 });
+
+delegate(document, '#domain_block_severity', 'change', ({ target }) => {
+  const rejectMediaDiv = document.querySelector('.input.with_label.domain_block_reject_media');
+  if (rejectMediaDiv) {
+    rejectMediaDiv.style.display = (target.value === 'suspend') ? 'none' : 'block';
+  }
+});
diff --git a/app/javascript/mastodon/components/extended_video_player.js b/app/javascript/mastodon/components/extended_video_player.js
index 9e2f6835a..009c0d559 100644
--- a/app/javascript/mastodon/components/extended_video_player.js
+++ b/app/javascript/mastodon/components/extended_video_player.js
@@ -50,6 +50,7 @@ export default class ExtendedVideoPlayer extends React.PureComponent {
           role='button'
           tabIndex='0'
           aria-label={alt}
+          title={alt}
           muted={muted}
           controls={controls}
           loop={!controls}
diff --git a/app/javascript/mastodon/components/media_gallery.js b/app/javascript/mastodon/components/media_gallery.js
index 63bc4a59b..6e1310cd6 100644
--- a/app/javascript/mastodon/components/media_gallery.js
+++ b/app/javascript/mastodon/components/media_gallery.js
@@ -154,6 +154,7 @@ class Item extends React.PureComponent {
           <video
             className='media-gallery__item-gifv-thumbnail'
             aria-label={attachment.get('description')}
+            title={attachment.get('description')}
             role='application'
             src={attachment.get('url')}
             onClick={this.handleClick}
diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js
index 9a3fd3576..9809a9a32 100644
--- a/app/javascript/mastodon/components/status.js
+++ b/app/javascript/mastodon/components/status.js
@@ -230,6 +230,7 @@ export default class Status extends ImmutablePureComponent {
               <Component
                 preview={video.get('preview_url')}
                 src={video.get('url')}
+                alt={video.get('description')}
                 width={239}
                 height={110}
                 inline
diff --git a/app/javascript/mastodon/containers/status_container.js b/app/javascript/mastodon/containers/status_container.js
index ed375c3e5..bbc0d5e96 100644
--- a/app/javascript/mastodon/containers/status_container.js
+++ b/app/javascript/mastodon/containers/status_container.js
@@ -34,7 +34,7 @@ const messages = defineMessages({
   deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
   deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
   redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
-  redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.' },
+  redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' },
   blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' },
 });
 
diff --git a/app/javascript/mastodon/features/account/components/header.js b/app/javascript/mastodon/features/account/components/header.js
index dd2cd406b..eb9236aeb 100644
--- a/app/javascript/mastodon/features/account/components/header.js
+++ b/app/javascript/mastodon/features/account/components/header.js
@@ -104,7 +104,9 @@ export default class Header extends ImmutablePureComponent {
     }
 
     if (me !== account.get('id')) {
-      if (account.getIn(['relationship', 'requested'])) {
+      if (!account.get('relationship')) { // Wait until the relationship is loaded
+        actionBtn = '';
+      } else if (account.getIn(['relationship', 'requested'])) {
         actionBtn = (
           <div className='account--action-button'>
             <IconButton size={26} active icon='hourglass' title={intl.formatMessage(messages.requested)} onClick={this.props.onFollow} />
diff --git a/app/javascript/mastodon/features/compose/components/upload_button.js b/app/javascript/mastodon/features/compose/components/upload_button.js
index 70b28a2ba..2f38f5148 100644
--- a/app/javascript/mastodon/features/compose/components/upload_button.js
+++ b/app/javascript/mastodon/features/compose/components/upload_button.js
@@ -7,7 +7,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 
 const messages = defineMessages({
-  upload: { id: 'upload_button.label', defaultMessage: 'Add media' },
+  upload: { id: 'upload_button.label', defaultMessage: 'Add media (JPEG, PNG, GIF, WebM, MP4)' },
 });
 
 const makeMapStateToProps = () => {
diff --git a/app/javascript/mastodon/features/report/components/status_check_box.js b/app/javascript/mastodon/features/report/components/status_check_box.js
index 9ff75a082..2552d94d8 100644
--- a/app/javascript/mastodon/features/report/components/status_check_box.js
+++ b/app/javascript/mastodon/features/report/components/status_check_box.js
@@ -36,6 +36,7 @@ export default class StatusCheckBox extends React.PureComponent {
               <Component
                 preview={video.get('preview_url')}
                 src={video.get('url')}
+                alt={video.get('description')}
                 width={239}
                 height={110}
                 inline
diff --git a/app/javascript/mastodon/features/status/components/detailed_status.js b/app/javascript/mastodon/features/status/components/detailed_status.js
index 12ffb7579..b4bbda161 100644
--- a/app/javascript/mastodon/features/status/components/detailed_status.js
+++ b/app/javascript/mastodon/features/status/components/detailed_status.js
@@ -60,6 +60,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
           <Video
             preview={video.get('preview_url')}
             src={video.get('url')}
+            alt={video.get('description')}
             width={300}
             height={150}
             inline
diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js
index 45e36e3eb..48931b2d6 100644
--- a/app/javascript/mastodon/features/status/index.js
+++ b/app/javascript/mastodon/features/status/index.js
@@ -49,7 +49,7 @@ const messages = defineMessages({
   deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
   deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
   redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
-  redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.' },
+  redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' },
   blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' },
   revealAll: { id: 'status.show_more_all', defaultMessage: 'Show more for all' },
   hideAll: { id: 'status.show_less_all', defaultMessage: 'Show less for all' },
diff --git a/app/javascript/mastodon/features/video/index.js b/app/javascript/mastodon/features/video/index.js
index 55ea32acb..52b395f88 100644
--- a/app/javascript/mastodon/features/video/index.js
+++ b/app/javascript/mastodon/features/video/index.js
@@ -315,6 +315,7 @@ export default class Video extends React.PureComponent {
           role='button'
           tabIndex='0'
           aria-label={alt}
+          title={alt}
           width={width}
           height={height}
           onClick={this.togglePlay}
diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json
index b38dbcf73..bf0701094 100644
--- a/app/javascript/mastodon/locales/ar.json
+++ b/app/javascript/mastodon/locales/ar.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "الحسابات المحجوبة",
   "navigation_bar.community_timeline": "الخيط العام المحلي",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "الرسائل المباشِرة",
   "navigation_bar.discover": "إكتشف",
   "navigation_bar.domain_blocks": "النطاقات المخفية",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "إلغاء الترقية",
   "status.cannot_reblog": "تعذرت ترقية هذا المنشور",
   "status.delete": "إحذف",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "رسالة خاصة إلى @{name}",
   "status.embed": "إدماج",
   "status.favourite": "أضف إلى المفضلة",
diff --git a/app/javascript/mastodon/locales/ast.json b/app/javascript/mastodon/locales/ast.json
index 96e3a14d9..a6d951626 100644
--- a/app/javascript/mastodon/locales/ast.json
+++ b/app/javascript/mastodon/locales/ast.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blocked users",
   "navigation_bar.community_timeline": "Local timeline",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Delete",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Favourite",
diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json
index b41045fb8..ae8222db5 100644
--- a/app/javascript/mastodon/locales/bg.json
+++ b/app/javascript/mastodon/locales/bg.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blocked users",
   "navigation_bar.community_timeline": "Local timeline",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Изтриване",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Предпочитани",
diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json
index 7378b703b..216e5d0f2 100644
--- a/app/javascript/mastodon/locales/ca.json
+++ b/app/javascript/mastodon/locales/ca.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Usuaris bloquejats",
   "navigation_bar.community_timeline": "Línia de temps Local",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Missatges directes",
   "navigation_bar.discover": "Descobreix",
   "navigation_bar.domain_blocks": "Dominis ocults",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Desfer l'impuls",
   "status.cannot_reblog": "Aquesta publicació no pot ser retootejada",
   "status.delete": "Esborrar",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Missatge directe @{name}",
   "status.embed": "Incrustar",
   "status.favourite": "Favorit",
diff --git a/app/javascript/mastodon/locales/co.json b/app/javascript/mastodon/locales/co.json
index 717397d41..91cdb541c 100644
--- a/app/javascript/mastodon/locales/co.json
+++ b/app/javascript/mastodon/locales/co.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Utilizatori bluccati",
   "navigation_bar.community_timeline": "Linea pubblica lucale",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Missaghji diretti",
   "navigation_bar.discover": "Scopre",
   "navigation_bar.domain_blocks": "Duminii piattati",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Ùn sparte più",
   "status.cannot_reblog": "Stu statutu ùn pò micca esse spartutu",
   "status.delete": "Toglie",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Mandà un missaghju @{name}",
   "status.embed": "Integrà",
   "status.favourite": "Aghjunghje à i favuriti",
diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json
index b71179ca0..2b19628a5 100644
--- a/app/javascript/mastodon/locales/cs.json
+++ b/app/javascript/mastodon/locales/cs.json
@@ -162,9 +162,10 @@
   "missing_indicator.label": "Nenalezeno",
   "missing_indicator.sublabel": "Tento zdroj se nepodařilo najít",
   "mute_modal.hide_notifications": "Skrýt oznámení před tímto uživatelem?",
-  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.apps": "Mobilní aplikace",
   "navigation_bar.blocks": "Blokovaní uživatelé",
   "navigation_bar.community_timeline": "Místní časová osa",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Přímé zprávy",
   "navigation_bar.discover": "Objevujte",
   "navigation_bar.domain_blocks": "Skryté domény",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Zrušit boost",
   "status.cannot_reblog": "Tento příspěvek nemůže být boostnutý",
   "status.delete": "Delete",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Poslat přímou zprávu uživateli @{name}",
   "status.embed": "Vložit",
   "status.favourite": "Oblíbit",
diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json
index 4c38b8eb2..92a09602c 100644
--- a/app/javascript/mastodon/locales/da.json
+++ b/app/javascript/mastodon/locales/da.json
@@ -20,7 +20,7 @@
   "account.mute_notifications": "Dæmp notifikationer fra @{name}",
   "account.muted": "Dæmpet",
   "account.posts": "Trut",
-  "account.posts_with_replies": "Trut samt svar",
+  "account.posts_with_replies": "Trut og svar",
   "account.report": "Rapporter @{name}",
   "account.requested": "Afventer godkendelse. Tryk for at annullere følgeanmodning",
   "account.share": "Del @{name}s profil",
@@ -51,7 +51,7 @@
   "column.lists": "Lister",
   "column.mutes": "Dæmpede brugere",
   "column.notifications": "Notifikationer",
-  "column.pins": "Fastgjorte toots",
+  "column.pins": "Fastgjorte trut",
   "column.public": "Fælles tidslinje",
   "column_back_button.label": "Tilbage",
   "column_header.hide_settings": "Skjul indstillinger",
@@ -61,7 +61,7 @@
   "column_header.show_settings": "Vis indstillinger",
   "column_header.unpin": "Fastgør ikke længere",
   "column_subheading.settings": "Indstillinger",
-  "community.column_settings.media_only": "Kun multimedier",
+  "community.column_settings.media_only": "Kun medie",
   "compose_form.direct_message_warning": "Dette trut vil kun blive sendt til de nævnte brugere.",
   "compose_form.direct_message_warning_learn_more": "Lær mere",
   "compose_form.hashtag_warning": "Dette trut vil ikke blive vist under noget hashtag da det ikke er listet. Kun offentlige trut kan blive vist under søgninger med hashtags.",
@@ -70,8 +70,8 @@
   "compose_form.placeholder": "Hvad har du på hjertet?",
   "compose_form.publish": "Trut",
   "compose_form.publish_loud": "{publish}!",
-  "compose_form.sensitive.marked": "Multimedie er markeret som værende følsomt",
-  "compose_form.sensitive.unmarked": "Multimediet er ikke markeret som værende følsomt",
+  "compose_form.sensitive.marked": "Medie er markeret som værende følsomt",
+  "compose_form.sensitive.unmarked": "Mediet er ikke markeret som værende følsomt",
   "compose_form.spoiler.marked": "Teksten er skjult bag en advarsel",
   "compose_form.spoiler.unmarked": "Teksten er ikke skjult",
   "compose_form.spoiler_placeholder": "Skriv din advarsel her",
@@ -162,9 +162,10 @@
   "missing_indicator.label": "Ikke fundet",
   "missing_indicator.sublabel": "Denne ressource kunne ikke blive fundet",
   "mute_modal.hide_notifications": "Skjul notifikationer fra denne bruger?",
-  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.apps": "Mobil apps",
   "navigation_bar.blocks": "Blokerede brugere",
   "navigation_bar.community_timeline": "Lokal tidslinje",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direkte beskeder",
   "navigation_bar.discover": "Opdag",
   "navigation_bar.domain_blocks": "Skjulte domæner",
@@ -258,12 +259,13 @@
   "status.cancel_reblog_private": "Fremhæv ikke længere",
   "status.cannot_reblog": "Denne post kan ikke fremhæves",
   "status.delete": "Slet",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Send direkte besked til @{name}",
   "status.embed": "Indlejre",
   "status.favourite": "Favorit",
   "status.filtered": "Filtreret",
   "status.load_more": "Indlæs mere",
-  "status.media_hidden": "Multimedia skjult",
+  "status.media_hidden": "Medie skjult",
   "status.mention": "Nævn @{name}",
   "status.more": "Mere",
   "status.mute": "Dæmp @{name}",
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index 3427b322b..e6139b218 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blockierte Profile",
   "navigation_bar.community_timeline": "Lokale Zeitleiste",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direktnachrichten",
   "navigation_bar.discover": "Entdecken",
   "navigation_bar.domain_blocks": "Versteckte Domains",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Nicht mehr teilen",
   "status.cannot_reblog": "Dieser Beitrag kann nicht geteilt werden",
   "status.delete": "Löschen",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direktnachricht @{name}",
   "status.embed": "Einbetten",
   "status.favourite": "Favorisieren",
diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json
index 2d836be90..98974bf47 100644
--- a/app/javascript/mastodon/locales/defaultMessages.json
+++ b/app/javascript/mastodon/locales/defaultMessages.json
@@ -390,7 +390,7 @@
         "id": "confirmations.redraft.confirm"
       },
       {
-        "defaultMessage": "Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.",
+        "defaultMessage": "Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.",
         "id": "confirmations.redraft.message"
       },
       {
@@ -1019,6 +1019,10 @@
       {
         "defaultMessage": "Logout",
         "id": "navigation_bar.logout"
+      },
+      {
+        "defaultMessage": "Compose new toot",
+        "id": "navigation_bar.compose"
       }
     ],
     "path": "app/javascript/mastodon/features/compose/index.json"
@@ -1636,7 +1640,7 @@
         "id": "confirmations.redraft.confirm"
       },
       {
-        "defaultMessage": "Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.",
+        "defaultMessage": "Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.",
         "id": "confirmations.redraft.message"
       },
       {
@@ -1652,6 +1656,10 @@
         "id": "status.show_less_all"
       },
       {
+        "defaultMessage": "Detailed conversation view",
+        "id": "status.detailed_status"
+      },
+      {
         "defaultMessage": "Are you sure you want to block {name}?",
         "id": "confirmations.block.message"
       }
diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json
index 76a003f65..fad0aa9bc 100644
--- a/app/javascript/mastodon/locales/el.json
+++ b/app/javascript/mastodon/locales/el.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Αποκλεισμένοι χρήστες",
   "navigation_bar.community_timeline": "Τοπική ροή",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Προσωπικά μηνύματα",
   "navigation_bar.discover": "Ανακάλυψη",
   "navigation_bar.domain_blocks": "Κρυμμένοι τομείς",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Ακύρωσε την προώθηση",
   "status.cannot_reblog": "Αυτή η δημοσίευση δεν μπορεί να προωθηθεί",
   "status.delete": "Διαγραφή",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Προσωπικό μήνυμα προς @{name}",
   "status.embed": "Ενσωμάτωσε",
   "status.favourite": "Σημείωσε ως αγαπημένο",
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index 27b2cb45e..52d501780 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -91,7 +91,7 @@
   "confirmations.mute.confirm": "Mute",
   "confirmations.mute.message": "Are you sure you want to mute {name}?",
   "confirmations.redraft.confirm": "Delete & redraft",
-  "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.",
+  "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.",
   "confirmations.unfollow.confirm": "Unfollow",
   "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
   "embed.instructions": "Embed this status on your website by copying the code below.",
@@ -169,6 +169,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blocked users",
   "navigation_bar.community_timeline": "Local timeline",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
@@ -263,6 +264,7 @@
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Delete",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Favourite",
@@ -300,7 +302,7 @@
   "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Drag & drop to upload",
-  "upload_button.label": "Add media",
+  "upload_button.label": "Add media (JPEG, PNG, GIF, WebM, MP4)",
   "upload_form.description": "Describe for the visually impaired",
   "upload_form.focus": "Crop",
   "upload_form.undo": "Delete",
diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
index f4c316441..cfeef55bb 100644
--- a/app/javascript/mastodon/locales/eo.json
+++ b/app/javascript/mastodon/locales/eo.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blokitaj uzantoj",
   "navigation_bar.community_timeline": "Loka tempolinio",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Rektaj mesaĝoj",
   "navigation_bar.discover": "Esplori",
   "navigation_bar.domain_blocks": "Kaŝitaj domajnoj",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Eksdiskonigi",
   "status.cannot_reblog": "Ĉi tiu mesaĝo ne diskonigeblas",
   "status.delete": "Forigi",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Rekte mesaĝi @{name}",
   "status.embed": "Enkorpigi",
   "status.favourite": "Stelumi",
diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json
index b17e1411e..7e835cd65 100644
--- a/app/javascript/mastodon/locales/es.json
+++ b/app/javascript/mastodon/locales/es.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Usuarios bloqueados",
   "navigation_bar.community_timeline": "Historia local",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Este toot no puede retootearse",
   "status.delete": "Borrar",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
   "status.embed": "Incrustado",
   "status.favourite": "Favorito",
diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json
index ec27fe425..c5cf627e8 100644
--- a/app/javascript/mastodon/locales/eu.json
+++ b/app/javascript/mastodon/locales/eu.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blokeatutako erabiltzaileak",
   "navigation_bar.community_timeline": "Denbora-lerro lokala",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Mezu zuzenak",
   "navigation_bar.discover": "Aurkitu",
   "navigation_bar.domain_blocks": "Ezkutatutako domeinuak",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Kendu bultzada",
   "status.cannot_reblog": "Mezu honi ezin zaio bultzada eman",
   "status.delete": "Ezabatu",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Mezu zuzena @{name}(r)i",
   "status.embed": "Txertatu",
   "status.favourite": "Gogokoa",
diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json
index 03c6bb7ce..55615baa3 100644
--- a/app/javascript/mastodon/locales/fa.json
+++ b/app/javascript/mastodon/locales/fa.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "کاربران مسدودشده",
   "navigation_bar.community_timeline": "نوشته‌های محلی",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "پیغام‌های خصوصی",
   "navigation_bar.discover": "گشت و گذار",
   "navigation_bar.domain_blocks": "دامین‌های پنهان‌شده",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "حذف بازبوق",
   "status.cannot_reblog": "این نوشته را نمی‌شود بازبوقید",
   "status.delete": "پاک‌کردن",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "پیغام مستقیم به @{name}",
   "status.embed": "جاگذاری",
   "status.favourite": "پسندیدن",
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
index 0861c42fc..fcf08a9b9 100644
--- a/app/javascript/mastodon/locales/fi.json
+++ b/app/javascript/mastodon/locales/fi.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Estetyt käyttäjät",
   "navigation_bar.community_timeline": "Paikallinen aikajana",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Viestit",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Piilotetut verkkotunnukset",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Peru buustaus",
   "status.cannot_reblog": "Tätä julkaisua ei voi buustata",
   "status.delete": "Poista",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Viesti käyttäjälle @{name}",
   "status.embed": "Upota",
   "status.favourite": "Tykkää",
diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json
index 835b1af65..79d318d1b 100644
--- a/app/javascript/mastodon/locales/fr.json
+++ b/app/javascript/mastodon/locales/fr.json
@@ -87,7 +87,7 @@
   "confirmations.mute.confirm": "Masquer",
   "confirmations.mute.message": "Confirmez-vous le masquage de {name} ?",
   "confirmations.redraft.confirm": "Effacer et ré-écrire",
-  "confirmations.redraft.message": "Êtes-vous sûr·e de vouloir effacer ce statut pour le ré-écrire ? Vous perdrez toutes ses réponses, ses repartages et ses mises en favori.",
+  "confirmations.redraft.message": "Êtes-vous sûr·e de vouloir effacer ce statut pour le ré-écrire ? Ses partages ainsi que ses mises en favori seront perdu·e·s et ses réponses seront orphelines.",
   "confirmations.unfollow.confirm": "Ne plus suivre",
   "confirmations.unfollow.message": "Voulez-vous arrêter de suivre {name} ?",
   "embed.instructions": "Intégrez ce statut à votre site en copiant le code ci-dessous.",
@@ -162,9 +162,10 @@
   "missing_indicator.label": "Non trouvé",
   "missing_indicator.sublabel": "Ressource introuvable",
   "mute_modal.hide_notifications": "Masquer les notifications de cette personne ?",
-  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.apps": "Applications mobiles",
   "navigation_bar.blocks": "Comptes bloqués",
   "navigation_bar.community_timeline": "Fil public local",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Messages directs",
   "navigation_bar.discover": "Découvrir",
   "navigation_bar.domain_blocks": "Domaines cachés",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Dé-booster",
   "status.cannot_reblog": "Cette publication ne peut être boostée",
   "status.delete": "Effacer",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Envoyer un message direct à @{name}",
   "status.embed": "Intégrer",
   "status.favourite": "Ajouter aux favoris",
diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json
index d83b11d74..e8b598263 100644
--- a/app/javascript/mastodon/locales/gl.json
+++ b/app/javascript/mastodon/locales/gl.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Usuarias bloqueadas",
   "navigation_bar.community_timeline": "Liña temporal local",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Mensaxes directas",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Dominios agochados",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Non promover",
   "status.cannot_reblog": "Esta mensaxe non pode ser promovida",
   "status.delete": "Eliminar",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Mensaxe directa @{name}",
   "status.embed": "Incrustar",
   "status.favourite": "Favorita",
diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json
index d20adce11..5f5e396b4 100644
--- a/app/javascript/mastodon/locales/he.json
+++ b/app/javascript/mastodon/locales/he.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "חסימות",
   "navigation_bar.community_timeline": "ציר זמן מקומי",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "לא ניתן להדהד הודעה זו",
   "status.delete": "מחיקה",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
   "status.embed": "הטמעה",
   "status.favourite": "חיבוב",
diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json
index e6ce3359d..d0bea84d6 100644
--- a/app/javascript/mastodon/locales/hr.json
+++ b/app/javascript/mastodon/locales/hr.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blokirani korisnici",
   "navigation_bar.community_timeline": "Lokalni timeline",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Ovaj post ne može biti boostan",
   "status.delete": "Obriši",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Označi omiljenim",
diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json
index 80c3a1de8..4dae46bb1 100644
--- a/app/javascript/mastodon/locales/hu.json
+++ b/app/javascript/mastodon/locales/hu.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Tiltott felhasználók",
   "navigation_bar.community_timeline": "Helyi idővonal",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Ezen státusz nem rebloggolható",
   "status.delete": "Törlés",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
   "status.embed": "Beágyaz",
   "status.favourite": "Kedvenc",
diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json
index e5ad93fd8..e9f60b727 100644
--- a/app/javascript/mastodon/locales/hy.json
+++ b/app/javascript/mastodon/locales/hy.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Արգելափակված օգտատերեր",
   "navigation_bar.community_timeline": "Տեղական հոսք",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Այս թութը չի կարող տարածվել",
   "status.delete": "Ջնջել",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
   "status.embed": "Ներդնել",
   "status.favourite": "Հավանել",
diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json
index 010d35b73..1dd30f2f7 100644
--- a/app/javascript/mastodon/locales/id.json
+++ b/app/javascript/mastodon/locales/id.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Pengguna diblokir",
   "navigation_bar.community_timeline": "Linimasa lokal",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Hapus",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Difavoritkan",
diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json
index 06e6b02d7..385313df3 100644
--- a/app/javascript/mastodon/locales/io.json
+++ b/app/javascript/mastodon/locales/io.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blokusita uzeri",
   "navigation_bar.community_timeline": "Lokala tempolineo",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Efacar",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Favorizar",
diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json
index c9ac57c74..0f460b3f6 100644
--- a/app/javascript/mastodon/locales/it.json
+++ b/app/javascript/mastodon/locales/it.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Utenti bloccati",
   "navigation_bar.community_timeline": "Timeline locale",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Messaggi diretti",
   "navigation_bar.discover": "Scopri",
   "navigation_bar.domain_blocks": "Domini nascosti",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Annulla condivisione",
   "status.cannot_reblog": "Questo post non può essere condiviso",
   "status.delete": "Elimina",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Messaggio diretto @{name}",
   "status.embed": "Incorpora",
   "status.favourite": "Apprezzato",
diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json
index d8ec6e934..dd755f1ca 100644
--- a/app/javascript/mastodon/locales/ja.json
+++ b/app/javascript/mastodon/locales/ja.json
@@ -166,9 +166,10 @@
   "missing_indicator.label": "見つかりません",
   "missing_indicator.sublabel": "見つかりませんでした",
   "mute_modal.hide_notifications": "このユーザーからの通知を隠しますか?",
-  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.apps": "アプリ",
   "navigation_bar.blocks": "ブロックしたユーザー",
   "navigation_bar.community_timeline": "ローカルタイムライン",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "ダイレクトメッセージ",
   "navigation_bar.discover": "見つける",
   "navigation_bar.domain_blocks": "非表示にしたドメイン",
@@ -263,6 +264,7 @@
   "status.cancel_reblog_private": "ブースト解除",
   "status.cannot_reblog": "この投稿はブーストできません",
   "status.delete": "削除",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "@{name}さんにダイレクトメッセージ",
   "status.embed": "埋め込み",
   "status.favourite": "お気に入り",
diff --git a/app/javascript/mastodon/locales/ka.json b/app/javascript/mastodon/locales/ka.json
index d2a915c60..1ded84143 100644
--- a/app/javascript/mastodon/locales/ka.json
+++ b/app/javascript/mastodon/locales/ka.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "დაბლოკილი მომხმარებლები",
   "navigation_bar.community_timeline": "ლოკალური თაიმლაინი",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "პირდაპირი წერილები",
   "navigation_bar.discover": "აღმოაჩინე",
   "navigation_bar.domain_blocks": "დამალული დომენები",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "ბუსტის მოშორება",
   "status.cannot_reblog": "ეს პოსტი ვერ დაიბუსტება",
   "status.delete": "წაშლა",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "პირდაპირი წერილი @{name}-ს",
   "status.embed": "ჩართვა",
   "status.favourite": "ფავორიტი",
diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json
index 18150d17c..dd9079878 100644
--- a/app/javascript/mastodon/locales/ko.json
+++ b/app/javascript/mastodon/locales/ko.json
@@ -7,7 +7,7 @@
   "account.disclaimer_full": "여기 있는 정보는 유저의 프로파일을 정확히 반영하지 못 할 수도 있습니다.",
   "account.domain_blocked": "도메인 숨겨짐",
   "account.edit_profile": "프로필 편집",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "프로필에 나타내기",
   "account.follow": "팔로우",
   "account.followers": "팔로워",
   "account.follows": "팔로우",
@@ -27,7 +27,7 @@
   "account.show_reblogs": "@{name}의 부스트 보기",
   "account.unblock": "차단 해제",
   "account.unblock_domain": "{domain} 숨김 해제",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "프로필에 나타내지 않기",
   "account.unfollow": "팔로우 해제",
   "account.unmute": "뮤트 해제",
   "account.unmute_notifications": "@{name}의 알림 뮤트 해제",
@@ -162,9 +162,10 @@
   "missing_indicator.label": "찾을 수 없습니다",
   "missing_indicator.sublabel": "이 리소스를 찾을 수 없었습니다",
   "mute_modal.hide_notifications": "이 사용자로부터의 알림을 뮤트하시겠습니까?",
-  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.apps": "모바일 앱",
   "navigation_bar.blocks": "차단한 사용자",
   "navigation_bar.community_timeline": "로컬 타임라인",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "다이렉트 메시지",
   "navigation_bar.discover": "발견하기",
   "navigation_bar.domain_blocks": "숨겨진 도메인",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "부스트 취소",
   "status.cannot_reblog": "이 포스트는 부스트 할 수 없습니다",
   "status.delete": "삭제",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "@{name}에게 다이렉트 메시지",
   "status.embed": "공유하기",
   "status.favourite": "즐겨찾기",
diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json
index 72ffbdc6e..7259ba49a 100644
--- a/app/javascript/mastodon/locales/nl.json
+++ b/app/javascript/mastodon/locales/nl.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Geblokkeerde gebruikers",
   "navigation_bar.community_timeline": "Lokale tijdlijn",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Directe berichten",
   "navigation_bar.discover": "Ontdekken",
   "navigation_bar.domain_blocks": "Verborgen domeinen",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Niet langer boosten",
   "status.cannot_reblog": "Deze toot kan niet geboost worden",
   "status.delete": "Verwijderen",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Directe toot @{name}",
   "status.embed": "Embed",
   "status.favourite": "Favoriet",
diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json
index 3e9612edb..520c9ec25 100644
--- a/app/javascript/mastodon/locales/no.json
+++ b/app/javascript/mastodon/locales/no.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blokkerte brukere",
   "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",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Denne posten kan ikke fremheves",
   "status.delete": "Slett",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
   "status.embed": "Bygge inn",
   "status.favourite": "Lik",
diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json
index 6aaf4cc63..a198a0d93 100644
--- a/app/javascript/mastodon/locales/oc.json
+++ b/app/javascript/mastodon/locales/oc.json
@@ -7,7 +7,7 @@
   "account.disclaimer_full": "Aquelas informacions de perfil pòdon èsser incomplètas.",
   "account.domain_blocked": "Domeni amagat",
   "account.edit_profile": "Modificar lo perfil",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "Mostrar pel perfil",
   "account.follow": "Sègre",
   "account.followers": "Seguidors",
   "account.follows": "Abonaments",
@@ -27,7 +27,7 @@
   "account.show_reblogs": "Mostrar los partatges de @{name}",
   "account.unblock": "Desblocar @{name}",
   "account.unblock_domain": "Desblocar {domain}",
-  "account.unendorse": "Don't feature on profile",
+  "account.unendorse": "Mostrar pas pel perfil",
   "account.unfollow": "Quitar de sègre",
   "account.unmute": "Quitar de rescondre @{name}",
   "account.unmute_notifications": "Mostrar las notificacions de @{name}",
@@ -139,7 +139,7 @@
   "keyboard_shortcuts.hotkey": "Acorchis",
   "keyboard_shortcuts.legend": "mostrar aquesta legenda",
   "keyboard_shortcuts.mention": "mencionar l’autor",
-  "keyboard_shortcuts.profile": "to open author's profile",
+  "keyboard_shortcuts.profile": "per dobrir lo perfil de l’autor",
   "keyboard_shortcuts.reply": "respondre",
   "keyboard_shortcuts.search": "anar a la recèrca",
   "keyboard_shortcuts.toggle_hidden": "mostrar/amagar lo tèxte dels avertiments",
@@ -162,9 +162,10 @@
   "missing_indicator.label": "Pas trobat",
   "missing_indicator.sublabel": "Aquesta ressorsa es pas estada trobada",
   "mute_modal.hide_notifications": "Rescondre las notificacions d’aquesta persona ?",
-  "navigation_bar.apps": "Mobile apps",
+  "navigation_bar.apps": "Aplicacions mobil",
   "navigation_bar.blocks": "Personas blocadas",
   "navigation_bar.community_timeline": "Flux public local",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Messatges dirèctes",
   "navigation_bar.discover": "Trobar",
   "navigation_bar.domain_blocks": "Domenis resconduts",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Quitar de partejar",
   "status.cannot_reblog": "Aqueste estatut pòt pas èsser partejat",
   "status.delete": "Escafar",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Messatge per @{name}",
   "status.embed": "Embarcar",
   "status.favourite": "Apondre als favorits",
diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json
index 595e5d57b..b8eceb1cf 100644
--- a/app/javascript/mastodon/locales/pl.json
+++ b/app/javascript/mastodon/locales/pl.json
@@ -169,6 +169,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Zablokowani użytkownicy",
   "navigation_bar.community_timeline": "Lokalna oś czasu",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Wiadomości bezpośrednie",
   "navigation_bar.discover": "Odkrywaj",
   "navigation_bar.domain_blocks": "Ukryte domeny",
@@ -263,6 +264,7 @@
   "status.cancel_reblog_private": "Cofnij podbicie",
   "status.cannot_reblog": "Ten wpis nie może zostać podbity",
   "status.delete": "Usuń",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Wyślij wiadomość bezpośrednią do @{name}",
   "status.embed": "Osadź",
   "status.favourite": "Dodaj do ulubionych",
diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json
index bdfdd46e5..d40e6d6a2 100644
--- a/app/javascript/mastodon/locales/pt-BR.json
+++ b/app/javascript/mastodon/locales/pt-BR.json
@@ -7,7 +7,7 @@
   "account.disclaimer_full": "As informações abaixo podem refletir o perfil do usuário de maneira incompleta.",
   "account.domain_blocked": "Domínio escondido",
   "account.edit_profile": "Editar perfil",
-  "account.endorse": "Feature on profile",
+  "account.endorse": "Destacar no perfil",
   "account.follow": "Seguir",
   "account.followers": "Seguidores",
   "account.follows": "Segue",
@@ -27,7 +27,7 @@
   "account.show_reblogs": "Mostra compartilhamentos de @{name}",
   "account.unblock": "Desbloquear @{name}",
   "account.unblock_domain": "Desbloquear {domain}",
-  "account.unendorse": "Don't feature on profile",
+  "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}",
@@ -162,9 +162,10 @@
   "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": "Mobile apps",
+  "navigation_bar.apps": "Apps",
   "navigation_bar.blocks": "Usuários bloqueados",
   "navigation_bar.community_timeline": "Local",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Mensagens diretas",
   "navigation_bar.discover": "Descobrir",
   "navigation_bar.domain_blocks": "Domínios escondidos",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Desfazer compartilhamento",
   "status.cannot_reblog": "Esta postagem não pode ser compartilhada",
   "status.delete": "Excluir",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Enviar mensagem direta a @{name}",
   "status.embed": "Incorporar",
   "status.favourite": "Adicionar aos favoritos",
diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json
index b3c4c3ad9..92c7343cc 100644
--- a/app/javascript/mastodon/locales/pt.json
+++ b/app/javascript/mastodon/locales/pt.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Utilizadores bloqueados",
   "navigation_bar.community_timeline": "Local",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Este post não pode ser partilhado",
   "status.delete": "Eliminar",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
   "status.embed": "Incorporar",
   "status.favourite": "Adicionar aos favoritos",
diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json
index 8ba26a3c8..7d964859b 100644
--- a/app/javascript/mastodon/locales/ru.json
+++ b/app/javascript/mastodon/locales/ru.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Список блокировки",
   "navigation_bar.community_timeline": "Локальная лента",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Личные сообщения",
   "navigation_bar.discover": "Изучайте",
   "navigation_bar.domain_blocks": "Скрытые домены",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Не продвигать",
   "status.cannot_reblog": "Этот статус не может быть продвинут",
   "status.delete": "Удалить",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Написать @{name}",
   "status.embed": "Встроить",
   "status.favourite": "Нравится",
diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json
index 8c36f866d..e51bb1d48 100644
--- a/app/javascript/mastodon/locales/sk.json
+++ b/app/javascript/mastodon/locales/sk.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blokovaní užívatelia",
   "navigation_bar.community_timeline": "Lokálna časová os",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Súkromné správy",
   "navigation_bar.discover": "Objavuj",
   "navigation_bar.domain_blocks": "Skryté domény",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Nezdieľaj",
   "status.cannot_reblog": "Tento príspevok nemôže byť re-tootnutý",
   "status.delete": "Zmazať",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Súkromná správa @{name}",
   "status.embed": "Vložiť",
   "status.favourite": "Páči sa mi",
diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json
index 01617f790..ca0f7a53f 100644
--- a/app/javascript/mastodon/locales/sl.json
+++ b/app/javascript/mastodon/locales/sl.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blocked users",
   "navigation_bar.community_timeline": "Local timeline",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Delete",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Favourite",
diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json
index 96b46077a..b76710419 100644
--- a/app/javascript/mastodon/locales/sr-Latn.json
+++ b/app/javascript/mastodon/locales/sr-Latn.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blokirani korisnici",
   "navigation_bar.community_timeline": "Lokalna lajna",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Ovaj status ne može da se podrži",
   "status.delete": "Obriši",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
   "status.embed": "Ugradi na sajt",
   "status.favourite": "Omiljeno",
diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json
index 681337947..e31618f66 100644
--- a/app/javascript/mastodon/locales/sr.json
+++ b/app/javascript/mastodon/locales/sr.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Блокирани корисници",
   "navigation_bar.community_timeline": "Локална лајна",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Овај статус не може да се подржи",
   "status.delete": "Обриши",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
   "status.embed": "Угради на сајт",
   "status.favourite": "Омиљено",
diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json
index 5c11e72d9..813bf45df 100644
--- a/app/javascript/mastodon/locales/sv.json
+++ b/app/javascript/mastodon/locales/sv.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blockerade användare",
   "navigation_bar.community_timeline": "Lokal tidslinje",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direktmeddelanden",
   "navigation_bar.discover": "Upptäck",
   "navigation_bar.domain_blocks": "Dolda domäner",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Ta bort knuff",
   "status.cannot_reblog": "Detta inlägg kan inte knuffas",
   "status.delete": "Ta bort",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direktmeddela @{name}",
   "status.embed": "Bädda in",
   "status.favourite": "Favorit",
diff --git a/app/javascript/mastodon/locales/te.json b/app/javascript/mastodon/locales/te.json
index 8aeebd048..2556b0ecb 100644
--- a/app/javascript/mastodon/locales/te.json
+++ b/app/javascript/mastodon/locales/te.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "బ్లాక్ చేయబడిన వినియోగదారులు",
   "navigation_bar.community_timeline": "స్థానిక కాలక్రమం",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "ప్రత్యక్ష సందేశాలు",
   "navigation_bar.discover": "కనుగొను",
   "navigation_bar.domain_blocks": "దాచిన డొమైన్లు",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "బూస్ట్ను తొలగించు",
   "status.cannot_reblog": "ఈ పోస్ట్ను బూస్ట్ చేయడం సాధ్యం కాదు",
   "status.delete": "తొలగించు",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "@{name}కు నేరుగా సందేశం పంపు",
   "status.embed": "ఎంబెడ్",
   "status.favourite": "ఇష్టపడు",
diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json
index 5e4995b71..c02d40d97 100644
--- a/app/javascript/mastodon/locales/th.json
+++ b/app/javascript/mastodon/locales/th.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Blocked users",
   "navigation_bar.community_timeline": "Local timeline",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Delete",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Favourite",
diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json
index be7ad47e4..e3193c99f 100644
--- a/app/javascript/mastodon/locales/tr.json
+++ b/app/javascript/mastodon/locales/tr.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Engellenen kullanıcılar",
   "navigation_bar.community_timeline": "Yerel zaman tüneli",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Direct messages",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Bu gönderi boost edilemez",
   "status.delete": "Sil",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Favorilere ekle",
diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json
index 5f7e5c266..c1a47b307 100644
--- a/app/javascript/mastodon/locales/uk.json
+++ b/app/javascript/mastodon/locales/uk.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "Заблоковані користувачі",
   "navigation_bar.community_timeline": "Локальна стрічка",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "Прямі повідомлення",
   "navigation_bar.discover": "Знайти",
   "navigation_bar.domain_blocks": "Приховані домени",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Цей допис не може бути передмухнутий",
   "status.delete": "Видалити",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Подобається",
diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json
index 378a1a45b..329a95084 100644
--- a/app/javascript/mastodon/locales/zh-CN.json
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "已屏蔽的用户",
   "navigation_bar.community_timeline": "本站时间轴",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "私信",
   "navigation_bar.discover": "发现",
   "navigation_bar.domain_blocks": "已屏蔽的网站",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "取消转嘟",
   "status.cannot_reblog": "无法转嘟这条嘟文",
   "status.delete": "删除",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "发送私信给 @{name}",
   "status.embed": "嵌入",
   "status.favourite": "收藏",
diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json
index f0718468a..df32369e7 100644
--- a/app/javascript/mastodon/locales/zh-HK.json
+++ b/app/javascript/mastodon/locales/zh-HK.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "被你封鎖的用戶",
   "navigation_bar.community_timeline": "本站時間軸",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "個人訊息",
   "navigation_bar.discover": "探索",
   "navigation_bar.domain_blocks": "隱藏的服務站",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "取消轉推",
   "status.cannot_reblog": "這篇文章無法被轉推",
   "status.delete": "刪除",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "私訊 @{name}",
   "status.embed": "鑲嵌",
   "status.favourite": "收藏",
diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json
index ca5919e30..dd7c84b32 100644
--- a/app/javascript/mastodon/locales/zh-TW.json
+++ b/app/javascript/mastodon/locales/zh-TW.json
@@ -165,6 +165,7 @@
   "navigation_bar.apps": "Mobile apps",
   "navigation_bar.blocks": "封鎖的使用者",
   "navigation_bar.community_timeline": "本地時間軸",
+  "navigation_bar.compose": "Compose new toot",
   "navigation_bar.direct": "私訊",
   "navigation_bar.discover": "探索",
   "navigation_bar.domain_blocks": "隱藏的站點",
@@ -258,6 +259,7 @@
   "status.cancel_reblog_private": "取消轉嘟",
   "status.cannot_reblog": "這篇嘟文無法被轉嘟",
   "status.delete": "刪除",
+  "status.detailed_status": "Detailed conversation view",
   "status.direct": "發送私訊給 @{name}",
   "status.embed": "嵌入",
   "status.favourite": "最愛",
diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js
index e5fdc7f83..1cb491e17 100644
--- a/app/javascript/packs/public.js
+++ b/app/javascript/packs/public.js
@@ -63,7 +63,10 @@ function main() {
         .catch(error => console.error(error));
     }
 
-    new Rellax('.parallax', { speed: -1 });
+    const parallaxComponents = document.querySelectorAll('.parallax');
+    if (parallaxComponents.length > 0 ) {
+      new Rellax('.parallax', { speed: -1 });
+    }
 
     const history = createHistory();
     const detailedStatuses = document.querySelectorAll('.public-layout .detailed-status');
diff --git a/app/lib/activitypub/activity.rb b/app/lib/activitypub/activity.rb
index 03476920b..3a39b723e 100644
--- a/app/lib/activitypub/activity.rb
+++ b/app/lib/activitypub/activity.rb
@@ -104,7 +104,9 @@ class ActivityPub::Activity
 
   def crawl_links(status)
     return if status.spoiler_text?
-    LinkCrawlWorker.perform_async(status.id)
+
+    # Spread out crawling randomly to avoid DDoSing the link
+    LinkCrawlWorker.perform_in(rand(1..59).seconds, status.id)
   end
 
   def distribute_to_followers(status)
diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb
index 79efc95d3..f40e1fa3e 100644
--- a/app/lib/activitypub/activity/create.rb
+++ b/app/lib/activitypub/activity/create.rb
@@ -48,7 +48,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
       account: @account,
       text: text_from_content || '',
       language: detected_language,
-      spoiler_text: @object['summary'] || '',
+      spoiler_text: text_from_summary || '',
       created_at: @object['published'],
       override_timestamps: @options[:override_timestamps],
       reply: @object['inReplyTo'].present?,
@@ -193,6 +193,14 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
     end
   end
 
+  def text_from_summary
+    if @object['summary'].present?
+      @object['summary']
+    elsif summary_language_map?
+      @object['summaryMap'].values.first
+    end
+  end
+
   def text_from_name
     if @object['name'].present?
       @object['name']
@@ -206,6 +214,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
       @object['contentMap'].keys.first
     elsif name_language_map?
       @object['nameMap'].keys.first
+    elsif summary_language_map?
+      @object['summaryMap'].keys.first
     elsif supported_object_type?
       LanguageDetector.instance.detect(text_from_content, @account)
     end
@@ -223,6 +233,10 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
     end
   end
 
+  def summary_language_map?
+    @object['summaryMap'].is_a?(Hash) && !@object['summaryMap'].empty?
+  end
+
   def content_language_map?
     @object['contentMap'].is_a?(Hash) && !@object['contentMap'].empty?
   end
diff --git a/app/views/admin/reports/_status.html.haml b/app/views/admin/reports/_status.html.haml
index 5b410ec84..4d557b071 100644
--- a/app/views/admin/reports/_status.html.haml
+++ b/app/views/admin/reports/_status.html.haml
@@ -14,7 +14,7 @@
     - 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: status.proper.sensitive? && !current_account&.user&.setting_display_sensitive_media, width: 610, height: 343, inline: true
+        = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.proper.sensitive? && !current_account&.user&.setting_display_sensitive_media, width: 610, height: 343, inline: true, alt: video.description
       - else
         = react_component :media_gallery, height: 343, sensitive: status.proper.sensitive? && !current_account&.user&.setting_display_sensitive_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 }
 
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index 21713f72e..36b4e9cae 100755
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -30,9 +30,5 @@
     = render partial: 'layouts/theme', object: @core
     = render partial: 'layouts/theme', object: @theme
 
-  - body_classes ||= @body_classes || ''
-  - body_classes += ' system-font' if current_account&.user&.setting_system_font_ui
-  - body_classes += current_account&.user&.setting_reduce_motion ? ' reduce-motion' : ' no-reduce-motion'
-
-  %body{ class: add_rtl_body_class(body_classes) }
+  %body{ class: body_classes }
     = content_for?(:content) ? yield(:content) : yield
diff --git a/app/views/stream_entries/_detailed_status.html.haml b/app/views/stream_entries/_detailed_status.html.haml
index a7c767816..7843005c1 100644
--- a/app/views/stream_entries/_detailed_status.html.haml
+++ b/app/views/stream_entries/_detailed_status.html.haml
@@ -21,7 +21,7 @@
   - if !status.media_attachments.empty?
     - if status.media_attachments.first.video?
       - video = status.media_attachments.first
-      = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, width: 670, height: 380, detailed: true, inline: true
+      = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, width: 670, height: 380, detailed: true, inline: true, alt: video.description
     - else
       = react_component :media_gallery, height: 380, sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, standalone: true, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, 'reduceMotion': current_account&.user&.setting_reduce_motion, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }
   - elsif status.preview_cards.first
diff --git a/app/views/stream_entries/_simple_status.html.haml b/app/views/stream_entries/_simple_status.html.haml
index 1d61684ab..effd4826e 100644
--- a/app/views/stream_entries/_simple_status.html.haml
+++ b/app/views/stream_entries/_simple_status.html.haml
@@ -25,7 +25,7 @@
   - unless status.media_attachments.empty?
     - if status.media_attachments.first.video?
       - video = status.media_attachments.first
-      = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, width: 610, height: 343, inline: true
+      = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, width: 610, height: 343, inline: true, alt: video.description
     - else
       = react_component :media_gallery, height: 343, sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }
 
diff --git a/app/workers/maintenance/destroy_media_worker.rb b/app/workers/maintenance/destroy_media_worker.rb
index 5f052983b..cde33d6d7 100644
--- a/app/workers/maintenance/destroy_media_worker.rb
+++ b/app/workers/maintenance/destroy_media_worker.rb
@@ -6,7 +6,7 @@ class Maintenance::DestroyMediaWorker
   sidekiq_options queue: 'pull'
 
   def perform(media_attachment_id)
-    media = MediaAttachment.find(media_attachment_id)
+    media = media_attachment_id.is_a?(MediaAttachment) ? media_attachment_id : MediaAttachment.find(media_attachment_id)
     media.destroy
   rescue ActiveRecord::RecordNotFound
     true
diff --git a/app/workers/maintenance/redownload_account_media_worker.rb b/app/workers/maintenance/redownload_account_media_worker.rb
index fc26815f2..6afbe6e19 100644
--- a/app/workers/maintenance/redownload_account_media_worker.rb
+++ b/app/workers/maintenance/redownload_account_media_worker.rb
@@ -6,7 +6,7 @@ class Maintenance::RedownloadAccountMediaWorker
   sidekiq_options queue: 'pull', retry: false
 
   def perform(account_id)
-    account = Account.find(account_id)
+    account = account_id.is_a?(Account) ? account_id : Account.find(account_id)
     account.reset_avatar!
     account.reset_header!
     account.save
diff --git a/app/workers/maintenance/uncache_media_worker.rb b/app/workers/maintenance/uncache_media_worker.rb
index 2d1a670a7..4bc62ef75 100644
--- a/app/workers/maintenance/uncache_media_worker.rb
+++ b/app/workers/maintenance/uncache_media_worker.rb
@@ -6,7 +6,7 @@ class Maintenance::UncacheMediaWorker
   sidekiq_options queue: 'pull'
 
   def perform(media_attachment_id)
-    media = MediaAttachment.find(media_attachment_id)
+    media = media_attachment_id.is_a?(MediaAttachment) ? media_attachment_id : MediaAttachment.find(media_attachment_id)
 
     return if media.file.blank?
 
diff --git a/app/workers/scheduler/backup_cleanup_scheduler.rb b/app/workers/scheduler/backup_cleanup_scheduler.rb
index 023a77307..d43660699 100644
--- a/app/workers/scheduler/backup_cleanup_scheduler.rb
+++ b/app/workers/scheduler/backup_cleanup_scheduler.rb
@@ -3,7 +3,7 @@
 class Scheduler::BackupCleanupScheduler
   include Sidekiq::Worker
 
-  sidekiq_options unique: :until_executed
+  sidekiq_options unique: :until_executed, retry: 0
 
   def perform
     old_backups.reorder(nil).find_each(&:destroy!)
diff --git a/app/workers/scheduler/doorkeeper_cleanup_scheduler.rb b/app/workers/scheduler/doorkeeper_cleanup_scheduler.rb
index fec08c6bc..e5e5f6bc4 100644
--- a/app/workers/scheduler/doorkeeper_cleanup_scheduler.rb
+++ b/app/workers/scheduler/doorkeeper_cleanup_scheduler.rb
@@ -3,7 +3,7 @@
 class Scheduler::DoorkeeperCleanupScheduler
   include Sidekiq::Worker
 
-  sidekiq_options unique: :until_executed
+  sidekiq_options unique: :until_executed, retry: 0
 
   def perform
     Doorkeeper::AccessToken.where('revoked_at IS NOT NULL').where('revoked_at < NOW()').delete_all
diff --git a/app/workers/scheduler/email_scheduler.rb b/app/workers/scheduler/email_scheduler.rb
index 24117e424..24ec89b29 100644
--- a/app/workers/scheduler/email_scheduler.rb
+++ b/app/workers/scheduler/email_scheduler.rb
@@ -3,7 +3,7 @@
 class Scheduler::EmailScheduler
   include Sidekiq::Worker
 
-  sidekiq_options unique: :until_executed
+  sidekiq_options unique: :until_executed, retry: 0
 
   def perform
     eligible_users.reorder(nil).find_each do |user|
diff --git a/app/workers/scheduler/feed_cleanup_scheduler.rb b/app/workers/scheduler/feed_cleanup_scheduler.rb
index b02bac883..cd2273418 100644
--- a/app/workers/scheduler/feed_cleanup_scheduler.rb
+++ b/app/workers/scheduler/feed_cleanup_scheduler.rb
@@ -3,7 +3,7 @@
 class Scheduler::FeedCleanupScheduler
   include Sidekiq::Worker
 
-  sidekiq_options unique: :until_executed
+  sidekiq_options unique: :until_executed, retry: 0
 
   def perform
     clean_home_feeds!
diff --git a/app/workers/scheduler/ip_cleanup_scheduler.rb b/app/workers/scheduler/ip_cleanup_scheduler.rb
index 6bb93df7d..42620332e 100644
--- a/app/workers/scheduler/ip_cleanup_scheduler.rb
+++ b/app/workers/scheduler/ip_cleanup_scheduler.rb
@@ -5,7 +5,7 @@ class Scheduler::IpCleanupScheduler
 
   RETENTION_PERIOD = 1.year
 
-  sidekiq_options unique: :until_executed
+  sidekiq_options unique: :until_executed, retry: 0
 
   def perform
     time_ago = RETENTION_PERIOD.ago
diff --git a/app/workers/scheduler/media_cleanup_scheduler.rb b/app/workers/scheduler/media_cleanup_scheduler.rb
index a27e02953..fb01aa70c 100644
--- a/app/workers/scheduler/media_cleanup_scheduler.rb
+++ b/app/workers/scheduler/media_cleanup_scheduler.rb
@@ -3,7 +3,7 @@
 class Scheduler::MediaCleanupScheduler
   include Sidekiq::Worker
 
-  sidekiq_options unique: :until_executed
+  sidekiq_options unique: :until_executed, retry: 0
 
   def perform
     unattached_media.find_each(&:destroy)
diff --git a/app/workers/scheduler/subscriptions_cleanup_scheduler.rb b/app/workers/scheduler/subscriptions_cleanup_scheduler.rb
index 06ba66205..5fba120f6 100644
--- a/app/workers/scheduler/subscriptions_cleanup_scheduler.rb
+++ b/app/workers/scheduler/subscriptions_cleanup_scheduler.rb
@@ -3,7 +3,7 @@
 class Scheduler::SubscriptionsCleanupScheduler
   include Sidekiq::Worker
 
-  sidekiq_options unique: :until_executed
+  sidekiq_options unique: :until_executed, retry: 0
 
   def perform
     Subscription.expired.in_batches.delete_all
diff --git a/app/workers/scheduler/subscriptions_scheduler.rb b/app/workers/scheduler/subscriptions_scheduler.rb
index 4b0959af2..d5873bccb 100644
--- a/app/workers/scheduler/subscriptions_scheduler.rb
+++ b/app/workers/scheduler/subscriptions_scheduler.rb
@@ -3,7 +3,7 @@
 class Scheduler::SubscriptionsScheduler
   include Sidekiq::Worker
 
-  sidekiq_options unique: :until_executed
+  sidekiq_options unique: :until_executed, retry: 0
 
   def perform
     Pubsubhubbub::SubscribeWorker.push_bulk(expiring_accounts.pluck(:id))
diff --git a/app/workers/scheduler/user_cleanup_scheduler.rb b/app/workers/scheduler/user_cleanup_scheduler.rb
index 626fb1652..881b911be 100644
--- a/app/workers/scheduler/user_cleanup_scheduler.rb
+++ b/app/workers/scheduler/user_cleanup_scheduler.rb
@@ -3,7 +3,7 @@
 class Scheduler::UserCleanupScheduler
   include Sidekiq::Worker
 
-  sidekiq_options unique: :until_executed
+  sidekiq_options unique: :until_executed, retry: 0
 
   def perform
     User.where('confirmed_at is NULL AND confirmation_sent_at <= ?', 2.days.ago).reorder(nil).find_in_batches do |batch|
diff --git a/bin/tootctl b/bin/tootctl
new file mode 100755
index 000000000..2fe02523a
--- /dev/null
+++ b/bin/tootctl
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+APP_PATH = File.expand_path('../config/application', __dir__)
+require_relative '../lib/cli'
+Mastodon::CLI.start(ARGV)
diff --git a/config/initializers/paperclip.rb b/config/initializers/paperclip.rb
index 59ab9b9a1..df0205879 100644
--- a/config/initializers/paperclip.rb
+++ b/config/initializers/paperclip.rb
@@ -47,10 +47,10 @@ if ENV['S3_ENABLED'] == 'true'
     Paperclip::Attachment.default_options[:url] = ':s3_path_url'
   end
 
-  if ENV.has_key?('S3_CLOUDFRONT_HOST')
+  if ENV.has_key?('S3_ALIAS_HOST') || ENV.has_key?('S3_CLOUDFRONT_HOST')
     Paperclip::Attachment.default_options.merge!(
       url: ':s3_alias_url',
-      s3_host_alias: ENV['S3_CLOUDFRONT_HOST']
+      s3_host_alias: ENV['S3_ALIAS_HOST'] || ENV['S3_CLOUDFRONT_HOST']
     )
   end
 elsif ENV['SWIFT_ENABLED'] == 'true'
diff --git a/config/locales/cs.yml b/config/locales/cs.yml
index 9ae9c259c..5f1f415ec 100644
--- a/config/locales/cs.yml
+++ b/config/locales/cs.yml
@@ -414,6 +414,9 @@ cs:
       last_delivery: Poslední doručení
       title: WebSub
       topic: Téma
+    suspensions:
+      proceed: Pokračovat
+      title: Suspendovat účet %{acct}
     title: Administrace
   admin_mailer:
     new_report:
diff --git a/config/locales/doorkeeper.da.yml b/config/locales/doorkeeper.da.yml
index dd45a5078..df964e4b1 100644
--- a/config/locales/doorkeeper.da.yml
+++ b/config/locales/doorkeeper.da.yml
@@ -91,7 +91,9 @@ da:
           unknown: Adgangs-beviset er ugyldigt
         resource_owner_authenticator_not_configured: Ressource ejeren kunne ikke blive fundet grundet Doorkeeper.configure.resource_owner_authenticator ikke er konfigureret.
         server_error: Autoriserings serveren blev mødt med en uventet betingelse der forhindrede den i at færdiggøre anmodningen.
+        temporarily_unavailable: Autoriserings serveren er på nuværende tidspunkt ikke i stand til at håndtere anmodningen grundet midlertidig overlast eller serveren er ved at blive opdateret.
         unauthorized_client: Klienten er ikke godkendt til at udføre denne anmodning ved at bruge denne metode.
+        unsupported_grant_type: Autoriserings typen understøttes ikke af autoriserings serveren.
         unsupported_response_type: Godkendelses serveren understøtter ikke denne type respons.
     flash:
       applications:
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 7e062781c..2dbb82b9a 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -350,6 +350,9 @@ en:
       contact_information:
         email: Business e-mail
         username: Contact username
+      custom_css:
+        desc_html: Modify the look with CSS loaded on every page
+        title: Custom CSS
       hero:
         desc_html: Displayed on the frontpage. At least 600x100px recommended. When not set, falls back to instance thumbnail
         title: Hero image
diff --git a/config/locales/ja.yml b/config/locales/ja.yml
index 47177b809..ef62ab32a 100644
--- a/config/locales/ja.yml
+++ b/config/locales/ja.yml
@@ -30,10 +30,14 @@ ja:
     other_instances: 他のインスタンス
     privacy_policy: プライバシーポリシー
     source_code: ソースコード
-    status_count_after: トゥート
+    status_count_after:
+      one: トゥート
+      other: トゥート
     status_count_before: トゥート数
     terms: 利用規約
-    user_count_after: 人
+    user_count_after:
+      one: 人
+      other: 人
     user_count_before: ユーザー数
     what_is_mastodon: Mastodon とは?
   accounts:
@@ -49,7 +53,7 @@ ja:
     people_followed_by: "%{name} さんがフォロー中のアカウント"
     people_who_follow: "%{name} さんをフォロー中のアカウント"
     pin_errors:
-      following: 推薦したい人はあなたが既にフォローしている必要があります
+      following: おすすめしたい人はあなたが既にフォローしている必要があります
     posts: トゥート
     posts_with_replies: トゥートと返信
     reserved_username: このユーザー名は予約されています
@@ -185,7 +189,7 @@ ja:
         unsuspend_account: "%{name} さんが %{target} さんの停止を解除しました"
         update_custom_emoji: "%{name} さんがカスタム絵文字 %{target} を更新しました"
         update_status: "%{name} さんが %{target} さんの投稿を更新しました"
-      deleted_status: "(削除されました)"
+      deleted_status: "(削除済)"
       title: 操作履歴
     custom_emojis:
       by_domain: ドメイン
@@ -414,6 +418,12 @@ ja:
       last_delivery: 最終配送
       title: WebSub
       topic: トピック
+    suspensions:
+      bad_acct_msg: 値が一致しませんでした。停止しようとしているアカウントに間違いはありませんか?
+      hint_html: 'アカウントの停止を確認するには、以下のフィールドに %{value} と入力してください:'
+      proceed: 完全に活動停止させる
+      title: "%{acct} を停止"
+      warning_html: 'このアカウントを停止すると、このアカウントから次のようなデータが<strong>不可逆的に</strong>削除されます:'
     title: 管理
   admin_mailer:
     new_report:
@@ -645,7 +655,6 @@ ja:
           quadrillion: Q
           thousand: K
           trillion: T
-          unit: ''
   pagination:
     newer: 新しいトゥート
     next: 次
@@ -868,7 +877,7 @@ ja:
     recovery_codes_regenerated: リカバリーコードが再生成されました
     recovery_instructions_html: 携帯電話を紛失した場合、以下の内どれかのリカバリーコードを使用してアカウントへアクセスすることができます。<strong>リカバリーコードは大切に保全してください。</strong>たとえば印刷してほかの重要な書類と一緒に保管することができます。
     setup: 初期設定
-    wrong_code: コードが間違っています。サーバー上の時間とデバイス上の時間が一致していることを確認してください。
+    wrong_code: コードが間違っています。サーバー上の時間とデバイス上の時間が一致していますか?
   user_mailer:
     backup_ready:
       explanation: Mastodonアカウントのアーカイブを受け付けました。今すぐダウンロードできます!
diff --git a/config/locales/ka.yml b/config/locales/ka.yml
index 86d982665..c1105b017 100644
--- a/config/locales/ka.yml
+++ b/config/locales/ka.yml
@@ -6,6 +6,7 @@ ka:
     about_this: შესახებ
     administered_by: 'ადმინისტრატორი:'
     api: აპი
+    apps: მობილური აპლიკაციები
     closed_registrations: რეგისტრაციები ამჟამად ინსტანციაზე დახურულია. თუმცა! ანგარიშის შესაქმნელად შეგიძლიათ იპოვოთ სხვა ინსტანცია და იმავე ქსელზე იქონიოთ წვდომა იქიდან.
     contact: კონტაქტი
     contact_missing: არაა დაყენებული
@@ -281,6 +282,7 @@ ka:
       search: ძებნა
       title: ცნობილი ინსტანციები
     invites:
+      deactivate_all: ყველას დეაქტივაცია
       filter:
         all: ყველა
         available: ხელმისაწვდომი
@@ -660,6 +662,9 @@ ka:
     no_account_html: არ გაქვთ ანგარიში? შეგიძლიათ <a href='%{sign_up_path}' target='_blank'>დარეგისტრირდეთ აქ</a>
     proceed: გააგრძელეთ გასაყოლად
     prompt: 'თქვენ გაჰყვებით:'
+  remote_interaction:
+    proceed: გააგრძელეთ ურთიერთქმედება
+    prompt: 'თქვენ გსურთ ურთიერთქმედება ამ ტუტთან:'
   remote_unfollow:
     error: შეცდომა
     title: სათაური
@@ -743,6 +748,7 @@ ka:
       private: არა-საჯარო ტუტი ვერ აიპინება
       reblog: ბუსტი ვერ აიპინება
     show_more: მეტის ჩვენება
+    sign_in_to_participate: საუბარში მონაწილეობისთვის გაიარეთ ავტორიზაცია
     title: '%{name}: "%{quote}"'
     visibilities:
       private: მხოლოდ-მიმდევრები
diff --git a/config/locales/ko.yml b/config/locales/ko.yml
index b5da99049..1ae825443 100644
--- a/config/locales/ko.yml
+++ b/config/locales/ko.yml
@@ -640,7 +640,7 @@ ko:
     publishing: 퍼블리싱
     web: 웹
   remote_follow:
-    acct: 아이디@도메인을 입력해 주십시오
+    acct: 당신이 사용하는 아이디@도메인을 입력해 주십시오
     missing_resource: 리디렉션 대상을 찾을 수 없습니다
     no_account_html: 계정이 없나요? <a href='%{sign_up_path}' target='_blank'>여기에서 가입 할 수 있습니다</a>
     proceed: 팔로우 하기
diff --git a/config/locales/simple_form.fr.yml b/config/locales/simple_form.fr.yml
index b08f4aa9b..c13cfa50d 100644
--- a/config/locales/simple_form.fr.yml
+++ b/config/locales/simple_form.fr.yml
@@ -30,7 +30,7 @@ fr:
       imports:
         data: Un fichier CSV généré par une autre instance de Mastodon
       sessions:
-        otp: 'Entrez le code d’authentification à deux facteurs généré par votre téléphone ou utilisez un de vos codes de récupération :'
+        otp: 'Entrez le code d’authentification à deux facteurs généré par l''application de votre téléphone ou utilisez un de vos codes de récupération :'
       user:
         chosen_languages: Lorsque coché, seuls les pouets dans les langues sélectionnées seront affichés sur les fils publics
     labels:
diff --git a/config/webpack/production.js b/config/webpack/production.js
index 4966807a1..27a78108b 100644
--- a/config/webpack/production.js
+++ b/config/webpack/production.js
@@ -23,8 +23,8 @@ try {
 let attachmentHost;
 
 if (process.env.S3_ENABLED === 'true') {
-  if (process.env.S3_CLOUDFRONT_HOST) {
-    attachmentHost = process.env.S3_CLOUDFRONT_HOST;
+  if (process.env.S3_ALIAS_HOST || process.env.S3_CLOUDFRONT_HOST) {
+    attachmentHost = process.env.S3_ALIAS_HOST || process.env.S3_CLOUDFRONT_HOST;
   } else {
     attachmentHost = process.env.S3_HOSTNAME || `s3-${process.env.S3_REGION || 'us-east-1'}.amazonaws.com`;
   }
diff --git a/lib/cli.rb b/lib/cli.rb
new file mode 100644
index 000000000..7e82806b6
--- /dev/null
+++ b/lib/cli.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'thor'
+require_relative 'mastodon/media_cli'
+
+module Mastodon
+  class CLI < Thor
+    desc 'media SUBCOMMAND ...ARGS', 'manage media files'
+    subcommand 'media', Mastodon::MediaCLI
+  end
+end
diff --git a/lib/mastodon/media_cli.rb b/lib/mastodon/media_cli.rb
new file mode 100644
index 000000000..cc6ad07d9
--- /dev/null
+++ b/lib/mastodon/media_cli.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require_relative '../../config/boot'
+require_relative '../../config/environment'
+
+# rubocop:disable Rails/Output
+
+module Mastodon
+  class MediaCLI < Thor
+    option :days, type: :numeric, default: 7
+    option :background, type: :boolean, default: false
+    desc 'remove', 'remove remote media files'
+    long_desc <<-DESC
+      Removes locally cached copies of media attachments from other servers.
+
+      The --days option specifies how old media attachments have to be before
+      they are removed. It defaults to 7 days.
+
+      With the --background option, instead of deleting the files sequentially,
+      they will be queued into Sidekiq and the command will exit as soon as
+      possible. In Sidekiq they will be processed with higher concurrency, but
+      it may impact other operations of the Mastodon server, and it may overload
+      the underlying file storage.
+    DESC
+    def remove
+      time_ago = options[:days].days.ago
+      queued   = 0
+
+      MediaAttachment.where.not(remote_url: '').where.not(file_file_name: nil).where('created_at < ?', time_ago).select(:id).reorder(nil).find_in_batches do |media_attachments|
+        if options[:background]
+          queued += media_attachments.size
+          Maintenance::UncacheMediaWorker.push_bulk(media_attachments.map(&:id))
+        else
+          media_attachments.each do |m|
+            Maintenance::UncacheMediaWorker.new.perform(m)
+            print '.'
+          end
+        end
+      end
+
+      puts
+      puts "Scheduled the deletion of #{queued} media attachments" if options[:background]
+    end
+  end
+end
+
+# rubocop:enable Rails/Output
diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake
index 191ce634c..519c21785 100644
--- a/lib/tasks/mastodon.rake
+++ b/lib/tasks/mastodon.rake
@@ -222,7 +222,7 @@ namespace :mastodon do
         end
 
         if prompt.yes?('Do you want to access the uploaded files from your own domain?')
-          env['S3_CLOUDFRONT_HOST'] = prompt.ask('Domain for uploaded files:') do |q|
+          env['S3_ALIAS_HOST'] = prompt.ask('Domain for uploaded files:') do |q|
             q.required true
             q.default "files.#{env['LOCAL_DOMAIN']}"
             q.modify :strip
@@ -512,14 +512,9 @@ namespace :mastodon do
 
     desc 'Remove cached remote media attachments that are older than NUM_DAYS. By default 7 (week)'
     task remove_remote: :environment do
-      time_ago = ENV.fetch('NUM_DAYS') { 7 }.to_i.days.ago
-      nb_media_attachments = 0
-
-      MediaAttachment.where.not(remote_url: '').where.not(file_file_name: nil).where('created_at < ?', time_ago).select(:id).reorder(nil).find_in_batches do |media_attachments|
-        nb_media_attachments += media_attachments.length
-        Maintenance::UncacheMediaWorker.push_bulk(media_attachments.map(&:id))
-      end
-      puts "Scheduled the deletion of #{nb_media_attachments} media attachments"
+      require_relative '../mastodon/media_cli'
+      cli = Mastodon::MediaCLI.new([], days: ENV['NUM_DAYS'] || 7)
+      cli.invoke(:remove)
     end
 
     desc 'Set unknown attachment type for remote-only attachments'
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index ac54f1f70..3ccd96f44 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -17,7 +17,7 @@ describe ApplicationHelper do
     end
   end
 
-  describe 'add_rtl_body_class' do
+  describe 'locale_direction' do
     around do |example|
       current_locale = I18n.locale
       example.run
@@ -26,22 +26,22 @@ describe ApplicationHelper do
 
     it 'adds rtl body class if locale is Arabic' do
       I18n.locale = :ar
-      expect(helper.add_rtl_body_class('other classes')).to eq 'other classes rtl'
+      expect(helper.locale_direction).to eq 'rtl'
     end
 
     it 'adds rtl body class if locale is Farsi' do
       I18n.locale = :fa
-      expect(helper.add_rtl_body_class('other classes')).to eq 'other classes rtl'
+      expect(helper.locale_direction).to eq 'rtl'
     end
 
     it 'adds rtl if locale is Hebrew' do
       I18n.locale = :he
-      expect(helper.add_rtl_body_class('other classes')).to eq 'other classes rtl'
+      expect(helper.locale_direction).to eq 'rtl'
     end
 
     it 'does not add rtl if locale is Thai' do
       I18n.locale = :th
-      expect(helper.add_rtl_body_class('other classes')).to eq 'other classes'
+      expect(helper.locale_direction).to_not eq 'rtl'
     end
   end
 
diff --git a/streaming/index.js b/streaming/index.js
index d7bfa6542..1c6004b77 100644
--- a/streaming/index.js
+++ b/streaming/index.js
@@ -9,6 +9,7 @@ const log = require('npmlog');
 const url = require('url');
 const WebSocket = require('uws');
 const uuid = require('uuid');
+const fs = require('fs');
 
 const env = process.env.NODE_ENV || 'development';
 
@@ -70,6 +71,9 @@ const redisUrlToClient = (defaultConfig, redisUrl) => {
 const numWorkers = +process.env.STREAMING_CLUSTER_NUM || (env === 'development' ? 1 : Math.max(os.cpus().length - 1, 1));
 
 const startMaster = () => {
+  if (!process.env.SOCKET && process.env.PORT && isNaN(+process.env.PORT)) {
+    log.warn('UNIX domain socket is now supported by using SOCKET. Please migrate from PORT hack.');
+  }
   log.info(`Starting streaming API server master with ${numWorkers} workers`);
 };
 
@@ -448,6 +452,12 @@ const startWorker = (workerId) => {
   app.use(setRequestId);
   app.use(setRemoteAddress);
   app.use(allowCrossDomain);
+
+  app.get('/api/v1/streaming/health', (req, res) => {
+    res.writeHead(200, { 'Content-Type': 'text/plain' });
+    res.end('OK');
+  });
+
   app.use(authenticationMiddleware);
   app.use(errorMiddleware);
 
@@ -574,9 +584,16 @@ const startWorker = (workerId) => {
     });
   }, 30000);
 
-  server.listen(process.env.PORT || 4000, process.env.BIND || '0.0.0.0', () => {
-    log.info(`Worker ${workerId} now listening on ${server.address().address}:${server.address().port}`);
-  });
+  if (process.env.SOCKET || process.env.PORT && isNaN(+process.env.PORT)) {
+    server.listen(process.env.SOCKET || process.env.PORT, () => {
+      fs.chmodSync(server.address(), 0o666);
+      log.info(`Worker ${workerId} now listening on ${server.address()}`);
+    });
+  } else {
+    server.listen(+process.env.PORT || 4000, process.env.BIND || '0.0.0.0', () => {
+      log.info(`Worker ${workerId} now listening on ${server.address().address}:${server.address().port}`);
+    });
+  }
 
   const onExit = () => {
     log.info(`Worker ${workerId} exiting, bye bye`);