about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
authorThibaut Girka <thib@sitedethib.com>2018-10-01 12:43:20 +0200
committerThibaut Girka <thib@sitedethib.com>2018-10-01 12:43:20 +0200
commitc91d9b7389c489980fe98812a8f3884b2d41e19e (patch)
tree78509567984a32411c661167e254c8645eba2919 /app
parent4e60a0d5433f5dfa4f71a452cc5c6ceb0f21ceab (diff)
parent5d5d1b528eda560191b2813714c9e461fda3c9d8 (diff)
Merge branch 'master' into glitch-soc/merge-upstream
Conflicts:
	app/lib/user_settings_decorator.rb
	app/models/user.rb
	app/serializers/initial_state_serializer.rb
	app/views/stream_entries/_simple_status.html.haml
	config/locales/simple_form.en.yml
	config/locales/simple_form.ja.yml
	config/locales/simple_form.pl.yml
	config/routes.rb
Diffstat (limited to 'app')
-rw-r--r--app/controllers/api/base_controller.rb4
-rw-r--r--app/controllers/api/v1/accounts/statuses_controller.rb7
-rw-r--r--app/controllers/api/v1/favourites_controller.rb7
-rw-r--r--app/controllers/api/v1/instances_controller.rb4
-rw-r--r--app/controllers/api/v1/notifications_controller.rb7
-rw-r--r--app/controllers/api/v1/reports_controller.rb5
-rw-r--r--app/controllers/api/v1/timelines/home_controller.rb5
-rw-r--r--app/controllers/api/v1/timelines/list_controller.rb5
-rw-r--r--app/controllers/api/v1/timelines/public_controller.rb7
-rw-r--r--app/controllers/api/v1/timelines/tag_controller.rb7
-rw-r--r--app/controllers/settings/preferences_controller.rb3
-rw-r--r--app/helpers/application_helper.rb4
-rw-r--r--app/javascript/mastodon/actions/importer/normalizer.js3
-rw-r--r--app/javascript/mastodon/components/__tests__/__snapshots__/autosuggest_emoji-test.js.snap27
-rw-r--r--app/javascript/mastodon/components/__tests__/autosuggest_emoji-test.js29
-rw-r--r--app/javascript/mastodon/components/media_gallery.js4
-rw-r--r--app/javascript/mastodon/components/status.js2
-rw-r--r--app/javascript/mastodon/components/status_content.js40
-rw-r--r--app/javascript/mastodon/features/account_gallery/components/media_item.js4
-rw-r--r--app/javascript/mastodon/features/notifications/components/column_settings.js9
-rw-r--r--app/javascript/mastodon/features/notifications/components/notification.js5
-rw-r--r--app/javascript/mastodon/features/notifications/components/setting_toggle.js4
-rw-r--r--app/javascript/mastodon/features/ui/index.js8
-rw-r--r--app/javascript/mastodon/features/video/index.js15
-rw-r--r--app/javascript/mastodon/initial_state.js3
-rw-r--r--app/javascript/mastodon/locales/ar.json1
-rw-r--r--app/javascript/mastodon/locales/ast.json1
-rw-r--r--app/javascript/mastodon/locales/bg.json1
-rw-r--r--app/javascript/mastodon/locales/ca.json1
-rw-r--r--app/javascript/mastodon/locales/co.json1
-rw-r--r--app/javascript/mastodon/locales/cs.json1
-rw-r--r--app/javascript/mastodon/locales/cy.json668
-rw-r--r--app/javascript/mastodon/locales/da.json1
-rw-r--r--app/javascript/mastodon/locales/de.json1
-rw-r--r--app/javascript/mastodon/locales/defaultMessages.json6
-rw-r--r--app/javascript/mastodon/locales/el.json1
-rw-r--r--app/javascript/mastodon/locales/en.json1
-rw-r--r--app/javascript/mastodon/locales/eo.json1
-rw-r--r--app/javascript/mastodon/locales/es.json1
-rw-r--r--app/javascript/mastodon/locales/eu.json1
-rw-r--r--app/javascript/mastodon/locales/fa.json1
-rw-r--r--app/javascript/mastodon/locales/fi.json1
-rw-r--r--app/javascript/mastodon/locales/fr.json1
-rw-r--r--app/javascript/mastodon/locales/gl.json1
-rw-r--r--app/javascript/mastodon/locales/he.json1
-rw-r--r--app/javascript/mastodon/locales/hr.json1
-rw-r--r--app/javascript/mastodon/locales/hu.json1
-rw-r--r--app/javascript/mastodon/locales/hy.json1
-rw-r--r--app/javascript/mastodon/locales/id.json1
-rw-r--r--app/javascript/mastodon/locales/io.json1
-rw-r--r--app/javascript/mastodon/locales/it.json23
-rw-r--r--app/javascript/mastodon/locales/ja.json1
-rw-r--r--app/javascript/mastodon/locales/ka.json1
-rw-r--r--app/javascript/mastodon/locales/ko.json1
-rw-r--r--app/javascript/mastodon/locales/nl.json1
-rw-r--r--app/javascript/mastodon/locales/no.json1
-rw-r--r--app/javascript/mastodon/locales/oc.json1
-rw-r--r--app/javascript/mastodon/locales/pl.json1
-rw-r--r--app/javascript/mastodon/locales/pt-BR.json1
-rw-r--r--app/javascript/mastodon/locales/pt.json1
-rw-r--r--app/javascript/mastodon/locales/ro.json1
-rw-r--r--app/javascript/mastodon/locales/ru.json1
-rw-r--r--app/javascript/mastodon/locales/sk.json1
-rw-r--r--app/javascript/mastodon/locales/sl.json1
-rw-r--r--app/javascript/mastodon/locales/sr-Latn.json1
-rw-r--r--app/javascript/mastodon/locales/sr.json1
-rw-r--r--app/javascript/mastodon/locales/sv.json1
-rw-r--r--app/javascript/mastodon/locales/ta.json1
-rw-r--r--app/javascript/mastodon/locales/te.json1
-rw-r--r--app/javascript/mastodon/locales/th.json1
-rw-r--r--app/javascript/mastodon/locales/tr.json1
-rw-r--r--app/javascript/mastodon/locales/uk.json1
-rw-r--r--app/javascript/mastodon/locales/zh-CN.json1
-rw-r--r--app/javascript/mastodon/locales/zh-HK.json1
-rw-r--r--app/javascript/mastodon/locales/zh-TW.json1
-rw-r--r--app/javascript/mastodon/service_worker/web_push_notifications.js2
-rw-r--r--app/javascript/styles/mailer.scss2
-rw-r--r--app/javascript/styles/mastodon/about.scss46
-rw-r--r--app/javascript/styles/mastodon/admin.scss2
-rw-r--r--app/javascript/styles/mastodon/basics.scss6
-rw-r--r--app/javascript/styles/mastodon/components.scss33
-rw-r--r--app/javascript/styles/mastodon/containers.scss4
-rw-r--r--app/javascript/styles/mastodon/dashboard.scss2
-rw-r--r--app/javascript/styles/mastodon/forms.scss8
-rw-r--r--app/javascript/styles/mastodon/rtl.scss30
-rw-r--r--app/javascript/styles/mastodon/tables.scss2
-rw-r--r--app/javascript/styles/mastodon/variables.scss4
-rw-r--r--app/lib/activitypub/activity/create.rb2
-rw-r--r--app/lib/feed_manager.rb1
-rw-r--r--app/lib/user_settings_decorator.rb45
-rw-r--r--app/models/concerns/paginable.rb8
-rw-r--r--app/models/feed.rb16
-rw-r--r--app/models/home_feed.rb8
-rw-r--r--app/models/user.rb12
-rw-r--r--app/serializers/initial_state_serializer.rb19
-rw-r--r--app/views/accounts/_header.html.haml2
-rw-r--r--app/views/accounts/_moved.html.haml2
-rw-r--r--app/views/admin/reports/_status.html.haml4
-rw-r--r--app/views/settings/preferences/show.html.haml3
-rw-r--r--app/views/stream_entries/_detailed_status.html.haml8
-rw-r--r--app/views/stream_entries/_simple_status.html.haml8
101 files changed, 675 insertions, 566 deletions
diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb
index 90f42251e..ac8de5fc0 100644
--- a/app/controllers/api/base_controller.rb
+++ b/app/controllers/api/base_controller.rb
@@ -53,6 +53,10 @@ class Api::BaseController < ApplicationController
     [params[:limit].to_i.abs, default_limit * 2].min
   end
 
+  def params_slice(*keys)
+    params.slice(*keys).permit(*keys)
+  end
+
   def current_resource_owner
     @current_user ||= User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
   end
diff --git a/app/controllers/api/v1/accounts/statuses_controller.rb b/app/controllers/api/v1/accounts/statuses_controller.rb
index 06fa6c762..b68a8805f 100644
--- a/app/controllers/api/v1/accounts/statuses_controller.rb
+++ b/app/controllers/api/v1/accounts/statuses_controller.rb
@@ -28,10 +28,9 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
 
   def account_statuses
     statuses = truthy_param?(:pinned) ? pinned_scope : permitted_account_statuses
-    statuses = statuses.paginate_by_max_id(
+    statuses = statuses.paginate_by_id(
       limit_param(DEFAULT_STATUSES_LIMIT),
-      params[:max_id],
-      params[:since_id]
+      params_slice(:max_id, :since_id, :min_id)
     )
 
     statuses.merge!(only_media_scope) if truthy_param?(:only_media)
@@ -82,7 +81,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
 
   def prev_path
     unless @statuses.empty?
-      api_v1_account_statuses_url pagination_params(since_id: pagination_since_id)
+      api_v1_account_statuses_url pagination_params(min_id: pagination_since_id)
     end
   end
 
diff --git a/app/controllers/api/v1/favourites_controller.rb b/app/controllers/api/v1/favourites_controller.rb
index ab5204355..db827f9d4 100644
--- a/app/controllers/api/v1/favourites_controller.rb
+++ b/app/controllers/api/v1/favourites_controller.rb
@@ -26,10 +26,9 @@ class Api::V1::FavouritesController < Api::BaseController
   end
 
   def results
-    @_results ||= account_favourites.paginate_by_max_id(
+    @_results ||= account_favourites.paginate_by_id(
       limit_param(DEFAULT_STATUSES_LIMIT),
-      params[:max_id],
-      params[:since_id]
+      params_slice(:max_id, :since_id, :min_id)
     )
   end
 
@@ -49,7 +48,7 @@ class Api::V1::FavouritesController < Api::BaseController
 
   def prev_path
     unless results.empty?
-      api_v1_favourites_url pagination_params(since_id: pagination_since_id)
+      api_v1_favourites_url pagination_params(min_id: pagination_since_id)
     end
   end
 
diff --git a/app/controllers/api/v1/instances_controller.rb b/app/controllers/api/v1/instances_controller.rb
index 1c6971c18..5686e8d7c 100644
--- a/app/controllers/api/v1/instances_controller.rb
+++ b/app/controllers/api/v1/instances_controller.rb
@@ -4,6 +4,8 @@ class Api::V1::InstancesController < Api::BaseController
   respond_to :json
 
   def show
-    render json: {}, serializer: REST::InstanceSerializer
+    render_cached_json('api:v1:instances', expires_in: 5.minutes) do
+      ActiveModelSerializers::SerializableResource.new({}, serializer: REST::InstanceSerializer)
+    end
   end
 end
diff --git a/app/controllers/api/v1/notifications_controller.rb b/app/controllers/api/v1/notifications_controller.rb
index a8ed5a63b..3b492c516 100644
--- a/app/controllers/api/v1/notifications_controller.rb
+++ b/app/controllers/api/v1/notifications_controller.rb
@@ -46,10 +46,9 @@ class Api::V1::NotificationsController < Api::BaseController
   end
 
   def paginated_notifications
-    browserable_account_notifications.paginate_by_max_id(
+    browserable_account_notifications.paginate_by_id(
       limit_param(DEFAULT_NOTIFICATIONS_LIMIT),
-      params[:max_id],
-      params[:since_id]
+      params_slice(:max_id, :since_id, :min_id)
     )
   end
 
@@ -73,7 +72,7 @@ class Api::V1::NotificationsController < Api::BaseController
 
   def prev_path
     unless @notifications.empty?
-      api_v1_notifications_url pagination_params(since_id: pagination_since_id)
+      api_v1_notifications_url pagination_params(min_id: pagination_since_id)
     end
   end
 
diff --git a/app/controllers/api/v1/reports_controller.rb b/app/controllers/api/v1/reports_controller.rb
index a954101cb..9c6ee0a50 100644
--- a/app/controllers/api/v1/reports_controller.rb
+++ b/app/controllers/api/v1/reports_controller.rb
@@ -7,11 +7,6 @@ class Api::V1::ReportsController < Api::BaseController
 
   respond_to :json
 
-  def index
-    @reports = current_account.reports
-    render json: @reports, each_serializer: REST::ReportSerializer
-  end
-
   def create
     @report = ReportService.new.call(
       current_account,
diff --git a/app/controllers/api/v1/timelines/home_controller.rb b/app/controllers/api/v1/timelines/home_controller.rb
index 4412aaaa3..fcd0757f1 100644
--- a/app/controllers/api/v1/timelines/home_controller.rb
+++ b/app/controllers/api/v1/timelines/home_controller.rb
@@ -30,7 +30,8 @@ class Api::V1::Timelines::HomeController < Api::BaseController
     account_home_feed.get(
       limit_param(DEFAULT_STATUSES_LIMIT),
       params[:max_id],
-      params[:since_id]
+      params[:since_id],
+      params[:min_id]
     )
   end
 
@@ -51,7 +52,7 @@ class Api::V1::Timelines::HomeController < Api::BaseController
   end
 
   def prev_path
-    api_v1_timelines_home_url pagination_params(since_id: pagination_since_id)
+    api_v1_timelines_home_url pagination_params(min_id: pagination_since_id)
   end
 
   def pagination_max_id
diff --git a/app/controllers/api/v1/timelines/list_controller.rb b/app/controllers/api/v1/timelines/list_controller.rb
index cfc5f3b5e..a15eae468 100644
--- a/app/controllers/api/v1/timelines/list_controller.rb
+++ b/app/controllers/api/v1/timelines/list_controller.rb
@@ -32,7 +32,8 @@ class Api::V1::Timelines::ListController < Api::BaseController
     list_feed.get(
       limit_param(DEFAULT_STATUSES_LIMIT),
       params[:max_id],
-      params[:since_id]
+      params[:since_id],
+      params[:min_id]
     )
   end
 
@@ -53,7 +54,7 @@ class Api::V1::Timelines::ListController < Api::BaseController
   end
 
   def prev_path
-    api_v1_timelines_list_url params[:id], pagination_params(since_id: pagination_since_id)
+    api_v1_timelines_list_url params[:id], pagination_params(min_id: pagination_since_id)
   end
 
   def pagination_max_id
diff --git a/app/controllers/api/v1/timelines/public_controller.rb b/app/controllers/api/v1/timelines/public_controller.rb
index 13fe015b7..aabe24324 100644
--- a/app/controllers/api/v1/timelines/public_controller.rb
+++ b/app/controllers/api/v1/timelines/public_controller.rb
@@ -21,10 +21,9 @@ class Api::V1::Timelines::PublicController < Api::BaseController
   end
 
   def public_statuses
-    statuses = public_timeline_statuses.paginate_by_max_id(
+    statuses = public_timeline_statuses.paginate_by_id(
       limit_param(DEFAULT_STATUSES_LIMIT),
-      params[:max_id],
-      params[:since_id]
+      params_slice(:max_id, :since_id, :min_id)
     )
 
     if truthy_param?(:only_media)
@@ -53,7 +52,7 @@ class Api::V1::Timelines::PublicController < Api::BaseController
   end
 
   def prev_path
-    api_v1_timelines_public_url pagination_params(since_id: pagination_since_id)
+    api_v1_timelines_public_url pagination_params(min_id: pagination_since_id)
   end
 
   def pagination_max_id
diff --git a/app/controllers/api/v1/timelines/tag_controller.rb b/app/controllers/api/v1/timelines/tag_controller.rb
index 7de49a5ed..cf58d5cf4 100644
--- a/app/controllers/api/v1/timelines/tag_controller.rb
+++ b/app/controllers/api/v1/timelines/tag_controller.rb
@@ -29,10 +29,9 @@ class Api::V1::Timelines::TagController < Api::BaseController
     if @tag.nil?
       []
     else
-      statuses = tag_timeline_statuses.paginate_by_max_id(
+      statuses = tag_timeline_statuses.paginate_by_id(
         limit_param(DEFAULT_STATUSES_LIMIT),
-        params[:max_id],
-        params[:since_id]
+        params_slice(:max_id, :since_id, :min_id)
       )
 
       if truthy_param?(:only_media)
@@ -62,7 +61,7 @@ class Api::V1::Timelines::TagController < Api::BaseController
   end
 
   def prev_path
-    api_v1_timelines_tag_url params[:id], pagination_params(since_id: pagination_since_id)
+    api_v1_timelines_tag_url params[:id], pagination_params(min_id: pagination_since_id)
   end
 
   def pagination_max_id
diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb
index d60e6a89f..5c5f31d2b 100644
--- a/app/controllers/settings/preferences_controller.rb
+++ b/app/controllers/settings/preferences_controller.rb
@@ -37,7 +37,8 @@ class Settings::PreferencesController < Settings::BaseController
       :setting_favourite_modal,
       :setting_delete_modal,
       :setting_auto_play_gif,
-      :setting_display_sensitive_media,
+      :setting_display_media,
+      :setting_expand_spoilers,
       :setting_reduce_motion,
       :setting_system_font_ui,
       :setting_noindex,
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 6b41fd36e..c002017ef 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -7,8 +7,8 @@ module ApplicationHelper
     follow
   ).freeze
 
-  def active_nav_class(path)
-    current_page?(path) ? 'active' : ''
+  def active_nav_class(*paths)
+    paths.any? { |path| current_page?(path) } ? 'active' : ''
   end
 
   def active_link_to(label, path, **options)
diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js
index 10a39e050..a2af3222e 100644
--- a/app/javascript/mastodon/actions/importer/normalizer.js
+++ b/app/javascript/mastodon/actions/importer/normalizer.js
@@ -1,6 +1,7 @@
 import escapeTextContentForBrowser from 'escape-html';
 import emojify from '../../features/emoji/emoji';
 import { unescapeHTML } from '../../utils/html';
+import { expandSpoilers } from '../../initial_state';
 
 const domParser = new DOMParser();
 
@@ -57,7 +58,7 @@ export function normalizeStatus(status, normalOldStatus) {
     normalStatus.search_index = domParser.parseFromString(searchContent, 'text/html').documentElement.textContent;
     normalStatus.contentHtml  = emojify(normalStatus.content, emojiMap);
     normalStatus.spoilerHtml  = emojify(escapeTextContentForBrowser(spoilerText), emojiMap);
-    normalStatus.hidden       = spoilerText.length > 0 || normalStatus.sensitive;
+    normalStatus.hidden       = expandSpoilers ? false : spoilerText.length > 0 || normalStatus.sensitive;
   }
 
   return normalStatus;
diff --git a/app/javascript/mastodon/components/__tests__/__snapshots__/autosuggest_emoji-test.js.snap b/app/javascript/mastodon/components/__tests__/__snapshots__/autosuggest_emoji-test.js.snap
new file mode 100644
index 000000000..1c3727848
--- /dev/null
+++ b/app/javascript/mastodon/components/__tests__/__snapshots__/autosuggest_emoji-test.js.snap
@@ -0,0 +1,27 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`<AutosuggestEmoji /> renders emoji with custom url 1`] = `
+<div
+  className="autosuggest-emoji"
+>
+  <img
+    alt="foobar"
+    className="emojione"
+    src="http://example.com/emoji.png"
+  />
+  :foobar:
+</div>
+`;
+
+exports[`<AutosuggestEmoji /> renders native emoji 1`] = `
+<div
+  className="autosuggest-emoji"
+>
+  <img
+    alt="💙"
+    className="emojione"
+    src="/emoji/1f499.svg"
+  />
+  :foobar:
+</div>
+`;
diff --git a/app/javascript/mastodon/components/__tests__/autosuggest_emoji-test.js b/app/javascript/mastodon/components/__tests__/autosuggest_emoji-test.js
new file mode 100644
index 000000000..05616e444
--- /dev/null
+++ b/app/javascript/mastodon/components/__tests__/autosuggest_emoji-test.js
@@ -0,0 +1,29 @@
+import React from 'react';
+import renderer from 'react-test-renderer';
+import AutosuggestEmoji from '../autosuggest_emoji';
+
+describe('<AutosuggestEmoji />', () => {
+  it('renders native emoji', () => {
+    const emoji = {
+      native: '💙',
+      colons: ':foobar:',
+    };
+    const component = renderer.create(<AutosuggestEmoji emoji={emoji} />);
+    const tree      = component.toJSON();
+
+    expect(tree).toMatchSnapshot();
+  });
+
+  it('renders emoji with custom url', () => {
+    const emoji = {
+      custom: true,
+      imageUrl: 'http://example.com/emoji.png',
+      native: 'foobar',
+      colons: ':foobar:',
+    };
+    const component = renderer.create(<AutosuggestEmoji emoji={emoji} />);
+    const tree      = component.toJSON();
+
+    expect(tree).toMatchSnapshot();
+  });
+});
diff --git a/app/javascript/mastodon/components/media_gallery.js b/app/javascript/mastodon/components/media_gallery.js
index a1785196f..ed0e4ff1b 100644
--- a/app/javascript/mastodon/components/media_gallery.js
+++ b/app/javascript/mastodon/components/media_gallery.js
@@ -6,7 +6,7 @@ import IconButton from './icon_button';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import { isIOS } from '../is_mobile';
 import classNames from 'classnames';
-import { autoPlayGif, displaySensitiveMedia } from '../initial_state';
+import { autoPlayGif, displayMedia } from '../initial_state';
 
 const messages = defineMessages({
   toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' },
@@ -197,7 +197,7 @@ class MediaGallery extends React.PureComponent {
   };
 
   state = {
-    visible: !this.props.sensitive || displaySensitiveMedia,
+    visible: displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all',
   };
 
   componentWillReceiveProps (nextProps) {
diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js
index 6c595c712..90c689a75 100644
--- a/app/javascript/mastodon/components/status.js
+++ b/app/javascript/mastodon/components/status.js
@@ -285,7 +285,7 @@ class Status extends ImmutablePureComponent {
               </a>
             </div>
 
-            <StatusContent status={status} onClick={this.handleClick} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} />
+            <StatusContent status={status} onClick={this.handleClick} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} collapsable />
 
             {media}
 
diff --git a/app/javascript/mastodon/components/status_content.js b/app/javascript/mastodon/components/status_content.js
index 81013747e..eda7d6ac3 100644
--- a/app/javascript/mastodon/components/status_content.js
+++ b/app/javascript/mastodon/components/status_content.js
@@ -6,6 +6,8 @@ import { FormattedMessage } from 'react-intl';
 import Permalink from './permalink';
 import classnames from 'classnames';
 
+const MAX_HEIGHT = 642; // 20px * 32 (+ 2px padding at the top)
+
 export default class StatusContent extends React.PureComponent {
 
   static contextTypes = {
@@ -17,10 +19,12 @@ export default class StatusContent extends React.PureComponent {
     expanded: PropTypes.bool,
     onExpandedToggle: PropTypes.func,
     onClick: PropTypes.func,
+    collapsable: PropTypes.bool,
   };
 
   state = {
     hidden: true,
+    collapsed: null, //  `collapsed: null` indicates that an element doesn't need collapsing, while `true` or `false` indicates that it does (and is/isn't).
   };
 
   _updateStatusLinks () {
@@ -53,6 +57,16 @@ export default class StatusContent extends React.PureComponent {
       link.setAttribute('target', '_blank');
       link.setAttribute('rel', 'noopener');
     }
+
+    if (
+      this.props.collapsable
+      && this.props.onClick
+      && this.state.collapsed === null
+      && node.clientHeight > MAX_HEIGHT
+      && this.props.status.get('spoiler_text').length === 0
+    ) {
+      this.setState({ collapsed: true });
+    }
   }
 
   componentDidMount () {
@@ -113,6 +127,11 @@ export default class StatusContent extends React.PureComponent {
     }
   }
 
+  handleCollapsedClick = (e) => {
+    e.preventDefault();
+    this.setState({ collapsed: !this.state.collapsed });
+  }
+
   setRef = (c) => {
     this.node = c;
   }
@@ -132,12 +151,19 @@ export default class StatusContent extends React.PureComponent {
     const classNames = classnames('status__content', {
       'status__content--with-action': this.props.onClick && this.context.router,
       'status__content--with-spoiler': status.get('spoiler_text').length > 0,
+      'status__content--collapsed': this.state.collapsed === true,
     });
 
     if (isRtl(status.get('search_index'))) {
       directionStyle.direction = 'rtl';
     }
 
+    const readMoreButton = (
+      <button className='status__content__read-more-button' onClick={this.props.onClick}>
+        <FormattedMessage id='status.read_more' defaultMessage='Read more' /><i className='fa fa-fw fa-angle-right' />
+      </button>
+    );
+
     if (status.get('spoiler_text').length > 0) {
       let mentionsPlaceholder = '';
 
@@ -167,17 +193,23 @@ export default class StatusContent extends React.PureComponent {
         </div>
       );
     } else if (this.props.onClick) {
-      return (
+      const output = [
         <div
           ref={this.setRef}
           tabIndex='0'
           className={classNames}
           style={directionStyle}
+          dangerouslySetInnerHTML={content}
           onMouseDown={this.handleMouseDown}
           onMouseUp={this.handleMouseUp}
-          dangerouslySetInnerHTML={content}
-        />
-      );
+        />,
+      ];
+
+      if (this.state.collapsed) {
+        output.push(readMoreButton);
+      }
+
+      return output;
     } else {
       return (
         <div
diff --git a/app/javascript/mastodon/features/account_gallery/components/media_item.js b/app/javascript/mastodon/features/account_gallery/components/media_item.js
index f7a802dc7..7c330c430 100644
--- a/app/javascript/mastodon/features/account_gallery/components/media_item.js
+++ b/app/javascript/mastodon/features/account_gallery/components/media_item.js
@@ -2,7 +2,7 @@ import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import Permalink from '../../../components/permalink';
-import { displaySensitiveMedia } from '../../../initial_state';
+import { displayMedia } from '../../../initial_state';
 
 export default class MediaItem extends ImmutablePureComponent {
 
@@ -11,7 +11,7 @@ export default class MediaItem extends ImmutablePureComponent {
   };
 
   state = {
-    visible: !this.props.media.getIn(['status', 'sensitive']) || displaySensitiveMedia,
+    visible: displayMedia !== 'hide_all' && !this.props.media.getIn(['status', 'sensitive']) || displayMedia === 'show_all',
   };
 
   handleClick = () => {
diff --git a/app/javascript/mastodon/features/notifications/components/column_settings.js b/app/javascript/mastodon/features/notifications/components/column_settings.js
index d9638aaf3..fcdf5c6e6 100644
--- a/app/javascript/mastodon/features/notifications/components/column_settings.js
+++ b/app/javascript/mastodon/features/notifications/components/column_settings.js
@@ -27,7 +27,6 @@ export default class ColumnSettings extends React.PureComponent {
 
     const showPushSettings = pushSettings.get('browserSupport') && pushSettings.get('isSubscribed');
     const pushStr = showPushSettings && <FormattedMessage id='notifications.column_settings.push' defaultMessage='Push notifications' />;
-    const pushMeta = showPushSettings && <FormattedMessage id='notifications.column_settings.push_meta' defaultMessage='This device' />;
 
     return (
       <div>
@@ -40,7 +39,7 @@ export default class ColumnSettings extends React.PureComponent {
 
           <div className='column-settings__row'>
             <SettingToggle prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'follow']} onChange={onChange} label={alertStr} />
-            {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'follow']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
+            {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'follow']} onChange={this.onPushChange} label={pushStr} />}
             <SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'follow']} onChange={onChange} label={showStr} />
             <SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'follow']} onChange={onChange} label={soundStr} />
           </div>
@@ -51,7 +50,7 @@ export default class ColumnSettings extends React.PureComponent {
 
           <div className='column-settings__row'>
             <SettingToggle prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'favourite']} onChange={onChange} label={alertStr} />
-            {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'favourite']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
+            {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'favourite']} onChange={this.onPushChange} label={pushStr} />}
             <SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'favourite']} onChange={onChange} label={showStr} />
             <SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'favourite']} onChange={onChange} label={soundStr} />
           </div>
@@ -62,7 +61,7 @@ export default class ColumnSettings extends React.PureComponent {
 
           <div className='column-settings__row'>
             <SettingToggle prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'mention']} onChange={onChange} label={alertStr} />
-            {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'mention']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
+            {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'mention']} onChange={this.onPushChange} label={pushStr} />}
             <SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'mention']} onChange={onChange} label={showStr} />
             <SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'mention']} onChange={onChange} label={soundStr} />
           </div>
@@ -73,7 +72,7 @@ export default class ColumnSettings extends React.PureComponent {
 
           <div className='column-settings__row'>
             <SettingToggle prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'reblog']} onChange={onChange} label={alertStr} />
-            {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'reblog']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
+            {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'reblog']} onChange={this.onPushChange} label={pushStr} />}
             <SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'reblog']} onChange={onChange} label={showStr} />
             <SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'reblog']} onChange={onChange} label={soundStr} />
           </div>
diff --git a/app/javascript/mastodon/features/notifications/components/notification.js b/app/javascript/mastodon/features/notifications/components/notification.js
index ed4a44ca6..8df6830c5 100644
--- a/app/javascript/mastodon/features/notifications/components/notification.js
+++ b/app/javascript/mastodon/features/notifications/components/notification.js
@@ -85,8 +85,9 @@ class Notification extends ImmutablePureComponent {
             <div className='notification__favourite-icon-wrapper'>
               <i className='fa fa-fw fa-user-plus' />
             </div>
-
-            <FormattedMessage id='notification.follow' defaultMessage='{name} followed you' values={{ name: link }} />
+            <span title={notification.get('created_at')}>
+              <FormattedMessage id='notification.follow' defaultMessage='{name} followed you' values={{ name: link }} />
+            </span>
           </div>
 
           <AccountContainer id={account.get('id')} withNote={false} hidden={this.props.hidden} />
diff --git a/app/javascript/mastodon/features/notifications/components/setting_toggle.js b/app/javascript/mastodon/features/notifications/components/setting_toggle.js
index ac2211e48..7aec16d2e 100644
--- a/app/javascript/mastodon/features/notifications/components/setting_toggle.js
+++ b/app/javascript/mastodon/features/notifications/components/setting_toggle.js
@@ -10,7 +10,6 @@ export default class SettingToggle extends React.PureComponent {
     settings: ImmutablePropTypes.map.isRequired,
     settingPath: PropTypes.array.isRequired,
     label: PropTypes.node.isRequired,
-    meta: PropTypes.node,
     onChange: PropTypes.func.isRequired,
   }
 
@@ -19,14 +18,13 @@ export default class SettingToggle extends React.PureComponent {
   }
 
   render () {
-    const { prefix, settings, settingPath, label, meta } = this.props;
+    const { prefix, settings, settingPath, label } = this.props;
     const id = ['setting-toggle', prefix, ...settingPath].filter(Boolean).join('-');
 
     return (
       <div className='setting-toggle'>
         <Toggle id={id} checked={settings.getIn(settingPath)} onChange={this.onChange} onKeyDown={this.onKeyDown} />
         <label htmlFor={id} className='setting-toggle__label'>{label}</label>
-        {meta && <span className='setting-meta__label'>{meta}</span>}
       </div>
     );
   }
diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js
index 2781c5d64..fb6f675f4 100644
--- a/app/javascript/mastodon/features/ui/index.js
+++ b/app/javascript/mastodon/features/ui/index.js
@@ -59,7 +59,8 @@ const messages = defineMessages({
 
 const mapStateToProps = state => ({
   isComposing: state.getIn(['compose', 'is_composing']),
-  hasComposingText: state.getIn(['compose', 'text']) !== '',
+  hasComposingText: state.getIn(['compose', 'text']).trim().length !== 0,
+  hasMediaAttachments: state.getIn(['compose', 'media_attachments']).size > 0,
   dropdownMenuIsOpen: state.getIn(['dropdown_menu', 'openId']) !== null,
 });
 
@@ -201,6 +202,7 @@ class UI extends React.PureComponent {
     children: PropTypes.node,
     isComposing: PropTypes.bool,
     hasComposingText: PropTypes.bool,
+    hasMediaAttachments: PropTypes.bool,
     location: PropTypes.object,
     intl: PropTypes.object.isRequired,
     dropdownMenuIsOpen: PropTypes.bool,
@@ -211,9 +213,9 @@ class UI extends React.PureComponent {
   };
 
   handleBeforeUnload = (e) => {
-    const { intl, isComposing, hasComposingText } = this.props;
+    const { intl, isComposing, hasComposingText, hasMediaAttachments } = this.props;
 
-    if (isComposing && hasComposingText) {
+    if (isComposing && (hasComposingText || hasMediaAttachments)) {
       // Setting returnValue to any string causes confirmation dialog.
       // Many browsers no longer display this text to users,
       // but we set user-friendly message for other browsers, e.g. Edge.
diff --git a/app/javascript/mastodon/features/video/index.js b/app/javascript/mastodon/features/video/index.js
index d17253957..67f7580b9 100644
--- a/app/javascript/mastodon/features/video/index.js
+++ b/app/javascript/mastodon/features/video/index.js
@@ -5,7 +5,7 @@ import { fromJS } from 'immutable';
 import { throttle } from 'lodash';
 import classNames from 'classnames';
 import { isFullscreen, requestFullscreen, exitFullscreen } from '../ui/util/fullscreen';
-import { displaySensitiveMedia } from '../../initial_state';
+import { displayMedia } from '../../initial_state';
 
 const messages = defineMessages({
   play: { id: 'video.play', defaultMessage: 'Play' },
@@ -111,7 +111,7 @@ class Video extends React.PureComponent {
     fullscreen: false,
     hovered: false,
     muted: false,
-    revealed: !this.props.sensitive || displaySensitiveMedia,
+    revealed: displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all',
   };
 
   setPlayerRef = c => {
@@ -272,7 +272,7 @@ class Video extends React.PureComponent {
   }
 
   render () {
-    const { preview, src, inline, startTime, onOpenVideo, onCloseVideo, intl, alt, detailed } = this.props;
+    const { preview, src, inline, startTime, onOpenVideo, onCloseVideo, intl, alt, detailed, sensitive } = this.props;
     const { containerWidth, currentTime, duration, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
     const progress = (currentTime / duration) * 100;
     const playerStyle = {};
@@ -296,6 +296,13 @@ class Video extends React.PureComponent {
       preload = 'none';
     }
 
+    let warning;
+    if (sensitive) {
+      warning = <FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' />;
+    } else {
+      warning = <FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' />;
+    }
+
     return (
       <div
         role='menuitem'
@@ -328,7 +335,7 @@ class Video extends React.PureComponent {
         />
 
         <button type='button' className={classNames('video-player__spoiler', { active: !revealed })} onClick={this.toggleReveal}>
-          <span className='video-player__spoiler__title'><FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' /></span>
+          <span className='video-player__spoiler__title'>{warning}</span>
           <span className='video-player__spoiler__subtitle'><FormattedMessage id='status.sensitive_toggle' defaultMessage='Click to view' /></span>
         </button>
 
diff --git a/app/javascript/mastodon/initial_state.js b/app/javascript/mastodon/initial_state.js
index 4c6922f9b..1d3d84f9d 100644
--- a/app/javascript/mastodon/initial_state.js
+++ b/app/javascript/mastodon/initial_state.js
@@ -5,7 +5,8 @@ const getMeta = (prop) => initialState && initialState.meta && initialState.meta
 
 export const reduceMotion = getMeta('reduce_motion');
 export const autoPlayGif = getMeta('auto_play_gif');
-export const displaySensitiveMedia = getMeta('display_sensitive_media');
+export const displayMedia = getMeta('display_media');
+export const expandSpoilers = getMeta('expand_spoilers');
 export const unfollowModal = getMeta('unfollow_modal');
 export const boostModal = getMeta('boost_modal');
 export const deleteModal = getMeta('delete_modal');
diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json
index 56d78ae64..a74207db6 100644
--- a/app/javascript/mastodon/locales/ar.json
+++ b/app/javascript/mastodon/locales/ar.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "متابعُون جُدُد :",
   "notifications.column_settings.mention": "الإشارات :",
   "notifications.column_settings.push": "الإخطارات المدفوعة",
-  "notifications.column_settings.push_meta": "هذا الجهاز",
   "notifications.column_settings.reblog": "الترقيّات:",
   "notifications.column_settings.show": "إعرِضها في عمود",
   "notifications.column_settings.sound": "أصدر صوتا",
diff --git a/app/javascript/mastodon/locales/ast.json b/app/javascript/mastodon/locales/ast.json
index e3cfabee3..147c5ad2a 100644
--- a/app/javascript/mastodon/locales/ast.json
+++ b/app/javascript/mastodon/locales/ast.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Siguidores nuevos:",
   "notifications.column_settings.mention": "Menciones:",
   "notifications.column_settings.push": "Push notifications",
-  "notifications.column_settings.push_meta": "Esti preséu",
   "notifications.column_settings.reblog": "Boosts:",
   "notifications.column_settings.show": "Amosar en columna",
   "notifications.column_settings.sound": "Reproducir soníu",
diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json
index 5730c1354..756c33393 100644
--- a/app/javascript/mastodon/locales/bg.json
+++ b/app/javascript/mastodon/locales/bg.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Нови последователи:",
   "notifications.column_settings.mention": "Споменавания:",
   "notifications.column_settings.push": "Push notifications",
-  "notifications.column_settings.push_meta": "This device",
   "notifications.column_settings.reblog": "Споделяния:",
   "notifications.column_settings.show": "Покажи в колона",
   "notifications.column_settings.sound": "Play sound",
diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json
index 0364d0bba..4701c9316 100644
--- a/app/javascript/mastodon/locales/ca.json
+++ b/app/javascript/mastodon/locales/ca.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Nous seguidors:",
   "notifications.column_settings.mention": "Mencions:",
   "notifications.column_settings.push": "Push notificacions",
-  "notifications.column_settings.push_meta": "Aquest dispositiu",
   "notifications.column_settings.reblog": "Impulsos:",
   "notifications.column_settings.show": "Mostrar en la columna",
   "notifications.column_settings.sound": "Reproduïr so",
diff --git a/app/javascript/mastodon/locales/co.json b/app/javascript/mastodon/locales/co.json
index 8bf083235..62976c98e 100644
--- a/app/javascript/mastodon/locales/co.json
+++ b/app/javascript/mastodon/locales/co.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Abbunati novi:",
   "notifications.column_settings.mention": "Minzione:",
   "notifications.column_settings.push": "Nutificazione Push",
-  "notifications.column_settings.push_meta": "Quess'apparechju",
   "notifications.column_settings.reblog": "Spartere:",
   "notifications.column_settings.show": "Mustrà indè a colonna",
   "notifications.column_settings.sound": "Sunà",
diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json
index 46aa954e8..2c34fd34c 100644
--- a/app/javascript/mastodon/locales/cs.json
+++ b/app/javascript/mastodon/locales/cs.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Noví sledovatelé:",
   "notifications.column_settings.mention": "Zmínky:",
   "notifications.column_settings.push": "Push oznámení",
-  "notifications.column_settings.push_meta": "Toto zařízení",
   "notifications.column_settings.reblog": "Boosty:",
   "notifications.column_settings.show": "Zobrazit ve sloupci",
   "notifications.column_settings.sound": "Přehrát zvuk",
diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json
index a2eea600a..90f3f8e30 100644
--- a/app/javascript/mastodon/locales/cy.json
+++ b/app/javascript/mastodon/locales/cy.json
@@ -1,337 +1,335 @@
 {
-  "account.badges.bot": "Bot",
-  "account.block": "Blociwch @{name}",
-  "account.block_domain": "Cuddiwch bopeth rhag {domain}",
-  "account.blocked": "Blociwyd",
-  "account.direct": "Neges breifat @{name}",
-  "account.disclaimer_full": "Gall y wybodaeth isod adlewyrchu darlun anghyflawn o broffil defnyddiwr.",
-  "account.domain_blocked": "Domain hidden",
-  "account.edit_profile": "Golygu proffil",
-  "account.endorse": "Feature on profile",
-  "account.follow": "Dilyn",
-  "account.followers": "Dilynwyr",
-  "account.followers.empty": "Nid oes neb yn dilyn y defnyddiwr hwn eto.",
-  "account.follows": "Yn dilyn",
-  "account.follows.empty": "Nid yw'r defnyddiwr hwn yn dilyn unrhyw un eto.",
-  "account.follows_you": "Yn eich dilyn chi",
-  "account.hide_reblogs": "Hide boosts from @{name}",
-  "account.link_verified_on": "Ownership of this link was checked on {date}",
-  "account.media": "Cyfryngau",
-  "account.mention": "Crybwyll @{name}",
-  "account.moved_to": "Mae @{name} wedi symud i:",
-  "account.mute": "Mute @{name}",
-  "account.mute_notifications": "Mute notifications from @{name}",
-  "account.muted": "Distewyd",
-  "account.posts": "Tŵtiau",
-  "account.posts_with_replies": "Toots and replies",
-  "account.report": "Adroddwch @{name}",
-  "account.requested": "Awaiting approval. Click to cancel follow request",
-  "account.share": "Rhannwch broffil @{name}",
-  "account.show_reblogs": "Show boosts from @{name}",
-  "account.unblock": "Dadflociwch @{name}",
-  "account.unblock_domain": "Dadguddiwch {domain}",
-  "account.unendorse": "Don't feature on profile",
-  "account.unfollow": "Daddilynwch",
-  "account.unmute": "Unmute @{name}",
-  "account.unmute_notifications": "Unmute notifications from @{name}",
-  "account.view_full_profile": "View full profile",
-  "alert.unexpected.message": "Digwyddodd gwall annisgwyl.",
-  "alert.unexpected.title": "Wps!",
-  "boost_modal.combo": "You can press {combo} to skip this next time",
-  "bundle_column_error.body": "Aeth rhywbeth o'i le tra'n llwytho'r elfen hon.",
-  "bundle_column_error.retry": "Ceisiwch eto",
-  "bundle_column_error.title": "Gwall rhwydwaith",
-  "bundle_modal_error.close": "Close",
-  "bundle_modal_error.message": "Aeth rhywbeth o'i le tra'n llwytho'r elfen hon.",
-  "bundle_modal_error.retry": "Ceiswich eto",
-  "column.blocks": "Defnyddwyr a flociwyd",
-  "column.community": "Llinell amser lleol",
-  "column.direct": "Negeseuon preifat",
-  "column.domain_blocks": "Parthau cuddiedig",
-  "column.favourites": "Ffefrynnau",
-  "column.follow_requests": "Ceisiadau dilyn",
-  "column.home": "Hafan",
-  "column.lists": "Rhestrau",
-  "column.mutes": "Defnyddwyr a ddistewyd",
-  "column.notifications": "Hysbysiadau",
-  "column.pins": "Pinned toot",
-  "column.public": "Federated timeline",
-  "column_back_button.label": "Nôl",
-  "column_header.hide_settings": "Hide settings",
-  "column_header.moveLeft_settings": "Symudwch y golofn i'r chwith",
-  "column_header.moveRight_settings": "Symudwch y golofn i'r dde",
-  "column_header.pin": "Piniwch",
-  "column_header.show_settings": "Dangos gosodiadau",
-  "column_header.unpin": "Dadbiniwch",
-  "column_subheading.settings": "Gosodiadau",
-  "community.column_settings.media_only": "Cyfryngau yn unig",
-  "compose_form.direct_message_warning": "Mi fydd y tŵt hwn ond yn cael ei anfon at y defnyddwyr sy'n cael eu crybwyll.",
-  "compose_form.direct_message_warning_learn_more": "Dysgwch fwy",
-  "compose_form.hashtag_warning": "Ni fydd y tŵt hwn wedi ei restru o dan unrhyw hashnod gan ei fod heb ei restru. Dim ond tŵtiau cyhoeddus gellid chwilota amdanynt drwy hashnod.",
-  "compose_form.lock_disclaimer": "Nid yw eich cyfri wedi'i {locked}. Gall unrhyw un eich dilyn i weld eich POSTS dilynwyr-yn-unig.",
-  "compose_form.lock_disclaimer.lock": "wedi ei gloi",
-  "compose_form.placeholder": "Be syd ar eich meddwl?",
-  "compose_form.publish": "Tŵt",
-  "compose_form.publish_loud": "{publish}!",
-  "compose_form.sensitive.marked": "Media is marked as sensitive",
-  "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
-  "compose_form.spoiler.marked": "Testun wedi ei guddio gan rybudd",
-  "compose_form.spoiler.unmarked": "Nid yw'r testun wedi ei guddio",
-  "compose_form.spoiler_placeholder": "Ysgrifenwch eich rhybudd yma",
-  "confirmation_modal.cancel": "Cancel",
-  "confirmations.block.confirm": "Blociwch",
-  "confirmations.block.message": "Ydych chi'n sicr eich bod eisiau blocio {name}?",
-  "confirmations.delete.confirm": "Dileu",
-  "confirmations.delete.message": "Ydych chi'n sicr eich bod eisiau dileu y statws hwn?",
-  "confirmations.delete_list.confirm": "Dileu",
-  "confirmations.delete_list.message": "Ydych chi'n sicr eich bod eisiau dileu y rhestr hwn am byth?",
-  "confirmations.domain_block.confirm": "Hide entire domain",
-  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.",
-  "confirmations.mute.confirm": "Mute",
-  "confirmations.mute.message": "Ydych chi'n sicr eich bod am ddistewi {name}?",
-  "confirmations.redraft.confirm": "Delete & redraft",
-  "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.",
-  "confirmations.unfollow.confirm": "Dad-ddilynwch",
-  "confirmations.unfollow.message": "Ydych chi'n sicr eich bod am ddad-ddilyn {name}?",
-  "embed.instructions": "Embed this status on your website by copying the code below.",
-  "embed.preview": "Dyma sut olwg fydd arno:",
-  "emoji_button.activity": "Gweithgarwch",
-  "emoji_button.custom": "Custom",
-  "emoji_button.flags": "Baneri",
-  "emoji_button.food": "Bwyd a Diod",
-  "emoji_button.label": "Mewnosodwch emoji",
-  "emoji_button.nature": "Natur",
-  "emoji_button.not_found": "Dim emojos!! (╯°□°)╯︵ ┻━┻",
-  "emoji_button.objects": "Gwrthrychau",
-  "emoji_button.people": "Pobl",
-  "emoji_button.recent": "Defnyddir yn aml",
-  "emoji_button.search": "Chwilio...",
-  "emoji_button.search_results": "Canlyniadau chwilio",
-  "emoji_button.symbols": "Symbolau",
-  "emoji_button.travel": "Teithio & Llefydd",
-  "empty_column.blocks": "Nid ydych wedi blocio unrhyw ddefnyddwyr eto.",
-  "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
-  "empty_column.direct": "Nid oes gennych unrhyw negeseuon preifat eto. Pan y byddwch yn anfon neu derbyn un, mi fydd yn ymddangos yma.",
-  "empty_column.domain_blocks": "Nid oes yna unrhyw barthau cuddiedig eto.",
-  "empty_column.favourited_statuses": "Nid oes gennych unrhyw hoff dwtiau eto. Pan y byddwch yn hoffi un, mi fydd yn ymddangos yma.",
-  "empty_column.favourites": "Nid oes neb wedi hoffi'r tŵt yma eto. Pan bydd rhywun yn ei hoffi, mi fyddent yn ymddangos yma.",
-  "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
-  "empty_column.hashtag": "Nid oes dim ar yr hashnod hwn eto.",
-  "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
-  "empty_column.home.public_timeline": "the public timeline",
-  "empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.",
-  "empty_column.lists": "You don't have any lists yet. When you create one, it will show up here.",
-  "empty_column.mutes": "You haven't muted any users yet.",
-  "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
-  "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up",
-  "follow_request.authorize": "Caniatau",
-  "follow_request.reject": "Gwrthod",
-  "getting_started.developers": "Datblygwyr",
-  "getting_started.documentation": "Dogfennaeth",
-  "getting_started.find_friends": "Canfod ffrindiau o Twitter",
-  "getting_started.heading": "Dechrau",
-  "getting_started.invite": "Gwahoddwch bobl",
-  "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}.",
-  "getting_started.security": "Diogelwch",
-  "getting_started.terms": "Terms of service",
-  "home.column_settings.basic": "Syml",
-  "home.column_settings.show_reblogs": "Show boosts",
-  "home.column_settings.show_replies": "Dangoswch ymatebion",
-  "keyboard_shortcuts.back": "to navigate back",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
-  "keyboard_shortcuts.boost": "to boost",
-  "keyboard_shortcuts.column": "to focus a status in one of the columns",
-  "keyboard_shortcuts.compose": "to focus the compose textarea",
-  "keyboard_shortcuts.description": "Disgrifiad",
-  "keyboard_shortcuts.direct": "i agor colofn negeseuon preifat",
-  "keyboard_shortcuts.down": "i symud lawr yn y rhestr",
-  "keyboard_shortcuts.enter": "i agor statws",
-  "keyboard_shortcuts.favourite": "i hoffi",
-  "keyboard_shortcuts.favourites": "i agor rhestr hoffi",
-  "keyboard_shortcuts.federated": "to open federated timeline",
-  "keyboard_shortcuts.heading": "Keyboard Shortcuts",
-  "keyboard_shortcuts.home": "to open home timeline",
-  "keyboard_shortcuts.hotkey": "Hotkey",
-  "keyboard_shortcuts.legend": "to display this legend",
-  "keyboard_shortcuts.local": "to open local timeline",
-  "keyboard_shortcuts.mention": "i grybwyll yr awdur",
-  "keyboard_shortcuts.muted": "to open muted users list",
-  "keyboard_shortcuts.my_profile": "i agor eich proffil",
-  "keyboard_shortcuts.notifications": "i agor colofn hysbysiadau",
-  "keyboard_shortcuts.pinned": "to open pinned toots list",
-  "keyboard_shortcuts.profile": "i agor proffil yr awdur",
-  "keyboard_shortcuts.reply": "i ateb",
-  "keyboard_shortcuts.requests": "to open follow requests list",
-  "keyboard_shortcuts.search": "to focus search",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
-  "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW",
-  "keyboard_shortcuts.toot": "i ddechrau tŵt newydd sbon",
-  "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
-  "keyboard_shortcuts.up": "to move up in the list",
-  "lightbox.close": "Cau",
-  "lightbox.next": "Nesaf",
-  "lightbox.previous": "Previous",
-  "lists.account.add": "Ychwanegwch at restr",
-  "lists.account.remove": "Remove from list",
-  "lists.delete": "Dileu rhestr",
-  "lists.edit": "Golygwch restr",
-  "lists.new.create": "Ychwanegwch restr",
-  "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among people you follow",
-  "lists.subheading": "Eich rhestrau",
-  "loading_indicator.label": "Llwytho...",
-  "media_gallery.toggle_visible": "Toggle visibility",
-  "missing_indicator.label": "Heb ei ganfod",
-  "missing_indicator.sublabel": "Ni ellid canfod yr adnodd hwn",
-  "mute_modal.hide_notifications": "Cuddiwch hysbysiadau rhag y defnyddiwr hwn?",
-  "navigation_bar.apps": "Apiau symudol",
-  "navigation_bar.blocks": "Defnyddwyr wedi eu blocio",
-  "navigation_bar.community_timeline": "Local timeline",
-  "navigation_bar.compose": "Cyfansoddwch dŵt newydd",
-  "navigation_bar.direct": "Negeseuon preifat",
-  "navigation_bar.discover": "Darganfyddwch",
-  "navigation_bar.domain_blocks": "Parthau cuddiedig",
-  "navigation_bar.edit_profile": "Golygu proffil",
-  "navigation_bar.favourites": "Ffefrynnau",
-  "navigation_bar.filters": "Muted words",
-  "navigation_bar.follow_requests": "Ceisiadau dilyn",
-  "navigation_bar.info": "About this instance",
-  "navigation_bar.keyboard_shortcuts": "Hotkeys",
-  "navigation_bar.lists": "Rhestrau",
-  "navigation_bar.logout": "Allgofnodi",
-  "navigation_bar.mutes": "Muted users",
-  "navigation_bar.personal": "Personol",
-  "navigation_bar.pins": "Tŵtiau wedi eu pinio",
-  "navigation_bar.preferences": "Dewisiadau",
-  "navigation_bar.public_timeline": "Federated timeline",
-  "navigation_bar.security": "Diogelwch",
-  "notification.favourite": "hoffodd {name} eich statws",
-  "notification.follow": "dilynodd {name} chi",
-  "notification.mention": "{name} mentioned you",
-  "notification.reblog": "{name} boosted your status",
-  "notifications.clear": "Clirio hysbysiadau",
-  "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
-  "notifications.column_settings.alert": "Desktop notifications",
-  "notifications.column_settings.favourite": "Ffefrynnau:",
-  "notifications.column_settings.follow": "Dilynwyr newydd:",
-  "notifications.column_settings.mention": "Mentions:",
-  "notifications.column_settings.push": "Push notifications",
-  "notifications.column_settings.push_meta": "This device",
-  "notifications.column_settings.reblog": "Boosts:",
-  "notifications.column_settings.show": "Show in column",
-  "notifications.column_settings.sound": "Chwarae sain",
-  "notifications.group": "{count} o hysbysiadau",
-  "onboarding.done": "Done",
-  "onboarding.next": "Nesaf",
-  "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
-  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
-  "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
-  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
-  "onboarding.page_one.full_handle": "Your full handle",
-  "onboarding.page_one.handle_hint": "This is what you would tell your friends to search for.",
-  "onboarding.page_one.welcome": "Croeso i Mastodon!",
-  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
-  "onboarding.page_six.almost_done": "Almost done...",
-  "onboarding.page_six.appetoot": "Bon Apetŵt!",
-  "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
-  "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
-  "onboarding.page_six.guidelines": "canllawiau cymunedol",
-  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
-  "onboarding.page_six.various_app": "apiau symudol",
-  "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
-  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
-  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
-  "onboarding.skip": "Skip",
-  "privacy.change": "Adjust status privacy",
-  "privacy.direct.long": "Post to mentioned users only",
-  "privacy.direct.short": "Direct",
-  "privacy.private.long": "Post to followers only",
-  "privacy.private.short": "Dilynwyr-yn-unig",
-  "privacy.public.long": "Post to public timelines",
-  "privacy.public.short": "Cyhoeddus",
-  "privacy.unlisted.long": "Do not show in public timelines",
-  "privacy.unlisted.short": "Heb ei restru",
-  "regeneration_indicator.label": "Llwytho…",
-  "regeneration_indicator.sublabel": "Mae eich ffrwd cartref yn cael ei baratoi!",
-  "relative_time.days": "{number}d",
-  "relative_time.hours": "{number}h",
-  "relative_time.just_now": "nawr",
-  "relative_time.minutes": "{number}m",
-  "relative_time.seconds": "{number}s",
-  "reply_indicator.cancel": "Canslo",
-  "report.forward": "Forward to {target}",
-  "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
-  "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
-  "report.placeholder": "Sylwadau ychwanegol",
-  "report.submit": "Submit",
-  "report.target": "Report {target}",
-  "search.placeholder": "Chwilio",
-  "search_popout.search_format": "Advanced search format",
-  "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
-  "search_popout.tips.hashtag": "hashnod",
-  "search_popout.tips.status": "statws",
-  "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
-  "search_popout.tips.user": "defnyddiwr",
-  "search_results.accounts": "Pobl",
-  "search_results.hashtags": "Hanshnodau",
-  "search_results.statuses": "Twtiau",
-  "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
-  "standalone.public_title": "A look inside...",
-  "status.block": "Block @{name}",
-  "status.cancel_reblog_private": "Unboost",
-  "status.cannot_reblog": "This post cannot be boosted",
-  "status.delete": "Dileu",
-  "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Neges breifat @{name}",
-  "status.embed": "Plannu",
-  "status.favourite": "Favourite",
-  "status.filtered": "Filtered",
-  "status.load_more": "Llwythwch mwy",
-  "status.media_hidden": "Media hidden",
-  "status.mention": "Mention @{name}",
-  "status.more": "Mwy",
-  "status.mute": "Mute @{name}",
-  "status.mute_conversation": "Mute conversation",
-  "status.open": "Expand this status",
-  "status.pin": "Pin on profile",
-  "status.pinned": "Pinned toot",
-  "status.reblog": "Boost",
-  "status.reblog_private": "Boost to original audience",
-  "status.reblogged_by": "{name} boosted",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
-  "status.redraft": "Delete & re-draft",
-  "status.reply": "Ateb",
-  "status.replyAll": "Ateb i edefyn",
-  "status.report": "Report @{name}",
-  "status.sensitive_toggle": "Click to view",
-  "status.sensitive_warning": "Cynnwys sensitif",
-  "status.share": "Rhannwch",
-  "status.show_less": "Dangoswch lai",
-  "status.show_less_all": "Dangoswch lai i bawb",
-  "status.show_more": "Dangoswch fwy",
-  "status.show_more_all": "Show more for all",
-  "status.unmute_conversation": "Unmute conversation",
-  "status.unpin": "Unpin from profile",
-  "tabs_bar.federated_timeline": "Federated",
-  "tabs_bar.home": "Hafan",
-  "tabs_bar.local_timeline": "Lleol",
-  "tabs_bar.notifications": "Hysbysiadau",
-  "tabs_bar.search": "Chwilio",
-  "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking",
-  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
-  "upload_area.title": "Drag & drop to upload",
-  "upload_button.label": "Add media (JPEG, PNG, GIF, WebM, MP4, MOV)",
-  "upload_form.description": "Describe for the visually impaired",
-  "upload_form.focus": "Crop",
-  "upload_form.undo": "Delete",
-  "upload_progress.label": "Uwchlwytho...",
-  "video.close": "Close video",
-  "video.exit_fullscreen": "Exit full screen",
-  "video.expand": "Expand video",
-  "video.fullscreen": "Full screen",
-  "video.hide": "Hide video",
-  "video.mute": "Mute sound",
-  "video.pause": "Pause",
-  "video.play": "Chwarae",
-  "video.unmute": "Unmute sound"
+    "account.badges.bot": "Bot",
+    "account.block": "Blociwch @{name}",
+    "account.block_domain": "Cuddiwch bopeth rhag {domain}",
+    "account.blocked": "Blociwyd",
+    "account.direct": "Neges breifat @{name}",
+    "account.disclaimer_full": "Gall y wybodaeth isod adlewyrchu darlun anghyflawn o broffil defnyddiwr.",
+    "account.domain_blocked": "Parth wedi ei guddio",
+    "account.edit_profile": "Golygu proffil",
+    "account.endorse": "Arddangos ar fy mhroffil",
+    "account.follow": "Dilyn",
+    "account.followers": "Dilynwyr",
+    "account.followers.empty": "Nid oes neb yn dilyn y defnyddiwr hwn eto.",
+    "account.follows": "Yn dilyn",
+    "account.follows.empty": "Nid yw'r defnyddiwr hwn yn dilyn unrhyw un eto.",
+    "account.follows_you": "Yn eich dilyn chi",
+    "account.hide_reblogs": "Cuddio bwstiau o @{name}",
+    "account.media": "Cyfryngau",
+    "account.mention": "Crybwyll @{name}",
+    "account.moved_to": "Mae @{name} wedi symud i:",
+    "account.mute": "Tawelu @{name}",
+    "account.mute_notifications": "Cuddio hysbysiadau o @{name}",
+    "account.muted": "Distewyd",
+    "account.posts": "Tŵtiau",
+    "account.posts_with_replies": "Tŵtiau ac atebion",
+    "account.report": "Adroddwch @{name}",
+    "account.requested": "Aros am gymeradwyaeth. Cliciwch er mwyn canslo cais dilyn",
+    "account.share": "Rhannwch broffil @{name}",
+    "account.show_reblogs": "Dangoswch bwstiau o @{name}",
+    "account.unblock": "Dadflociwch @{name}",
+    "account.unblock_domain": "Dadguddiwch {domain}",
+    "account.unendorse": "Peidwch a'i arddangos ar fy mhroffil",
+    "account.unfollow": "Daddilynwch",
+    "account.unmute": "Dad-dawelu @{name}",
+    "account.unmute_notifications": "Dad-dawelu hysbysiadau o @{name}",
+    "account.view_full_profile": "Gweld proffil llawn",
+    "alert.unexpected.message": "Digwyddodd gwall annisgwyl.",
+    "alert.unexpected.title": "Wps!",
+    "boost_modal.combo": "Mae modd gwasgu {combo} er mwyn sgipio hyn tro nesa",
+    "bundle_column_error.body": "Aeth rhywbeth o'i le tra'n llwytho'r elfen hon.",
+    "bundle_column_error.retry": "Ceisiwch eto",
+    "bundle_column_error.title": "Gwall rhwydwaith",
+    "bundle_modal_error.close": "Cau",
+    "bundle_modal_error.message": "Aeth rhywbeth o'i le tra'n llwytho'r elfen hon.",
+    "bundle_modal_error.retry": "Ceiswich eto",
+    "column.blocks": "Defnyddwyr a flociwyd",
+    "column.community": "Llinell amser lleol",
+    "column.direct": "Negeseuon preifat",
+    "column.domain_blocks": "Parthau cuddiedig",
+    "column.favourites": "Ffefrynnau",
+    "column.follow_requests": "Ceisiadau dilyn",
+    "column.home": "Hafan",
+    "column.lists": "Rhestrau",
+    "column.mutes": "Defnyddwyr a ddistewyd",
+    "column.notifications": "Hysbysiadau",
+    "column.pins": "Tŵtiau wedi eu pinio",
+    "column.public": "",
+    "column_back_button.label": "Nôl",
+    "column_header.hide_settings": "Cuddiwch dewisiadau",
+    "column_header.moveLeft_settings": "Symudwch y golofn i'r chwith",
+    "column_header.moveRight_settings": "Symudwch y golofn i'r dde",
+    "column_header.pin": "Piniwch",
+    "column_header.show_settings": "Dangos gosodiadau",
+    "column_header.unpin": "Dadbiniwch",
+    "column_subheading.settings": "Gosodiadau",
+    "community.column_settings.media_only": "Cyfryngau yn unig",
+    "compose_form.direct_message_warning": "Mi fydd y tŵt hwn ond yn cael ei anfon at y defnyddwyr sy'n cael eu crybwyll.",
+    "compose_form.direct_message_warning_learn_more": "Dysgwch fwy",
+    "compose_form.hashtag_warning": "Ni fydd y tŵt hwn wedi ei restru o dan unrhyw hashnod gan ei fod heb ei restru. Dim ond tŵtiau cyhoeddus gellid chwilota amdanynt drwy hashnod.",
+    "compose_form.lock_disclaimer": "Nid yw eich cyfri wedi'i {locked}. Gall unrhyw un eich dilyn i weld eich POSTS dilynwyr-yn-unig.",
+    "compose_form.lock_disclaimer.lock": "wedi ei gloi",
+    "compose_form.placeholder": "Be syd ar eich meddwl?",
+    "compose_form.publish": "Tŵt",
+    "compose_form.publish_loud": "{publish}!",
+    "compose_form.sensitive.marked": "",
+    "compose_form.sensitive.unmarked": "",
+    "compose_form.spoiler.marked": "Testun wedi ei guddio gan rybudd",
+    "compose_form.spoiler.unmarked": "Nid yw'r testun wedi ei guddio",
+    "compose_form.spoiler_placeholder": "Ysgrifenwch eich rhybudd yma",
+    "confirmation_modal.cancel": "Canslo",
+    "confirmations.block.confirm": "Blociwch",
+    "confirmations.block.message": "Ydych chi'n sicr eich bod eisiau blocio {name}?",
+    "confirmations.delete.confirm": "Dileu",
+    "confirmations.delete.message": "Ydych chi'n sicr eich bod eisiau dileu y statws hwn?",
+    "confirmations.delete_list.confirm": "Dileu",
+    "confirmations.delete_list.message": "Ydych chi'n sicr eich bod eisiau dileu y rhestr hwn am byth?",
+    "confirmations.domain_block.confirm": "",
+    "confirmations.domain_block.message": "",
+    "confirmations.mute.confirm": "Tawelu",
+    "confirmations.mute.message": "Ydych chi'n sicr eich bod am ddistewi {name}?",
+    "confirmations.redraft.confirm": "Dilëwch & ailddrafftio",
+    "confirmations.redraft.message": "Ydych chi'n siwr eich bod eisiau dileu y statws hwn a'i ailddrafftio? Bydd ffefrynnau a bwstiau'n cael ei colli, a bydd ymatebion i'r statws gwreiddiol yn cael eu hamddifadu.",
+    "confirmations.unfollow.confirm": "Dad-ddilynwch",
+    "confirmations.unfollow.message": "Ydych chi'n sicr eich bod am ddad-ddilyn {name}?",
+    "embed.instructions": "Mewnblannwch y statws hwn ar eich gwefan drwy gopïo'r côd isod.",
+    "embed.preview": "Dyma sut olwg fydd arno:",
+    "emoji_button.activity": "Gweithgarwch",
+    "emoji_button.custom": "",
+    "emoji_button.flags": "Baneri",
+    "emoji_button.food": "Bwyd a Diod",
+    "emoji_button.label": "Mewnosodwch emoji",
+    "emoji_button.nature": "Natur",
+    "emoji_button.not_found": "Dim emojos!! (╯°□°)╯︵ ┻━┻",
+    "emoji_button.objects": "Gwrthrychau",
+    "emoji_button.people": "Pobl",
+    "emoji_button.recent": "Defnyddir yn aml",
+    "emoji_button.search": "Chwilio...",
+    "emoji_button.search_results": "Canlyniadau chwilio",
+    "emoji_button.symbols": "Symbolau",
+    "emoji_button.travel": "Teithio & Llefydd",
+    "empty_column.blocks": "Nid ydych wedi blocio unrhyw ddefnyddwyr eto.",
+    "empty_column.community": "",
+    "empty_column.direct": "Nid oes gennych unrhyw negeseuon preifat eto. Pan y byddwch yn anfon neu derbyn un, mi fydd yn ymddangos yma.",
+    "empty_column.domain_blocks": "Nid oes yna unrhyw barthau cuddiedig eto.",
+    "empty_column.favourited_statuses": "Nid oes gennych unrhyw hoff dwtiau eto. Pan y byddwch yn hoffi un, mi fydd yn ymddangos yma.",
+    "empty_column.favourites": "Nid oes neb wedi hoffi'r tŵt yma eto. Pan bydd rhywun yn ei hoffi, mi fyddent yn ymddangos yma.",
+    "empty_column.follow_requests": "Nid oes gennych unrhyw geisiadau dilyn eto. Pan dderbyniwch chi un, bydd yn ymddangos yma.",
+    "empty_column.hashtag": "Nid oes dim ar yr hashnod hwn eto.",
+    "empty_column.home": "",
+    "empty_column.home.public_timeline": "y ffrwd cyhoeddus",
+    "empty_column.list": "Nid oes dim yn y rhestr yma eto. Pan y bydd aelodau'r rhestr yn cyhoeddi statws newydd, mi fydd yn ymddangos yma.",
+    "empty_column.lists": "Nid oes gennych unrhyw restrau eto. Pan grëwch chi un, mi fydd yn ymddangos yma.",
+    "empty_column.mutes": "Nid ydych wedi tawelu unrhyw ddefnyddwyr eto.",
+    "empty_column.notifications": "Nid oes gennych unrhyw hysbysiadau eto. Rhyngweithiwch ac eraill i ddechrau'r sgwrs.",
+    "empty_column.public": "Does dim byd yma! Ysgrifennwch rhywbeth yn gyhoeddus, neu dilynwch ddefnyddwyr o INSTANCES eraill i'w lenwi",
+    "follow_request.authorize": "Caniatau",
+    "follow_request.reject": "Gwrthod",
+    "getting_started.developers": "Datblygwyr",
+    "getting_started.documentation": "Dogfennaeth",
+    "getting_started.find_friends": "Canfod ffrindiau o Twitter",
+    "getting_started.heading": "Dechrau",
+    "getting_started.invite": "Gwahoddwch bobl",
+    "getting_started.open_source_notice": "Mae Mastodon yn feddalwedd côd agored. Mae modd cyfrannu neu adrodd materion ar GitHUb ar {github}.",
+    "getting_started.security": "Diogelwch",
+    "getting_started.terms": "Telerau Gwasanaeth",
+    "home.column_settings.basic": "Syml",
+    "home.column_settings.show_reblogs": "",
+    "home.column_settings.show_replies": "Dangoswch ymatebion",
+    "keyboard_shortcuts.back": "",
+    "keyboard_shortcuts.blocked": "i agor rhestr defnyddwyr a flociwyd",
+    "keyboard_shortcuts.boost": "",
+    "keyboard_shortcuts.column": "",
+    "keyboard_shortcuts.compose": "",
+    "keyboard_shortcuts.description": "Disgrifiad",
+    "keyboard_shortcuts.direct": "i agor colofn negeseuon preifat",
+    "keyboard_shortcuts.down": "i symud lawr yn y rhestr",
+    "keyboard_shortcuts.enter": "i agor statws",
+    "keyboard_shortcuts.favourite": "i hoffi",
+    "keyboard_shortcuts.favourites": "i agor rhestr hoffi",
+    "keyboard_shortcuts.federated": "",
+    "keyboard_shortcuts.heading": "",
+    "keyboard_shortcuts.home": "i agor ffrwd cartref",
+    "keyboard_shortcuts.hotkey": "Hotkey",
+    "keyboard_shortcuts.legend": "",
+    "keyboard_shortcuts.local": "i agor ffrwd lleol",
+    "keyboard_shortcuts.mention": "i grybwyll yr awdur",
+    "keyboard_shortcuts.muted": "i agor rhestr defnyddwyr a dawelwyd",
+    "keyboard_shortcuts.my_profile": "i agor eich proffil",
+    "keyboard_shortcuts.notifications": "i agor colofn hysbysiadau",
+    "keyboard_shortcuts.pinned": "",
+    "keyboard_shortcuts.profile": "i agor proffil yr awdur",
+    "keyboard_shortcuts.reply": "i ateb",
+    "keyboard_shortcuts.requests": "i agor rhestr ceisiadau dilyn",
+    "keyboard_shortcuts.search": "",
+    "keyboard_shortcuts.start": "",
+    "keyboard_shortcuts.toggle_hidden": "",
+    "keyboard_shortcuts.toot": "i ddechrau tŵt newydd sbon",
+    "keyboard_shortcuts.unfocus": "",
+    "keyboard_shortcuts.up": "i symud yn uwch yn y rhestr",
+    "lightbox.close": "Cau",
+    "lightbox.next": "Nesaf",
+    "lightbox.previous": "",
+    "lists.account.add": "Ychwanegwch at restr",
+    "lists.account.remove": "",
+    "lists.delete": "Dileu rhestr",
+    "lists.edit": "Golygwch restr",
+    "lists.new.create": "Ychwanegwch restr",
+    "lists.new.title_placeholder": "Teitl rhestr newydd",
+    "lists.search": "",
+    "lists.subheading": "Eich rhestrau",
+    "loading_indicator.label": "Llwytho...",
+    "media_gallery.toggle_visible": "",
+    "missing_indicator.label": "Heb ei ganfod",
+    "missing_indicator.sublabel": "Ni ellid canfod yr adnodd hwn",
+    "mute_modal.hide_notifications": "Cuddiwch hysbysiadau rhag y defnyddiwr hwn?",
+    "navigation_bar.apps": "Apiau symudol",
+    "navigation_bar.blocks": "Defnyddwyr wedi eu blocio",
+    "navigation_bar.community_timeline": "",
+    "navigation_bar.compose": "Cyfansoddwch dŵt newydd",
+    "navigation_bar.direct": "Negeseuon preifat",
+    "navigation_bar.discover": "Darganfyddwch",
+    "navigation_bar.domain_blocks": "Parthau cuddiedig",
+    "navigation_bar.edit_profile": "Golygu proffil",
+    "navigation_bar.favourites": "Ffefrynnau",
+    "navigation_bar.filters": "Geiriau a dawelwyd",
+    "navigation_bar.follow_requests": "Ceisiadau dilyn",
+    "navigation_bar.info": "",
+    "navigation_bar.keyboard_shortcuts": "",
+    "navigation_bar.lists": "Rhestrau",
+    "navigation_bar.logout": "Allgofnodi",
+    "navigation_bar.mutes": "Defnyddwyr a dawelwyd",
+    "navigation_bar.personal": "Personol",
+    "navigation_bar.pins": "Tŵtiau wedi eu pinio",
+    "navigation_bar.preferences": "Dewisiadau",
+    "navigation_bar.public_timeline": "",
+    "navigation_bar.security": "Diogelwch",
+    "notification.favourite": "hoffodd {name} eich statws",
+    "notification.follow": "dilynodd {name} chi",
+    "notification.mention": "Soniodd {name} amdanoch chi",
+    "notification.reblog": "",
+    "notifications.clear": "Clirio hysbysiadau",
+    "notifications.clear_confirmation": "",
+    "notifications.column_settings.alert": "",
+    "notifications.column_settings.favourite": "Ffefrynnau:",
+    "notifications.column_settings.follow": "Dilynwyr newydd:",
+    "notifications.column_settings.mention": "",
+    "notifications.column_settings.push": "Hysbysiadau push",
+    "notifications.column_settings.reblog": "",
+    "notifications.column_settings.show": "",
+    "notifications.column_settings.sound": "Chwarae sain",
+    "notifications.group": "{count} o hysbysiadau",
+    "onboarding.done": "Wedi'i wneud",
+    "onboarding.next": "Nesaf",
+    "onboarding.page_five.public_timelines": "",
+    "onboarding.page_four.home": "Mae'r ffrwd gartref yn dangos twtiau o bobl yr ydych yn dilyn.",
+    "onboarding.page_four.notifications": "",
+    "onboarding.page_one.federation": "",
+    "onboarding.page_one.full_handle": "",
+    "onboarding.page_one.handle_hint": "",
+    "onboarding.page_one.welcome": "Croeso i Mastodon!",
+    "onboarding.page_six.admin": "",
+    "onboarding.page_six.almost_done": "Bron a gorffen...",
+    "onboarding.page_six.appetoot": "Bon Apetŵt!",
+    "onboarding.page_six.apps_available": "Mae yna {apps} ar gael i iOS, Android a platfformau eraill.",
+    "onboarding.page_six.github": "Mae Mastodon yn feddalwedd côd agored rhad ac am ddim. Mae modd adrodd bygiau, gwneud ceisiadau am nodweddion penodol, neu gyfrannu i'r côd ar {github}.",
+    "onboarding.page_six.guidelines": "canllawiau cymunedol",
+    "onboarding.page_six.read_guidelines": "Darllenwch {guidelines} y {domain} os gwelwch yn dda!",
+    "onboarding.page_six.various_app": "apiau symudol",
+    "onboarding.page_three.profile": "",
+    "onboarding.page_three.search": "",
+    "onboarding.page_two.compose": "",
+    "onboarding.skip": "Sgipiwch",
+    "privacy.change": "",
+    "privacy.direct.long": "",
+    "privacy.direct.short": "Uniongyrchol",
+    "privacy.private.long": "Cyhoeddi i ddilynwyr yn unig",
+    "privacy.private.short": "Dilynwyr-yn-unig",
+    "privacy.public.long": "Cyhoeddi i ffrydiau cyhoeddus",
+    "privacy.public.short": "Cyhoeddus",
+    "privacy.unlisted.long": "Peidio a cyhoeddi i ffrydiau cyhoeddus",
+    "privacy.unlisted.short": "Heb ei restru",
+    "regeneration_indicator.label": "Llwytho…",
+    "regeneration_indicator.sublabel": "Mae eich ffrwd cartref yn cael ei baratoi!",
+    "relative_time.days": "{number}d",
+    "relative_time.hours": "{number}h",
+    "relative_time.just_now": "nawr",
+    "relative_time.minutes": "{number}m",
+    "relative_time.seconds": "{number}s",
+    "reply_indicator.cancel": "Canslo",
+    "report.forward": "",
+    "report.forward_hint": "",
+    "report.hint": "",
+    "report.placeholder": "Sylwadau ychwanegol",
+    "report.submit": "Cyflwyno",
+    "report.target": "",
+    "search.placeholder": "Chwilio",
+    "search_popout.search_format": "Fformat chwilio uwch",
+    "search_popout.tips.full_text": "",
+    "search_popout.tips.hashtag": "hashnod",
+    "search_popout.tips.status": "statws",
+    "search_popout.tips.text": "",
+    "search_popout.tips.user": "defnyddiwr",
+    "search_results.accounts": "Pobl",
+    "search_results.hashtags": "Hanshnodau",
+    "search_results.statuses": "Twtiau",
+    "search_results.total": "",
+    "standalone.public_title": "Golwg tu fewn...",
+    "status.block": "Blociwch @{name}",
+    "status.cancel_reblog_private": "",
+    "status.cannot_reblog": "",
+    "status.delete": "Dileu",
+    "status.detailed_status": "",
+    "status.direct": "Neges breifat @{name}",
+    "status.embed": "Plannu",
+    "status.favourite": "",
+    "status.filtered": "",
+    "status.load_more": "Llwythwch mwy",
+    "status.media_hidden": "",
+    "status.mention": "",
+    "status.more": "Mwy",
+    "status.mute": "Tawelu @{name}",
+    "status.mute_conversation": "",
+    "status.open": "",
+    "status.pin": "",
+    "status.pinned": "",
+    "status.reblog": "",
+    "status.reblog_private": "",
+    "status.reblogged_by": "",
+    "status.reblogs.empty": "",
+    "status.redraft": "Dilëwh & ailddrafftio",
+    "status.reply": "Ateb",
+    "status.replyAll": "Ateb i edefyn",
+    "status.report": "",
+    "status.sensitive_toggle": "",
+    "status.sensitive_warning": "Cynnwys sensitif",
+    "status.share": "Rhannwch",
+    "status.show_less": "Dangoswch lai",
+    "status.show_less_all": "Dangoswch lai i bawb",
+    "status.show_more": "Dangoswch fwy",
+    "status.show_more_all": "",
+    "status.unmute_conversation": "Dad-dawelu sgwrs",
+    "status.unpin": "",
+    "tabs_bar.federated_timeline": "",
+    "tabs_bar.home": "Hafan",
+    "tabs_bar.local_timeline": "Lleol",
+    "tabs_bar.notifications": "Hysbysiadau",
+    "tabs_bar.search": "Chwilio",
+    "trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} yn siarad",
+    "ui.beforeunload": "Mi fyddwch yn colli eich drafft os gadewch Mastodon.",
+    "upload_area.title": "Llusgwch & gollwing i uwchlwytho",
+    "upload_button.label": "Ychwanegwch gyfryngau (JPEG, PNG, GIF, WebM, MP4, MOV)",
+    "upload_form.description": "",
+    "upload_form.focus": "",
+    "upload_form.undo": "Dileu",
+    "upload_progress.label": "Uwchlwytho...",
+    "video.close": "Cau fideo",
+    "video.exit_fullscreen": "Gadael sgrîn llawn",
+    "video.expand": "Ymestyn fideo",
+    "video.fullscreen": "Sgrîn llawn",
+    "video.hide": "Cuddio fideo",
+    "video.mute": "Tawelu sain",
+    "video.pause": "Oedi",
+    "video.play": "Chwarae",
+    "video.unmute": "Dad-dawelu sain"
 }
diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json
index 959a50377..e3d040ea8 100644
--- a/app/javascript/mastodon/locales/da.json
+++ b/app/javascript/mastodon/locales/da.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Nye følgere:",
   "notifications.column_settings.mention": "Omtale:",
   "notifications.column_settings.push": "Push notifikationer",
-  "notifications.column_settings.push_meta": "Denne enhed",
   "notifications.column_settings.reblog": "Fremhævelser:",
   "notifications.column_settings.show": "Vis i kolonne",
   "notifications.column_settings.sound": "Afspil lyd",
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index 4f1b2c8a0..d798878fb 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Neue Folgende:",
   "notifications.column_settings.mention": "Erwähnungen:",
   "notifications.column_settings.push": "Push-Benachrichtigungen",
-  "notifications.column_settings.push_meta": "Auf diesem Gerät",
   "notifications.column_settings.reblog": "Geteilte Beiträge:",
   "notifications.column_settings.show": "In der Spalte anzeigen",
   "notifications.column_settings.sound": "Ton abspielen",
diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json
index 5215bfb4e..b396726f7 100644
--- a/app/javascript/mastodon/locales/defaultMessages.json
+++ b/app/javascript/mastodon/locales/defaultMessages.json
@@ -1542,10 +1542,6 @@
         "id": "notifications.column_settings.push"
       },
       {
-        "defaultMessage": "This device",
-        "id": "notifications.column_settings.push_meta"
-      },
-      {
         "defaultMessage": "New followers:",
         "id": "notifications.column_settings.follow"
       },
@@ -2141,4 +2137,4 @@
     ],
     "path": "app/javascript/mastodon/features/video/index.json"
   }
-]
\ No newline at end of file
+]
diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json
index 9c2a31722..4e4b733ec 100644
--- a/app/javascript/mastodon/locales/el.json
+++ b/app/javascript/mastodon/locales/el.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Νέοι ακόλουθοι:",
   "notifications.column_settings.mention": "Αναφορές:",
   "notifications.column_settings.push": "Άμεσες ειδοποιήσεις",
-  "notifications.column_settings.push_meta": "Αυτή η συσκευή",
   "notifications.column_settings.reblog": "Προωθήσεις:",
   "notifications.column_settings.show": "Εμφάνισε σε στήλη",
   "notifications.column_settings.sound": "Ηχητική ειδοποίηση",
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index b117dad95..65b290be7 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -221,7 +221,6 @@
   "notifications.column_settings.follow": "New followers:",
   "notifications.column_settings.mention": "Mentions:",
   "notifications.column_settings.push": "Push notifications",
-  "notifications.column_settings.push_meta": "This device",
   "notifications.column_settings.reblog": "Boosts:",
   "notifications.column_settings.show": "Show in column",
   "notifications.column_settings.sound": "Play sound",
diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
index dcc565eb9..86bee46b1 100644
--- a/app/javascript/mastodon/locales/eo.json
+++ b/app/javascript/mastodon/locales/eo.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Novaj sekvantoj:",
   "notifications.column_settings.mention": "Mencioj:",
   "notifications.column_settings.push": "Puŝsciigoj",
-  "notifications.column_settings.push_meta": "Ĉi tiu aparato",
   "notifications.column_settings.reblog": "Diskonigoj:",
   "notifications.column_settings.show": "Montri en kolumno",
   "notifications.column_settings.sound": "Eligi sonon",
diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json
index 355c901fa..63f197c28 100644
--- a/app/javascript/mastodon/locales/es.json
+++ b/app/javascript/mastodon/locales/es.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Nuevos seguidores:",
   "notifications.column_settings.mention": "Menciones:",
   "notifications.column_settings.push": "Notificaciones push",
-  "notifications.column_settings.push_meta": "Este dispositivo",
   "notifications.column_settings.reblog": "Retoots:",
   "notifications.column_settings.show": "Mostrar en columna",
   "notifications.column_settings.sound": "Reproducir sonido",
diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json
index 1dc42ae68..e4b1154b7 100644
--- a/app/javascript/mastodon/locales/eu.json
+++ b/app/javascript/mastodon/locales/eu.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Jarraitzaile berriak:",
   "notifications.column_settings.mention": "Aipamenak:",
   "notifications.column_settings.push": "Push jakinarazpenak",
-  "notifications.column_settings.push_meta": "Gailu hau",
   "notifications.column_settings.reblog": "Bultzadak:",
   "notifications.column_settings.show": "Erakutsi zutabean",
   "notifications.column_settings.sound": "Jo soinua",
diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json
index 2e3999918..fb4ded11c 100644
--- a/app/javascript/mastodon/locales/fa.json
+++ b/app/javascript/mastodon/locales/fa.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "پیگیران تازه:",
   "notifications.column_settings.mention": "نام‌بردن‌ها:",
   "notifications.column_settings.push": "اعلان‌ها از سمت سرور",
-  "notifications.column_settings.push_meta": "این دستگاه",
   "notifications.column_settings.reblog": "بازبوق‌ها:",
   "notifications.column_settings.show": "نمایش در ستون",
   "notifications.column_settings.sound": "پخش صدا",
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
index f6c0469be..caf949e8c 100644
--- a/app/javascript/mastodon/locales/fi.json
+++ b/app/javascript/mastodon/locales/fi.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Uudet seuraajat:",
   "notifications.column_settings.mention": "Maininnat:",
   "notifications.column_settings.push": "Push-ilmoitukset",
-  "notifications.column_settings.push_meta": "Tämä laite",
   "notifications.column_settings.reblog": "Buustit:",
   "notifications.column_settings.show": "Näytä sarakkeessa",
   "notifications.column_settings.sound": "Äänimerkki",
diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json
index 075bdcf30..ff09a1402 100644
--- a/app/javascript/mastodon/locales/fr.json
+++ b/app/javascript/mastodon/locales/fr.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Nouveaux⋅elles abonné⋅e·s :",
   "notifications.column_settings.mention": "Mentions :",
   "notifications.column_settings.push": "Notifications",
-  "notifications.column_settings.push_meta": "Cet appareil",
   "notifications.column_settings.reblog": "Partages :",
   "notifications.column_settings.show": "Afficher dans la colonne",
   "notifications.column_settings.sound": "Émettre un son",
diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json
index 8b830ae7b..6afa21c9f 100644
--- a/app/javascript/mastodon/locales/gl.json
+++ b/app/javascript/mastodon/locales/gl.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Novos seguidores:",
   "notifications.column_settings.mention": "Mencións:",
   "notifications.column_settings.push": "Enviar notificacións",
-  "notifications.column_settings.push_meta": "Este aparello",
   "notifications.column_settings.reblog": "Promocións:",
   "notifications.column_settings.show": "Mostrar en columna",
   "notifications.column_settings.sound": "Reproducir son",
diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json
index 4aee82212..d670d8a55 100644
--- a/app/javascript/mastodon/locales/he.json
+++ b/app/javascript/mastodon/locales/he.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "עוקבים חדשים:",
   "notifications.column_settings.mention": "פניות:",
   "notifications.column_settings.push": "הודעות בדחיפה",
-  "notifications.column_settings.push_meta": "מכשיר זה",
   "notifications.column_settings.reblog": "הדהודים:",
   "notifications.column_settings.show": "הצגה בטור",
   "notifications.column_settings.sound": "שמע מופעל",
diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json
index a138cf589..b76b82e1b 100644
--- a/app/javascript/mastodon/locales/hr.json
+++ b/app/javascript/mastodon/locales/hr.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Novi sljedbenici:",
   "notifications.column_settings.mention": "Spominjanja:",
   "notifications.column_settings.push": "Push notifications",
-  "notifications.column_settings.push_meta": "This device",
   "notifications.column_settings.reblog": "Boostovi:",
   "notifications.column_settings.show": "Prikaži u stupcu",
   "notifications.column_settings.sound": "Sviraj zvuk",
diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json
index 6a4d0ecf0..57a8b7cfa 100644
--- a/app/javascript/mastodon/locales/hu.json
+++ b/app/javascript/mastodon/locales/hu.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Új követők:",
   "notifications.column_settings.mention": "Megemítéseim:",
   "notifications.column_settings.push": "Push értesítések",
-  "notifications.column_settings.push_meta": "Ezen eszköz",
   "notifications.column_settings.reblog": "Rebloggolások:",
   "notifications.column_settings.show": "Oszlopban mutatás",
   "notifications.column_settings.sound": "Hang lejátszása",
diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json
index 7d7090236..077748a0a 100644
--- a/app/javascript/mastodon/locales/hy.json
+++ b/app/javascript/mastodon/locales/hy.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Նոր հետեւողներ՝",
   "notifications.column_settings.mention": "Նշումներ՝",
   "notifications.column_settings.push": "Հրելու ծանուցումներ",
-  "notifications.column_settings.push_meta": "Այս սարքը",
   "notifications.column_settings.reblog": "Տարածածներից՝",
   "notifications.column_settings.show": "Ցուցադրել սյունում",
   "notifications.column_settings.sound": "Ձայն հանել",
diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json
index c8ed00e9f..3d80c0949 100644
--- a/app/javascript/mastodon/locales/id.json
+++ b/app/javascript/mastodon/locales/id.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Pengikut baru:",
   "notifications.column_settings.mention": "Balasan:",
   "notifications.column_settings.push": "Push notifications",
-  "notifications.column_settings.push_meta": "This device",
   "notifications.column_settings.reblog": "Boost:",
   "notifications.column_settings.show": "Tampilkan dalam kolom",
   "notifications.column_settings.sound": "Mainkan suara",
diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json
index c92ceccd1..9059b3a2b 100644
--- a/app/javascript/mastodon/locales/io.json
+++ b/app/javascript/mastodon/locales/io.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Nova sequanti:",
   "notifications.column_settings.mention": "Mencioni:",
   "notifications.column_settings.push": "Push notifications",
-  "notifications.column_settings.push_meta": "This device",
   "notifications.column_settings.reblog": "Repeti:",
   "notifications.column_settings.show": "Montrar en kolumno",
   "notifications.column_settings.sound": "Plear sono",
diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json
index e9eb62e71..5d8e3fe4a 100644
--- a/app/javascript/mastodon/locales/it.json
+++ b/app/javascript/mastodon/locales/it.json
@@ -3,7 +3,7 @@
   "account.block": "Blocca @{name}",
   "account.block_domain": "Nascondi tutto da {domain}",
   "account.blocked": "Bloccato",
-  "account.direct": "Direct Message @{name}",
+  "account.direct": "Invia messaggio diretto a @{name}",
   "account.disclaimer_full": "Il profilo dell'utente mostrato qui sotto potrebbe essere incompleto.",
   "account.domain_blocked": "Dominio nascosto",
   "account.edit_profile": "Modifica profilo",
@@ -54,7 +54,7 @@
   "column.lists": "Liste",
   "column.mutes": "Utenti silenziati",
   "column.notifications": "Notifiche",
-  "column.pins": "Pinned toot",
+  "column.pins": "Toot fissati in cima",
   "column.public": "Timeline federata",
   "column_back_button.label": "Indietro",
   "column_header.hide_settings": "Nascondi impostazioni",
@@ -79,11 +79,11 @@
   "compose_form.spoiler.unmarked": "Il testo non è nascosto",
   "compose_form.spoiler_placeholder": "Content warning",
   "confirmation_modal.cancel": "Annulla",
-  "confirmations.block.confirm": "Block",
+  "confirmations.block.confirm": "Blocca",
   "confirmations.block.message": "Sei sicuro di voler bloccare {name}?",
-  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.confirm": "Cancella",
   "confirmations.delete.message": "Sei sicuro di voler cancellare questo status?",
-  "confirmations.delete_list.confirm": "Delete",
+  "confirmations.delete_list.confirm": "Cancella",
   "confirmations.delete_list.message": "Sei sicuro di voler cancellare definitivamente questa lista?",
   "confirmations.domain_block.confirm": "Nascondi intero dominio",
   "confirmations.domain_block.message": "Sei davvero sicuro che vuoi bloccare l'intero {domain}? Nella maggior parte dei casi, pochi blocchi o silenziamenti mirati sono sufficienti e preferibili. Non vedrai nessun contenuto di quel dominio né nelle timeline pubbliche né nelle notifiche. I tuoi seguaci di quel dominio saranno eliminati.",
@@ -119,7 +119,7 @@
   "empty_column.hashtag": "Non c'è ancora nessun post con questo hashtag.",
   "empty_column.home": "Non stai ancora seguendo nessuno. Visita {public} o usa la ricerca per incontrare nuove persone.",
   "empty_column.home.public_timeline": "la timeline pubblica",
-  "empty_column.list": "Non c'è niente in questo elenco ancora. Quando i membri di questo elenco postano nuovi stati, questi appariranno qui.",
+  "empty_column.list": "Non c'è ancora niente in questa lista. Quando i membri di questa lista pubblicheranno nuovi stati, appariranno qui.",
   "empty_column.lists": "Non hai ancora nessuna lista. Quando ne creerai qualcuna, comparirà qui.",
   "empty_column.mutes": "Non hai ancora silenziato nessun utente.",
   "empty_column.notifications": "Non hai ancora nessuna notifica. Interagisci con altri per iniziare conversazioni.",
@@ -179,7 +179,7 @@
   "lists.new.title_placeholder": "Titolo della nuova lista",
   "lists.search": "Cerca tra le persone che segui",
   "lists.subheading": "Le tue liste",
-  "loading_indicator.label": "Carico...",
+  "loading_indicator.label": "Caricamento...",
   "media_gallery.toggle_visible": "Imposta visibilità",
   "missing_indicator.label": "Non trovato",
   "missing_indicator.sublabel": "Risorsa non trovata",
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Nuovi seguaci:",
   "notifications.column_settings.mention": "Menzioni:",
   "notifications.column_settings.push": "Notifiche push",
-  "notifications.column_settings.push_meta": "Questo dispositivo",
   "notifications.column_settings.reblog": "Post condivisi:",
   "notifications.column_settings.show": "Mostra in colonna",
   "notifications.column_settings.sound": "Riproduci suono",
@@ -242,7 +241,7 @@
   "onboarding.page_three.search": "Usa la barra di ricerca per trovare persone e hashtag, come {illustration} e {introductions}. Per trovare una persona che non è su questa istanza, usa il suo nome utente completo.",
   "onboarding.page_two.compose": "Puoi scrivere dei post dalla colonna di composizione. Puoi caricare immagini, modificare le impostazioni di privacy, e aggiungere avvisi sul contenuto con le icone qui sotto.",
   "onboarding.skip": "Salta",
-  "privacy.change": "Modifica privacy post",
+  "privacy.change": "Modifica privacy del post",
   "privacy.direct.long": "Invia solo a utenti menzionati",
   "privacy.direct.short": "Diretto",
   "privacy.private.long": "Invia solo ai seguaci",
@@ -261,7 +260,7 @@
   "reply_indicator.cancel": "Annulla",
   "report.forward": "Inoltra a {target}",
   "report.forward_hint": "Questo account appartiene a un altro server. Mandare anche là una copia anonima del rapporto?",
-  "report.hint": "La segnalazione sara' invata ai tuoi moderatori di istanza. Di seguito, puoi fornire  il motivo per il quale stai segnalando questo account:",
+  "report.hint": "La segnalazione sarà inviata ai moderatori della tua istanza. Di seguito, puoi fornire il motivo per il quale stai segnalando questo account:",
   "report.placeholder": "Commenti aggiuntivi",
   "report.submit": "Invia",
   "report.target": "Invio la segnalazione {target}",
@@ -328,10 +327,10 @@
   "video.close": "Chiudi video",
   "video.exit_fullscreen": "Esci da modalità a schermo intero",
   "video.expand": "Espandi video",
-  "video.fullscreen": "Full screen",
+  "video.fullscreen": "Schermo intero",
   "video.hide": "Nascondi video",
   "video.mute": "Silenzia suono",
-  "video.pause": "Pause",
+  "video.pause": "Pausa",
   "video.play": "Avvia",
   "video.unmute": "Riattiva suono"
 }
diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json
index 04a2e4692..c84387051 100644
--- a/app/javascript/mastodon/locales/ja.json
+++ b/app/javascript/mastodon/locales/ja.json
@@ -221,7 +221,6 @@
   "notifications.column_settings.follow": "新しいフォロワー:",
   "notifications.column_settings.mention": "返信:",
   "notifications.column_settings.push": "プッシュ通知",
-  "notifications.column_settings.push_meta": "このデバイス",
   "notifications.column_settings.reblog": "ブースト:",
   "notifications.column_settings.show": "カラムに表示",
   "notifications.column_settings.sound": "通知音を再生",
diff --git a/app/javascript/mastodon/locales/ka.json b/app/javascript/mastodon/locales/ka.json
index ec6e211c6..21cd7d644 100644
--- a/app/javascript/mastodon/locales/ka.json
+++ b/app/javascript/mastodon/locales/ka.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "ახალი მიმდევრები:",
   "notifications.column_settings.mention": "ხსენებები:",
   "notifications.column_settings.push": "ფუშ შეტყობინებები",
-  "notifications.column_settings.push_meta": "ეს მოწყობილობა",
   "notifications.column_settings.reblog": "ბუსტები:",
   "notifications.column_settings.show": "გამოჩნდეს სვეტში",
   "notifications.column_settings.sound": "ხმის დაკვრა",
diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json
index 15e8524e0..8e0f9f59f 100644
--- a/app/javascript/mastodon/locales/ko.json
+++ b/app/javascript/mastodon/locales/ko.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "새 팔로워:",
   "notifications.column_settings.mention": "답글:",
   "notifications.column_settings.push": "푸시 알림",
-  "notifications.column_settings.push_meta": "이 장치",
   "notifications.column_settings.reblog": "부스트:",
   "notifications.column_settings.show": "컬럼에 표시",
   "notifications.column_settings.sound": "효과음 재생",
diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json
index f829146f1..e6b85692c 100644
--- a/app/javascript/mastodon/locales/nl.json
+++ b/app/javascript/mastodon/locales/nl.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Nieuwe volgers:",
   "notifications.column_settings.mention": "Vermeldingen:",
   "notifications.column_settings.push": "Pushmeldingen",
-  "notifications.column_settings.push_meta": "Dit apparaat",
   "notifications.column_settings.reblog": "Boosts:",
   "notifications.column_settings.show": "In kolom tonen",
   "notifications.column_settings.sound": "Geluid afspelen",
diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json
index e531789a9..4a8176e82 100644
--- a/app/javascript/mastodon/locales/no.json
+++ b/app/javascript/mastodon/locales/no.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Nye følgere:",
   "notifications.column_settings.mention": "Nevnt:",
   "notifications.column_settings.push": "Push varsler",
-  "notifications.column_settings.push_meta": "Denne enheten",
   "notifications.column_settings.reblog": "Fremhevet:",
   "notifications.column_settings.show": "Vis i kolonne",
   "notifications.column_settings.sound": "Spill lyd",
diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json
index 182710038..64cbaef55 100644
--- a/app/javascript/mastodon/locales/oc.json
+++ b/app/javascript/mastodon/locales/oc.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Nòus seguidors :",
   "notifications.column_settings.mention": "Mencions :",
   "notifications.column_settings.push": "Notificacions",
-  "notifications.column_settings.push_meta": "Aqueste periferic",
   "notifications.column_settings.reblog": "Partatges :",
   "notifications.column_settings.show": "Mostrar dins la colomna",
   "notifications.column_settings.sound": "Emetre un son",
diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json
index aa2535bf7..0b9828813 100644
--- a/app/javascript/mastodon/locales/pl.json
+++ b/app/javascript/mastodon/locales/pl.json
@@ -221,7 +221,6 @@
   "notifications.column_settings.follow": "Nowi śledzący:",
   "notifications.column_settings.mention": "Wspomnienia:",
   "notifications.column_settings.push": "Powiadomienia push",
-  "notifications.column_settings.push_meta": "To urządzenie",
   "notifications.column_settings.reblog": "Podbicia:",
   "notifications.column_settings.show": "Pokaż w kolumnie",
   "notifications.column_settings.sound": "Odtwarzaj dźwięk",
diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json
index 9c48f0bc1..3e5b5da8e 100644
--- a/app/javascript/mastodon/locales/pt-BR.json
+++ b/app/javascript/mastodon/locales/pt-BR.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Novos seguidores:",
   "notifications.column_settings.mention": "Menções:",
   "notifications.column_settings.push": "Enviar notificações",
-  "notifications.column_settings.push_meta": "Este aparelho",
   "notifications.column_settings.reblog": "Compartilhamento:",
   "notifications.column_settings.show": "Mostrar nas colunas",
   "notifications.column_settings.sound": "Reproduzir som",
diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json
index 096fded95..e9d91f631 100644
--- a/app/javascript/mastodon/locales/pt.json
+++ b/app/javascript/mastodon/locales/pt.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Novos seguidores:",
   "notifications.column_settings.mention": "Menções:",
   "notifications.column_settings.push": "Notificações Push",
-  "notifications.column_settings.push_meta": "Este dispositivo",
   "notifications.column_settings.reblog": "Partilhas:",
   "notifications.column_settings.show": "Mostrar nas colunas",
   "notifications.column_settings.sound": "Reproduzir som",
diff --git a/app/javascript/mastodon/locales/ro.json b/app/javascript/mastodon/locales/ro.json
index fda82136e..1d6e73bfd 100644
--- a/app/javascript/mastodon/locales/ro.json
+++ b/app/javascript/mastodon/locales/ro.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Noi urmăritori:",
   "notifications.column_settings.mention": "Mențiuni:",
   "notifications.column_settings.push": "Notificări push",
-  "notifications.column_settings.push_meta": "Acest dispozitiv",
   "notifications.column_settings.reblog": "Redistribuite:",
   "notifications.column_settings.show": "Arată în coloană",
   "notifications.column_settings.sound": "Redă sunet",
diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json
index 8961136a3..15fbfac3f 100644
--- a/app/javascript/mastodon/locales/ru.json
+++ b/app/javascript/mastodon/locales/ru.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Новые подписчики:",
   "notifications.column_settings.mention": "Упоминания:",
   "notifications.column_settings.push": "Push-уведомления",
-  "notifications.column_settings.push_meta": "Это устройство",
   "notifications.column_settings.reblog": "Продвижения:",
   "notifications.column_settings.show": "Показывать в колонке",
   "notifications.column_settings.sound": "Проигрывать звук",
diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json
index 1264b6d08..11a6b76c1 100644
--- a/app/javascript/mastodon/locales/sk.json
+++ b/app/javascript/mastodon/locales/sk.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Noví následujúci:",
   "notifications.column_settings.mention": "Zmienenia:",
   "notifications.column_settings.push": "Push notifikácie",
-  "notifications.column_settings.push_meta": "Toto zariadenie",
   "notifications.column_settings.reblog": "Boosty:",
   "notifications.column_settings.show": "Zobraziť v stĺpci",
   "notifications.column_settings.sound": "Prehrať zvuk",
diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json
index 6250aab62..f715abe85 100644
--- a/app/javascript/mastodon/locales/sl.json
+++ b/app/javascript/mastodon/locales/sl.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "New followers:",
   "notifications.column_settings.mention": "Mentions:",
   "notifications.column_settings.push": "Push notifications",
-  "notifications.column_settings.push_meta": "This device",
   "notifications.column_settings.reblog": "Boosts:",
   "notifications.column_settings.show": "Show in column",
   "notifications.column_settings.sound": "Play sound",
diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json
index 0ef07a170..7131d3044 100644
--- a/app/javascript/mastodon/locales/sr-Latn.json
+++ b/app/javascript/mastodon/locales/sr-Latn.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Novi pratioci:",
   "notifications.column_settings.mention": "Pominjanja:",
   "notifications.column_settings.push": "Guraj obaveštenja",
-  "notifications.column_settings.push_meta": "Ovaj uređaj",
   "notifications.column_settings.reblog": "Podrški:",
   "notifications.column_settings.show": "Prikaži u koloni",
   "notifications.column_settings.sound": "Puštaj zvuk",
diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json
index ca2c1b225..806c0acb3 100644
--- a/app/javascript/mastodon/locales/sr.json
+++ b/app/javascript/mastodon/locales/sr.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Нови пратиоци:",
   "notifications.column_settings.mention": "Помињања:",
   "notifications.column_settings.push": "Гурај обавештења",
-  "notifications.column_settings.push_meta": "Овај уређај",
   "notifications.column_settings.reblog": "Подршки:",
   "notifications.column_settings.show": "Прикажи у колони",
   "notifications.column_settings.sound": "Пуштај звук",
diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json
index c7df96cf5..f9129d368 100644
--- a/app/javascript/mastodon/locales/sv.json
+++ b/app/javascript/mastodon/locales/sv.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Nya följare:",
   "notifications.column_settings.mention": "Omnämningar:",
   "notifications.column_settings.push": "Push meddelanden",
-  "notifications.column_settings.push_meta": "Denna anordning",
   "notifications.column_settings.reblog": "Knuffar:",
   "notifications.column_settings.show": "Visa i kolumnen",
   "notifications.column_settings.sound": "Spela upp ljud",
diff --git a/app/javascript/mastodon/locales/ta.json b/app/javascript/mastodon/locales/ta.json
index e0237c023..427e9a3dc 100644
--- a/app/javascript/mastodon/locales/ta.json
+++ b/app/javascript/mastodon/locales/ta.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "New followers:",
   "notifications.column_settings.mention": "Mentions:",
   "notifications.column_settings.push": "Push notifications",
-  "notifications.column_settings.push_meta": "This device",
   "notifications.column_settings.reblog": "Boosts:",
   "notifications.column_settings.show": "Show in column",
   "notifications.column_settings.sound": "Play sound",
diff --git a/app/javascript/mastodon/locales/te.json b/app/javascript/mastodon/locales/te.json
index 49f3ce2d9..bc13b02f1 100644
--- a/app/javascript/mastodon/locales/te.json
+++ b/app/javascript/mastodon/locales/te.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "క్రొత్త అనుచరులు:",
   "notifications.column_settings.mention": "ప్రస్తావనలు:",
   "notifications.column_settings.push": "పుష్ ప్రకటనలు",
-  "notifications.column_settings.push_meta": "ఈ పరికరం",
   "notifications.column_settings.reblog": "బూస్ట్ లు:",
   "notifications.column_settings.show": "నిలువు వరుసలో చూపు",
   "notifications.column_settings.sound": "ధ్వనిని ప్లే చేయి",
diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json
index 2af5bfca1..3114bca60 100644
--- a/app/javascript/mastodon/locales/th.json
+++ b/app/javascript/mastodon/locales/th.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "New followers:",
   "notifications.column_settings.mention": "Mentions:",
   "notifications.column_settings.push": "Push notifications",
-  "notifications.column_settings.push_meta": "This device",
   "notifications.column_settings.reblog": "Boosts:",
   "notifications.column_settings.show": "Show in column",
   "notifications.column_settings.sound": "Play sound",
diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json
index 68dc37e6b..a661b022b 100644
--- a/app/javascript/mastodon/locales/tr.json
+++ b/app/javascript/mastodon/locales/tr.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Yeni takipçiler:",
   "notifications.column_settings.mention": "Bahsedilenler:",
   "notifications.column_settings.push": "Push notifications",
-  "notifications.column_settings.push_meta": "This device",
   "notifications.column_settings.reblog": "Boost’lar:",
   "notifications.column_settings.show": "Bildirimlerde göster",
   "notifications.column_settings.sound": "Ses çal",
diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json
index 5517abdf8..116dfc489 100644
--- a/app/javascript/mastodon/locales/uk.json
+++ b/app/javascript/mastodon/locales/uk.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "Нові підписники:",
   "notifications.column_settings.mention": "Згадки:",
   "notifications.column_settings.push": "Push-сповіщення",
-  "notifications.column_settings.push_meta": "Цей пристрій",
   "notifications.column_settings.reblog": "Передмухи:",
   "notifications.column_settings.show": "Показати в колонці",
   "notifications.column_settings.sound": "Відтворювати звуки",
diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json
index 254beab42..69ecd9431 100644
--- a/app/javascript/mastodon/locales/zh-CN.json
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "当有人关注你时:",
   "notifications.column_settings.mention": "当有人在嘟文中提及你时:",
   "notifications.column_settings.push": "推送通知",
-  "notifications.column_settings.push_meta": "此设备",
   "notifications.column_settings.reblog": "当有人转嘟了你的嘟文时:",
   "notifications.column_settings.show": "在通知栏显示",
   "notifications.column_settings.sound": "播放音效",
diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json
index ce141a8a7..fc5376699 100644
--- a/app/javascript/mastodon/locales/zh-HK.json
+++ b/app/javascript/mastodon/locales/zh-HK.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "關注你:",
   "notifications.column_settings.mention": "提及你:",
   "notifications.column_settings.push": "推送通知",
-  "notifications.column_settings.push_meta": "這臺設備",
   "notifications.column_settings.reblog": "轉推你的文章:",
   "notifications.column_settings.show": "在通知欄顯示",
   "notifications.column_settings.sound": "播放音效",
diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json
index fe4a81170..458af6b95 100644
--- a/app/javascript/mastodon/locales/zh-TW.json
+++ b/app/javascript/mastodon/locales/zh-TW.json
@@ -216,7 +216,6 @@
   "notifications.column_settings.follow": "新的關注者:",
   "notifications.column_settings.mention": "提到:",
   "notifications.column_settings.push": "推送通知",
-  "notifications.column_settings.push_meta": "這臺設備",
   "notifications.column_settings.reblog": "轉嘟:",
   "notifications.column_settings.show": "顯示在欄位中",
   "notifications.column_settings.sound": "播放音效",
diff --git a/app/javascript/mastodon/service_worker/web_push_notifications.js b/app/javascript/mastodon/service_worker/web_push_notifications.js
index d61d916b1..80a4fb329 100644
--- a/app/javascript/mastodon/service_worker/web_push_notifications.js
+++ b/app/javascript/mastodon/service_worker/web_push_notifications.js
@@ -168,7 +168,7 @@ const openUrl = url =>
 
       if (webClients.length !== 0) {
         const client       = findBestClient(webClients);
-        const { pathname } = new URL(url);
+        const { pathname } = new URL(url, self.location);
 
         if (pathname.startsWith('/web/')) {
           return client.focus().then(client => client.postMessage({
diff --git a/app/javascript/styles/mailer.scss b/app/javascript/styles/mailer.scss
index dbe070f9a..d83bd4d96 100644
--- a/app/javascript/styles/mailer.scss
+++ b/app/javascript/styles/mailer.scss
@@ -169,7 +169,7 @@ p {
   font-family: Helvetica, Arial, sans-serif;
 
   @media only screen {
-    font-family: 'mastodon-font-sans-serif', sans-serif !important;
+    font-family: $font-sans-serif, sans-serif !important;
   }
 }
 
diff --git a/app/javascript/styles/mastodon/about.scss b/app/javascript/styles/mastodon/about.scss
index 228dd96f0..dc456be3e 100644
--- a/app/javascript/styles/mastodon/about.scss
+++ b/app/javascript/styles/mastodon/about.scss
@@ -16,7 +16,7 @@ $small-breakpoint: 960px;
 }
 
 .rich-formatting {
-  font-family: 'mastodon-font-sans-serif', sans-serif;
+  font-family: $font-sans-serif, sans-serif;
   font-size: 16px;
   font-weight: 400;
   font-size: 16px;
@@ -31,7 +31,7 @@ $small-breakpoint: 960px;
 
   p,
   li {
-    font-family: 'mastodon-font-sans-serif', sans-serif;
+    font-family: $font-sans-serif, sans-serif;
     font-size: 16px;
     font-weight: 400;
     font-size: 16px;
@@ -62,7 +62,7 @@ $small-breakpoint: 960px;
   }
 
   h1 {
-    font-family: 'mastodon-font-display', sans-serif;
+    font-family: $font-display, sans-serif;
     font-size: 26px;
     line-height: 30px;
     font-weight: 500;
@@ -70,7 +70,7 @@ $small-breakpoint: 960px;
     color: $secondary-text-color;
 
     small {
-      font-family: 'mastodon-font-sans-serif', sans-serif;
+      font-family: $font-sans-serif, sans-serif;
       display: block;
       font-size: 18px;
       font-weight: 400;
@@ -79,7 +79,7 @@ $small-breakpoint: 960px;
   }
 
   h2 {
-    font-family: 'mastodon-font-display', sans-serif;
+    font-family: $font-display, sans-serif;
     font-size: 22px;
     line-height: 26px;
     font-weight: 500;
@@ -88,7 +88,7 @@ $small-breakpoint: 960px;
   }
 
   h3 {
-    font-family: 'mastodon-font-display', sans-serif;
+    font-family: $font-display, sans-serif;
     font-size: 18px;
     line-height: 24px;
     font-weight: 500;
@@ -97,7 +97,7 @@ $small-breakpoint: 960px;
   }
 
   h4 {
-    font-family: 'mastodon-font-display', sans-serif;
+    font-family: $font-display, sans-serif;
     font-size: 16px;
     line-height: 24px;
     font-weight: 500;
@@ -106,7 +106,7 @@ $small-breakpoint: 960px;
   }
 
   h5 {
-    font-family: 'mastodon-font-display', sans-serif;
+    font-family: $font-display, sans-serif;
     font-size: 14px;
     line-height: 24px;
     font-weight: 500;
@@ -115,7 +115,7 @@ $small-breakpoint: 960px;
   }
 
   h6 {
-    font-family: 'mastodon-font-display', sans-serif;
+    font-family: $font-display, sans-serif;
     font-size: 12px;
     line-height: 24px;
     font-weight: 500;
@@ -180,7 +180,7 @@ $small-breakpoint: 960px;
 
   &__section {
     flex: 1 0 0;
-    font-family: 'mastodon-font-sans-serif', sans-serif;
+    font-family: $font-sans-serif, sans-serif;
     font-size: 16px;
     line-height: 28px;
     color: $primary-text-color;
@@ -221,7 +221,7 @@ $small-breakpoint: 960px;
     bottom: -40px;
 
     .panel-header {
-      font-family: 'mastodon-font-display', sans-serif;
+      font-family: $font-display, sans-serif;
       font-size: 14px;
       line-height: 24px;
       font-weight: 500;
@@ -450,7 +450,7 @@ $small-breakpoint: 960px;
 
   p,
   li {
-    font-family: 'mastodon-font-sans-serif', sans-serif;
+    font-family: $font-sans-serif, sans-serif;
     font-size: 16px;
     font-weight: 400;
     font-size: 16px;
@@ -499,7 +499,7 @@ $small-breakpoint: 960px;
   }
 
   h1 {
-    font-family: 'mastodon-font-display', sans-serif;
+    font-family: $font-display, sans-serif;
     font-size: 26px;
     line-height: 30px;
     font-weight: 500;
@@ -507,7 +507,7 @@ $small-breakpoint: 960px;
     color: $secondary-text-color;
 
     small {
-      font-family: 'mastodon-font-sans-serif', sans-serif;
+      font-family: $font-sans-serif, sans-serif;
       display: block;
       font-size: 18px;
       font-weight: 400;
@@ -516,7 +516,7 @@ $small-breakpoint: 960px;
   }
 
   h2 {
-    font-family: 'mastodon-font-display', sans-serif;
+    font-family: $font-display, sans-serif;
     font-size: 22px;
     line-height: 26px;
     font-weight: 500;
@@ -525,7 +525,7 @@ $small-breakpoint: 960px;
   }
 
   h3 {
-    font-family: 'mastodon-font-display', sans-serif;
+    font-family: $font-display, sans-serif;
     font-size: 18px;
     line-height: 24px;
     font-weight: 500;
@@ -534,7 +534,7 @@ $small-breakpoint: 960px;
   }
 
   h4 {
-    font-family: 'mastodon-font-display', sans-serif;
+    font-family: $font-display, sans-serif;
     font-size: 16px;
     line-height: 24px;
     font-weight: 500;
@@ -543,7 +543,7 @@ $small-breakpoint: 960px;
   }
 
   h5 {
-    font-family: 'mastodon-font-display', sans-serif;
+    font-family: $font-display, sans-serif;
     font-size: 14px;
     line-height: 24px;
     font-weight: 500;
@@ -552,7 +552,7 @@ $small-breakpoint: 960px;
   }
 
   h6 {
-    font-family: 'mastodon-font-display', sans-serif;
+    font-family: $font-display, sans-serif;
     font-size: 12px;
     line-height: 24px;
     font-weight: 500;
@@ -619,7 +619,7 @@ $small-breakpoint: 960px;
 
       .hero .heading {
         padding-bottom: 20px;
-        font-family: 'mastodon-font-sans-serif', sans-serif;
+        font-family: $font-sans-serif, sans-serif;
         font-size: 16px;
         font-weight: 400;
         font-size: 16px;
@@ -670,7 +670,7 @@ $small-breakpoint: 960px;
         text-decoration: none;
         padding: 12px 16px;
         line-height: 32px;
-        font-family: 'mastodon-font-display', sans-serif;
+        font-family: $font-display, sans-serif;
         font-weight: 500;
         font-size: 14px;
 
@@ -743,7 +743,7 @@ $small-breakpoint: 960px;
   .about-short {
     background: darken($ui-base-color, 4%);
     padding: 50px 0 30px;
-    font-family: 'mastodon-font-sans-serif', sans-serif;
+    font-family: $font-sans-serif, sans-serif;
     font-size: 16px;
     font-weight: 400;
     font-size: 16px;
@@ -1012,7 +1012,7 @@ $small-breakpoint: 960px;
     display: flex;
     -webkit-overflow-scrolling: touch;
     -ms-overflow-style: -ms-autohiding-scrollbar;
-    font-family: 'mastodon-font-sans-serif', sans-serif;
+    font-family: $font-sans-serif, sans-serif;
     font-size: 13px;
     line-height: 18px;
     font-weight: 400;
diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss
index 9dfd89dc2..b6c771abf 100644
--- a/app/javascript/styles/mastodon/admin.scss
+++ b/app/javascript/styles/mastodon/admin.scss
@@ -432,7 +432,7 @@ $no-columns-breakpoint: 600px;
     border-radius: 0 0 4px 4px;
     padding: 10px;
     color: $darker-text-color;
-    font-family: 'mastodon-font-monospace', monospace;
+    font-family: $font-monospace, monospace;
     font-size: 12px;
     word-wrap: break-word;
     min-height: 20px;
diff --git a/app/javascript/styles/mastodon/basics.scss b/app/javascript/styles/mastodon/basics.scss
index 3bbb31e6e..746def625 100644
--- a/app/javascript/styles/mastodon/basics.scss
+++ b/app/javascript/styles/mastodon/basics.scss
@@ -6,7 +6,7 @@
 }
 
 body {
-  font-family: 'mastodon-font-sans-serif', sans-serif;
+  font-family: $font-sans-serif, sans-serif;
   background: darken($ui-base-color, 8%);
   font-size: 13px;
   line-height: 18px;
@@ -29,8 +29,8 @@ body {
     // Fira Sans => Firefox OS
     // Droid Sans => Older Androids (<4.0)
     // Helvetica Neue => Older macOS <10.11
-    // mastodon-font-sans-serif => web-font (Roboto) fallback and newer Androids (>=4.0)
-    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", mastodon-font-sans-serif, sans-serif;
+    // $font-sans-serif => web-font (Roboto) fallback and newer Androids (>=4.0)
+    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", $font-sans-serif, sans-serif;
   }
 
   &.app-body {
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 490e26ccc..ecd1a8063 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -552,7 +552,7 @@
 
       .character-counter {
         cursor: default;
-        font-family: 'mastodon-font-sans-serif', sans-serif;
+        font-family: $font-sans-serif, sans-serif;
         font-size: 14px;
         font-weight: 600;
         color: $lighter-text-color;
@@ -631,11 +631,13 @@
 
 .status__content,
 .reply-indicator__content {
+  position: relative;
   font-size: 15px;
   line-height: 20px;
   word-wrap: break-word;
   font-weight: 400;
   overflow: hidden;
+  text-overflow: ellipsis;
   white-space: pre-wrap;
   padding-top: 2px;
   color: $primary-text-color;
@@ -721,6 +723,26 @@
   }
 }
 
+.status__content.status__content--collapsed {
+  max-height: 20px * 15; // 15 lines is roughly above 500 characters
+}
+
+.status__content__read-more-button {
+  display: block;
+  font-size: 15px;
+  line-height: 20px;
+  color: lighten($ui-highlight-color, 8%);
+  border: 0;
+  background: transparent;
+  padding: 0;
+  padding-top: 8px;
+
+  &:hover,
+  &:active {
+    text-decoration: underline;
+  }
+}
+
 .status__content__spoiler-link {
   display: inline-block;
   border-radius: 2px;
@@ -3009,8 +3031,7 @@ a.status-card {
   line-height: 24px;
 }
 
-.setting-toggle__label,
-.setting-meta__label {
+.setting-toggle__label {
   color: $darker-text-color;
   display: inline-block;
   margin-bottom: 14px;
@@ -3018,10 +3039,6 @@ a.status-card {
   vertical-align: middle;
 }
 
-.setting-meta__label {
-  float: right;
-}
-
 .empty-column-indicator,
 .error-column {
   color: $dark-text-color;
@@ -5122,7 +5139,7 @@ noscript {
       width: 100%;
       border: none;
       padding: 10px;
-      font-family: 'mastodon-font-monospace', monospace;
+      font-family: $font-monospace, monospace;
       background: $ui-base-color;
       color: $primary-text-color;
       font-size: 14px;
diff --git a/app/javascript/styles/mastodon/containers.scss b/app/javascript/styles/mastodon/containers.scss
index 47582f323..44fc1e538 100644
--- a/app/javascript/styles/mastodon/containers.scss
+++ b/app/javascript/styles/mastodon/containers.scss
@@ -37,7 +37,7 @@
       outline: 0;
       padding: 12px 16px;
       line-height: 32px;
-      font-family: 'mastodon-font-display', sans-serif;
+      font-family: $font-display, sans-serif;
       font-weight: 500;
       font-size: 14px;
     }
@@ -627,7 +627,7 @@
             font-size: 18px;
             margin-bottom: 5px;
             color: $primary-text-color;
-            font-family: 'mastodon-font-display', sans-serif;
+            font-family: $font-display, sans-serif;
           }
         }
 
diff --git a/app/javascript/styles/mastodon/dashboard.scss b/app/javascript/styles/mastodon/dashboard.scss
index 949ca733f..86cf6c61b 100644
--- a/app/javascript/styles/mastodon/dashboard.scss
+++ b/app/javascript/styles/mastodon/dashboard.scss
@@ -35,7 +35,7 @@
     font-weight: 500;
     font-size: 24px;
     color: $primary-text-color;
-    font-family: 'mastodon-font-display', sans-serif;
+    font-family: $font-display, sans-serif;
     margin-bottom: 20px;
   }
 
diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss
index cbd3de94c..be2bf7cea 100644
--- a/app/javascript/styles/mastodon/forms.scss
+++ b/app/javascript/styles/mastodon/forms.scss
@@ -1,7 +1,7 @@
 $no-columns-breakpoint: 600px;
 
 code {
-  font-family: 'mastodon-font-monospace', monospace;
+  font-family: $font-monospace, monospace;
   font-weight: 400;
 }
 
@@ -474,7 +474,7 @@ code {
     width: 100%;
     border: none;
     padding: 10px;
-    font-family: 'mastodon-font-monospace', monospace;
+    font-family: $font-monospace, monospace;
     background: $ui-base-color;
     color: $primary-text-color;
     font-size: 14px;
@@ -718,7 +718,7 @@ code {
 .form_admin_settings_custom_css,
 .form_admin_settings_closed_registrations_message {
   textarea {
-    font-family: 'mastodon-font-monospace', monospace;
+    font-family: $font-monospace, monospace;
   }
 }
 
@@ -742,7 +742,7 @@ code {
     border: 0;
     padding: 10px;
     font-size: 14px;
-    font-family: 'mastodon-font-monospace', monospace;
+    font-family: $font-monospace, monospace;
   }
 
   button {
diff --git a/app/javascript/styles/mastodon/rtl.scss b/app/javascript/styles/mastodon/rtl.scss
index b8c0efad8..ffc875b88 100644
--- a/app/javascript/styles/mastodon/rtl.scss
+++ b/app/javascript/styles/mastodon/rtl.scss
@@ -46,8 +46,8 @@ body.rtl {
   .column-header__buttons {
     left: 0;
     right: auto;
-    margin-left: -15px;
-    margin-right: 0;
+    margin-left: 0;
+    margin-right: -15px;
   }
 
   .column-inline-form .icon-button {
@@ -78,10 +78,6 @@ body.rtl {
     margin-right: 8px;
   }
 
-  .setting-meta__label {
-    float: left;
-  }
-
   .status__avatar {
     left: auto;
     right: 10px;
@@ -145,6 +141,19 @@ body.rtl {
     margin-right: 6px;
   }
 
+  .status__action-bar {
+
+    &__counter {
+      margin-right: 0;
+      margin-left: 11px;
+
+      .status__action-bar-button {
+        margin-right: 0;
+        margin-left: 4px;
+      }
+    }
+  }
+
   .status__action-bar-button {
     float: right;
     margin-right: 0;
@@ -285,4 +294,13 @@ body.rtl {
       }
     }
   }
+
+  .public-layout {
+    .header {
+      .nav-button {
+        margin-left: 8px;
+        margin-right: 0;
+      }
+    }
+  }
 }
diff --git a/app/javascript/styles/mastodon/tables.scss b/app/javascript/styles/mastodon/tables.scss
index c2206cf55..adb75afe5 100644
--- a/app/javascript/styles/mastodon/tables.scss
+++ b/app/javascript/styles/mastodon/tables.scss
@@ -90,7 +90,7 @@
 }
 
 samp {
-  font-family: 'mastodon-font-monospace', monospace;
+  font-family: $font-monospace, monospace;
 }
 
 button.table-action-link {
diff --git a/app/javascript/styles/mastodon/variables.scss b/app/javascript/styles/mastodon/variables.scss
index 009f0a3c9..a82c44229 100644
--- a/app/javascript/styles/mastodon/variables.scss
+++ b/app/javascript/styles/mastodon/variables.scss
@@ -48,3 +48,7 @@ $media-modal-media-max-width: 100%;
 $media-modal-media-max-height: 80%;
 
 $no-gap-breakpoint: 415px;
+
+$font-sans-serif: 'mastodon-font-sans-serif' !default;
+$font-display: 'mastodon-font-display' !default;
+$font-monospace: 'mastodon-font-monospace' !default;
diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb
index f40e1fa3e..978289788 100644
--- a/app/lib/activitypub/activity/create.rb
+++ b/app/lib/activitypub/activity/create.rb
@@ -92,7 +92,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
     return if tag['href'].blank?
 
     account = account_from_uri(tag['href'])
-    account = FetchRemoteAccountService.new.call(tag['href'], id: false) if account.nil?
+    account = ::FetchRemoteAccountService.new.call(tag['href'], id: false) if account.nil?
     return if account.nil?
     account.mentions.create(status: status)
   end
diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb
index b59a9f1cd..b10e5dd24 100644
--- a/app/lib/feed_manager.rb
+++ b/app/lib/feed_manager.rb
@@ -40,6 +40,7 @@ class FeedManager
   end
 
   def push_to_list(list, status)
+    return false if status.reply? && status.in_reply_to_account_id != status.account_id
     return false unless add_to_feed(:list, list.id, status)
     trim(:list, list.id)
     PushUpdateWorker.perform_async(list.account_id, status.id, "timeline:list:#{list.id}") if push_update_required?("timeline:list:#{list.id}")
diff --git a/app/lib/user_settings_decorator.rb b/app/lib/user_settings_decorator.rb
index 4bedfd680..e5b168502 100644
--- a/app/lib/user_settings_decorator.rb
+++ b/app/lib/user_settings_decorator.rb
@@ -15,23 +15,24 @@ class UserSettingsDecorator
   private
 
   def process_update
-    user.settings['notification_emails']     = merged_notification_emails if change?('notification_emails')
-    user.settings['interactions']            = merged_interactions if change?('interactions')
-    user.settings['default_privacy']         = default_privacy_preference if change?('setting_default_privacy')
-    user.settings['default_sensitive']       = default_sensitive_preference if change?('setting_default_sensitive')
-    user.settings['default_language']        = default_language_preference if change?('setting_default_language')
-    user.settings['unfollow_modal']          = unfollow_modal_preference if change?('setting_unfollow_modal')
-    user.settings['boost_modal']             = boost_modal_preference if change?('setting_boost_modal')
-    user.settings['favourite_modal']         = favourite_modal_preference if change?('setting_favourite_modal')
-    user.settings['delete_modal']            = delete_modal_preference if change?('setting_delete_modal')
-    user.settings['auto_play_gif']           = auto_play_gif_preference if change?('setting_auto_play_gif')
-    user.settings['display_sensitive_media'] = display_sensitive_media_preference if change?('setting_display_sensitive_media')
-    user.settings['reduce_motion']           = reduce_motion_preference if change?('setting_reduce_motion')
-    user.settings['system_font_ui']          = system_font_ui_preference if change?('setting_system_font_ui')
-    user.settings['noindex']                 = noindex_preference if change?('setting_noindex')
-    user.settings['flavour']                 = flavour_preference if change?('setting_flavour')
-    user.settings['skin']                    = skin_preference if change?('setting_skin')
-    user.settings['hide_network']            = hide_network_preference if change?('setting_hide_network')
+    user.settings['notification_emails'] = merged_notification_emails if change?('notification_emails')
+    user.settings['interactions']        = merged_interactions if change?('interactions')
+    user.settings['default_privacy']     = default_privacy_preference if change?('setting_default_privacy')
+    user.settings['default_sensitive']   = default_sensitive_preference if change?('setting_default_sensitive')
+    user.settings['default_language']    = default_language_preference if change?('setting_default_language')
+    user.settings['unfollow_modal']      = unfollow_modal_preference if change?('setting_unfollow_modal')
+    user.settings['boost_modal']         = boost_modal_preference if change?('setting_boost_modal')
+    user.settings['favourite_modal']     = favourite_modal_preference if change?('setting_favourite_modal')
+    user.settings['delete_modal']        = delete_modal_preference if change?('setting_delete_modal')
+    user.settings['auto_play_gif']       = auto_play_gif_preference if change?('setting_auto_play_gif')
+    user.settings['display_media']       = display_media_preference if change?('setting_display_media')
+    user.settings['expand_spoilers']     = expand_spoilers_preference if change?('setting_expand_spoilers')
+    user.settings['reduce_motion']       = reduce_motion_preference if change?('setting_reduce_motion')
+    user.settings['system_font_ui']      = system_font_ui_preference if change?('setting_system_font_ui')
+    user.settings['noindex']             = noindex_preference if change?('setting_noindex')
+    user.settings['flavour']             = flavour_preference if change?('setting_flavour')
+    user.settings['skin']                = skin_preference if change?('setting_skin')
+    user.settings['hide_network']        = hide_network_preference if change?('setting_hide_network')
   end
 
   def merged_notification_emails
@@ -57,7 +58,7 @@ class UserSettingsDecorator
   def boost_modal_preference
     boolean_cast_setting 'setting_boost_modal'
   end
-  
+
   def favourite_modal_preference
     boolean_cast_setting 'setting_favourite_modal'
   end
@@ -74,8 +75,12 @@ class UserSettingsDecorator
     boolean_cast_setting 'setting_auto_play_gif'
   end
 
-  def display_sensitive_media_preference
-    boolean_cast_setting 'setting_display_sensitive_media'
+  def display_media_preference
+    settings['setting_display_media']
+  end
+
+  def expand_spoilers_preference
+    boolean_cast_setting 'setting_expand_spoilers'
   end
 
   def reduce_motion_preference
diff --git a/app/models/concerns/paginable.rb b/app/models/concerns/paginable.rb
index 66695677e..8863094f7 100644
--- a/app/models/concerns/paginable.rb
+++ b/app/models/concerns/paginable.rb
@@ -19,5 +19,13 @@ module Paginable
       query = query.where(arel_table[:id].gt(min_id)) if min_id.present?
       query
     }
+
+    scope :paginate_by_id, ->(limit, options = {}) {
+      if options[:min_id].present?
+        paginate_by_min_id(limit, options[:min_id]).reverse
+      else
+        paginate_by_max_id(limit, options[:max_id], options[:since_id])
+      end
+    }
   end
 end
diff --git a/app/models/feed.rb b/app/models/feed.rb
index d99f1ffb2..5bce88f25 100644
--- a/app/models/feed.rb
+++ b/app/models/feed.rb
@@ -6,16 +6,20 @@ class Feed
     @id   = id
   end
 
-  def get(limit, max_id = nil, since_id = nil)
-    from_redis(limit, max_id, since_id)
+  def get(limit, max_id = nil, since_id = nil, min_id = nil)
+    from_redis(limit, max_id, since_id, min_id)
   end
 
   protected
 
-  def from_redis(limit, max_id, since_id)
-    max_id     = '+inf' if max_id.blank?
-    since_id   = '-inf' if since_id.blank?
-    unhydrated = redis.zrevrangebyscore(key, "(#{max_id}", "(#{since_id}", limit: [0, limit], with_scores: true).map(&:first).map(&:to_i)
+  def from_redis(limit, max_id, since_id, min_id)
+    if min_id.blank?
+      max_id     = '+inf' if max_id.blank?
+      since_id   = '-inf' if since_id.blank?
+      unhydrated = redis.zrevrangebyscore(key, "(#{max_id}", "(#{since_id}", limit: [0, limit], with_scores: true).map(&:first).map(&:to_i)
+    else
+      unhydrated = redis.zrangebyscore(key, "(#{min_id}", '+inf', limit: [0, limit], with_scores: true).map(&:first).map(&:to_i)
+    end
 
     Status.where(id: unhydrated).cache_ids
   end
diff --git a/app/models/home_feed.rb b/app/models/home_feed.rb
index b943a34ce..ba7564983 100644
--- a/app/models/home_feed.rb
+++ b/app/models/home_feed.rb
@@ -7,9 +7,9 @@ class HomeFeed < Feed
     @account = account
   end
 
-  def get(limit, max_id = nil, since_id = nil)
+  def get(limit, max_id = nil, since_id = nil, min_id = nil)
     if redis.exists("account:#{@account.id}:regeneration")
-      from_database(limit, max_id, since_id)
+      from_database(limit, max_id, since_id, min_id)
     else
       super
     end
@@ -17,9 +17,9 @@ class HomeFeed < Feed
 
   private
 
-  def from_database(limit, max_id, since_id)
+  def from_database(limit, max_id, since_id, min_id)
     Status.as_home_timeline(@account)
-          .paginate_by_max_id(limit, max_id, since_id)
+          .paginate_by_id(limit, max_id: max_id, since_id: since_id, min_id: min_id)
           .reject { |status| FeedManager.instance.filter?(:home, status, @account.id) }
   end
 end
diff --git a/app/models/user.rb b/app/models/user.rb
index 6022a5eb0..b9e18eecd 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -95,8 +95,8 @@ class User < ApplicationRecord
   has_many :session_activations, dependent: :destroy
 
   delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :favourite_modal, :delete_modal,
-           :reduce_motion, :system_font_ui, :noindex, :flavour, :skin, :display_sensitive_media, :hide_network,
-           :default_language, to: :settings, prefix: :setting, allow_nil: false
+           :reduce_motion, :system_font_ui, :noindex, :flavour, :skin, :display_media, :hide_network,
+           :expand_spoilers, :default_language, to: :settings, prefix: :setting, allow_nil: false
 
   attr_reader :invite_code
 
@@ -316,6 +316,14 @@ class User < ApplicationRecord
     super
   end
 
+  def show_all_media?
+    setting_display_media == 'show_all'
+  end
+
+  def hide_all_media?
+    setting_display_media == 'hide_all'
+  end
+
   protected
 
   def send_devise_notification(notification, *args)
diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb
index 0845d0210..ac08a3f1e 100644
--- a/app/serializers/initial_state_serializer.rb
+++ b/app/serializers/initial_state_serializer.rb
@@ -24,15 +24,16 @@ class InitialStateSerializer < ActiveModel::Serializer
     }
 
     if object.current_account
-      store[:me]                      = object.current_account.id.to_s
-      store[:unfollow_modal]          = object.current_account.user.setting_unfollow_modal
-      store[:boost_modal]             = object.current_account.user.setting_boost_modal
-      store[:favourite_modal]         = object.current_account.user.setting_favourite_modal
-      store[:delete_modal]            = object.current_account.user.setting_delete_modal
-      store[:auto_play_gif]           = object.current_account.user.setting_auto_play_gif
-      store[:display_sensitive_media] = object.current_account.user.setting_display_sensitive_media
-      store[:reduce_motion]           = object.current_account.user.setting_reduce_motion
-      store[:is_staff]                = object.current_account.user.staff?
+      store[:me]              = object.current_account.id.to_s
+      store[:unfollow_modal]  = object.current_account.user.setting_unfollow_modal
+      store[:boost_modal]     = object.current_account.user.setting_boost_modal
+      store[:favourite_modal] = object.current_account.user.setting_favourite_modal
+      store[:delete_modal]    = object.current_account.user.setting_delete_modal
+      store[:auto_play_gif]   = object.current_account.user.setting_auto_play_gif
+      store[:display_media]   = object.current_account.user.setting_display_media
+      store[:expand_spoilers] = object.current_account.user.setting_expand_spoilers
+      store[:reduce_motion]   = object.current_account.user.setting_reduce_motion
+      store[:is_staff]        = object.current_account.user.staff?
     end
 
     store
diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml
index 3da270d27..9ceae007d 100644
--- a/app/views/accounts/_header.html.haml
+++ b/app/views/accounts/_header.html.haml
@@ -16,7 +16,7 @@
             = fa_icon('lock') if account.locked?
       .public-account-header__tabs__tabs
         .details-counters
-          .counter{ class: active_nav_class(short_account_url(account)) + active_nav_class(short_account_with_replies_url(account)) + active_nav_class(short_account_media_url(account)) }
+          .counter{ class: active_nav_class(short_account_url(account), short_account_with_replies_url(account), short_account_media_url(account)) }
             = link_to short_account_url(account), class: 'u-url u-uid', title: number_with_delimiter(account.statuses_count) do
               %span.counter-number= number_to_human account.statuses_count, strip_insignificant_zeros: true
               %span.counter-label= t('accounts.posts', count: account.statuses_count)
diff --git a/app/views/accounts/_moved.html.haml b/app/views/accounts/_moved.html.haml
index f99328dbd..7a777bfea 100644
--- a/app/views/accounts/_moved.html.haml
+++ b/app/views/accounts/_moved.html.haml
@@ -6,7 +6,7 @@
     = t('accounts.moved_html', name: content_tag(:bdi, content_tag(:strong, display_name(account, custom_emojify: true), class: :emojify)), new_profile_link: link_to(content_tag(:strong, safe_join(['@', content_tag(:span, moved_to_account.acct)])), TagManager.instance.url_for(moved_to_account), class: 'mention'))
 
   .moved-account-widget__card
-    = link_to TagManager.instance.url_for(moved_to_account), class: 'detailed-status__display-name p-author h-card', target: '_blank', rel: 'noopener' do
+    = link_to TagManager.instance.url_for(moved_to_account), class: 'detailed-status__display-name p-author h-card', target: '_blank', rel: 'me noopener' do
       .detailed-status__display-avatar
         .account__avatar-overlay
           .account__avatar-overlay-base{ style: "background-image: url('#{moved_to_account.avatar.url(:original)}')" }
diff --git a/app/views/admin/reports/_status.html.haml b/app/views/admin/reports/_status.html.haml
index 4d557b071..b3c145120 100644
--- a/app/views/admin/reports/_status.html.haml
+++ b/app/views/admin/reports/_status.html.haml
@@ -14,9 +14,9 @@
     - 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, alt: video.description
+        = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: !current_account&.user&.show_all_media? && status.proper.sensitive? || current_account&.user&.hide_all_media?, width: 610, height: 343, inline: true, alt: video.description
       - 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 }
+        = react_component :media_gallery, height: 343, sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, media: status.proper.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }
 
     .detailed-status__meta
       = link_to TagManager.instance.url_for(status), class: 'detailed-status__datetime', target: stream_link_target, rel: 'noopener' do
diff --git a/app/views/settings/preferences/show.html.haml b/app/views/settings/preferences/show.html.haml
index bb267db8a..751a6e50b 100644
--- a/app/views/settings/preferences/show.html.haml
+++ b/app/views/settings/preferences/show.html.haml
@@ -44,7 +44,8 @@
 
   .fields-group
     = f.input :setting_auto_play_gif, as: :boolean, wrapper: :with_label
-    = f.input :setting_display_sensitive_media, as: :boolean, wrapper: :with_label
+    = f.input :setting_display_media, collection: ['default', 'show_all', 'hide_all'], wrapper: :with_label, include_blank: false, label_method: lambda { |item| safe_join([t("simple_form.labels.defaults.setting_display_media_#{item}"), content_tag(:span, t("simple_form.hints.defaults.setting_display_media_#{item}"), class: 'hint')]) }, required: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
+    = f.input :setting_expand_spoilers, as: :boolean, wrapper: :with_label
     = f.input :setting_reduce_motion, as: :boolean, wrapper: :with_label
     = f.input :setting_system_font_ui, as: :boolean, wrapper: :with_label
 
diff --git a/app/views/stream_entries/_detailed_status.html.haml b/app/views/stream_entries/_detailed_status.html.haml
index d0d9cc5fc..6cedfb337 100644
--- a/app/views/stream_entries/_detailed_status.html.haml
+++ b/app/views/stream_entries/_detailed_status.html.haml
@@ -16,17 +16,17 @@
 
   .status__content.emojify<
     - if status.spoiler_text?
-      %p{ style: 'margin-bottom: 0' }<
+      %p{ :style => ('margin-bottom: 0' unless current_account&.user&.setting_expand_spoilers) }<
         %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}&nbsp;
         %a.status__content__spoiler-link{ href: '#' }= t('statuses.show_more')
-    .e-content{ lang: status.language, style: "display: #{status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }= Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay)
+    .e-content{ lang: status.language, style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }= Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay)
 
   - if !status.media_attachments.empty?
     - if status.media_attachments.first.video?
       - video = status.media_attachments.first
-      = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, width: 670, height: 380, detailed: true, inline: true, alt: video.description
+      = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, width: 670, height: 380, detailed: true, inline: true, alt: video.description
     - 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 || autoplay, 'reduceMotion': current_account&.user&.setting_reduce_motion, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }
+      = react_component :media_gallery, height: 380, sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, standalone: true, 'autoPlayGif': current_account&.user&.setting_auto_play_gif || autoplay, 'reduceMotion': current_account&.user&.setting_reduce_motion, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }
   - elsif status.preview_cards.first
     = react_component :card, 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_cards.first, serializer: REST::PreviewCardSerializer).as_json
 
diff --git a/app/views/stream_entries/_simple_status.html.haml b/app/views/stream_entries/_simple_status.html.haml
index 4484a7e62..5d7e2ad82 100644
--- a/app/views/stream_entries/_simple_status.html.haml
+++ b/app/views/stream_entries/_simple_status.html.haml
@@ -19,18 +19,18 @@
           = fa_icon('lock') if status.account.locked?
   .status__content.emojify<
     - if status.spoiler_text?
-      %p{ style: 'margin-bottom: 0' }<
+      %p{ :style => ('margin-bottom: 0' unless current_account&.user&.setting_expand_spoilers) }<
         %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}&nbsp;
         %a.status__content__spoiler-link{ href: '#' }= t('statuses.show_more')
-    .e-content{ lang: status.language, style: "display: #{status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }<
+    .e-content{ lang: status.language, style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }<
       = Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay)
 
   - unless status.media_attachments.empty?
     - if status.media_attachments.first.video?
       - video = status.media_attachments.first
-      = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, width: 610, height: 343, inline: true, alt: video.description
+      = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_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 || autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }
+      = react_component :media_gallery, height: 343, sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, 'autoPlayGif': current_account&.user&.setting_auto_play_gif || autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }
 
   .status__action-bar
     .status__action-bar__counter