about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--app/controllers/api/web/base_controller.rb9
-rw-r--r--app/controllers/api/web/embeds_controller.rb2
-rw-r--r--app/controllers/api/web/push_subscriptions_controller.rb3
-rw-r--r--app/controllers/api/web/settings_controller.rb2
-rw-r--r--app/controllers/home_controller.rb6
-rw-r--r--app/controllers/settings/profiles_controller.rb6
-rw-r--r--app/controllers/statuses_controller.rb6
-rw-r--r--app/controllers/stream_entries_controller.rb3
-rw-r--r--app/javascript/mastodon/actions/compose.js5
-rw-r--r--app/javascript/mastodon/actions/importer/normalizer.js8
-rw-r--r--app/javascript/mastodon/actions/notifications.js37
-rw-r--r--app/javascript/mastodon/actions/push_notifications/registerer.js10
-rw-r--r--app/javascript/mastodon/actions/settings.js2
-rw-r--r--app/javascript/mastodon/actions/streaming.js1
-rw-r--r--app/javascript/mastodon/actions/timelines.js1
-rw-r--r--app/javascript/mastodon/compare_id.js10
-rw-r--r--app/javascript/mastodon/components/scrollable_list.js32
-rw-r--r--app/javascript/mastodon/components/status.js5
-rw-r--r--app/javascript/mastodon/components/status_action_bar.js4
-rw-r--r--app/javascript/mastodon/features/account/components/header.js14
-rw-r--r--app/javascript/mastodon/features/compose/components/compose_form.js9
-rw-r--r--app/javascript/mastodon/features/compose/containers/compose_form_container.js4
-rw-r--r--app/javascript/mastodon/features/direct_timeline/containers/column_settings_container.js17
-rw-r--r--app/javascript/mastodon/features/direct_timeline/index.js104
-rw-r--r--app/javascript/mastodon/features/domain_blocks/index.js2
-rw-r--r--app/javascript/mastodon/features/getting_started/index.js17
-rw-r--r--app/javascript/mastodon/features/keyboard_shortcuts/index.js4
-rw-r--r--app/javascript/mastodon/features/status/components/action_bar.js4
-rw-r--r--app/javascript/mastodon/features/status/index.js5
-rw-r--r--app/javascript/mastodon/features/ui/components/actions_modal.js2
-rw-r--r--app/javascript/mastodon/features/ui/components/columns_area.js3
-rw-r--r--app/javascript/mastodon/features/ui/index.js9
-rw-r--r--app/javascript/mastodon/features/ui/util/async-components.js4
-rw-r--r--app/javascript/mastodon/locales/ar.json7
-rw-r--r--app/javascript/mastodon/locales/bg.json7
-rw-r--r--app/javascript/mastodon/locales/ca.json7
-rw-r--r--app/javascript/mastodon/locales/de.json7
-rw-r--r--app/javascript/mastodon/locales/defaultMessages.json59
-rw-r--r--app/javascript/mastodon/locales/en.json2
-rw-r--r--app/javascript/mastodon/locales/eo.json19
-rw-r--r--app/javascript/mastodon/locales/es.json7
-rw-r--r--app/javascript/mastodon/locales/fa.json7
-rw-r--r--app/javascript/mastodon/locales/fi.json233
-rw-r--r--app/javascript/mastodon/locales/fr.json11
-rw-r--r--app/javascript/mastodon/locales/gl.json11
-rw-r--r--app/javascript/mastodon/locales/he.json7
-rw-r--r--app/javascript/mastodon/locales/hr.json7
-rw-r--r--app/javascript/mastodon/locales/hu.json7
-rw-r--r--app/javascript/mastodon/locales/hy.json7
-rw-r--r--app/javascript/mastodon/locales/id.json7
-rw-r--r--app/javascript/mastodon/locales/io.json7
-rw-r--r--app/javascript/mastodon/locales/it.json7
-rw-r--r--app/javascript/mastodon/locales/ja.json7
-rw-r--r--app/javascript/mastodon/locales/ko.json7
-rw-r--r--app/javascript/mastodon/locales/nl.json7
-rw-r--r--app/javascript/mastodon/locales/no.json7
-rw-r--r--app/javascript/mastodon/locales/oc.json7
-rw-r--r--app/javascript/mastodon/locales/pl.json6
-rw-r--r--app/javascript/mastodon/locales/pt-BR.json7
-rw-r--r--app/javascript/mastodon/locales/pt.json7
-rw-r--r--app/javascript/mastodon/locales/ru.json7
-rw-r--r--app/javascript/mastodon/locales/sk.json33
-rw-r--r--app/javascript/mastodon/locales/sr-Latn.json7
-rw-r--r--app/javascript/mastodon/locales/sr.json7
-rw-r--r--app/javascript/mastodon/locales/sv.json7
-rw-r--r--app/javascript/mastodon/locales/th.json7
-rw-r--r--app/javascript/mastodon/locales/tr.json7
-rw-r--r--app/javascript/mastodon/locales/uk.json7
-rw-r--r--app/javascript/mastodon/locales/zh-CN.json7
-rw-r--r--app/javascript/mastodon/locales/zh-HK.json13
-rw-r--r--app/javascript/mastodon/locales/zh-TW.json131
-rw-r--r--app/javascript/mastodon/reducers/compose.js7
-rw-r--r--app/javascript/mastodon/reducers/contexts.js39
-rw-r--r--app/javascript/mastodon/reducers/notifications.js12
-rw-r--r--app/javascript/mastodon/reducers/settings.js6
-rw-r--r--app/javascript/mastodon/reducers/timelines.js5
-rw-r--r--app/javascript/styles/mastodon/about.scss64
-rw-r--r--app/javascript/styles/mastodon/accounts.scss90
-rw-r--r--app/javascript/styles/mastodon/admin.scss42
-rw-r--r--app/javascript/styles/mastodon/basics.scss2
-rw-r--r--app/javascript/styles/mastodon/boost.scss6
-rw-r--r--app/javascript/styles/mastodon/compact_header.scss4
-rw-r--r--app/javascript/styles/mastodon/components.scss388
-rw-r--r--app/javascript/styles/mastodon/containers.scss2
-rw-r--r--app/javascript/styles/mastodon/emoji_picker.scss16
-rw-r--r--app/javascript/styles/mastodon/footer.scss2
-rw-r--r--app/javascript/styles/mastodon/forms.scss51
-rw-r--r--app/javascript/styles/mastodon/landing_strip.scss8
-rw-r--r--app/javascript/styles/mastodon/reset.scss5
-rw-r--r--app/javascript/styles/mastodon/stream_entries.scss32
-rw-r--r--app/javascript/styles/mastodon/tables.scss4
-rw-r--r--app/javascript/styles/mastodon/variables.scss7
-rw-r--r--app/lib/activitypub/adapter.rb3
-rw-r--r--app/lib/formatter.rb18
-rw-r--r--app/models/account.rb38
-rw-r--r--app/models/status.rb1
-rw-r--r--app/policies/status_policy.rb2
-rw-r--r--app/serializers/activitypub/actor_serializer.rb17
-rw-r--r--app/serializers/rest/account_serializer.rb10
-rw-r--r--app/services/activitypub/process_account_service.rb19
-rw-r--r--app/services/process_mentions_service.rb4
-rw-r--r--app/services/search_service.rb2
-rw-r--r--app/validators/status_pin_validator.rb2
-rw-r--r--app/views/about/_administration.html.haml19
-rw-r--r--app/views/about/show.html.haml30
-rw-r--r--app/views/accounts/_header.html.haml12
-rw-r--r--app/views/admin/accounts/_card.html.haml17
-rw-r--r--app/views/admin/reports/_account_details.html.haml20
-rw-r--r--app/views/admin/reports/show.html.haml4
-rw-r--r--app/views/invites/_invite.html.haml2
-rw-r--r--app/views/settings/profiles/show.html.haml10
-rw-r--r--app/workers/activitypub/synchronize_featured_collection_worker.rb2
-rw-r--r--config/locales/ar.yml4
-rw-r--r--config/locales/devise.fi.yml91
-rw-r--r--config/locales/devise.sk.yml10
-rw-r--r--config/locales/devise.zh-HK.yml4
-rw-r--r--config/locales/doorkeeper.fi.yml108
-rw-r--r--config/locales/doorkeeper.sk.yml2
-rw-r--r--config/locales/doorkeeper.zh-HK.yml4
-rw-r--r--config/locales/en.yml15
-rw-r--r--config/locales/eo.yml6
-rw-r--r--config/locales/es.yml13
-rw-r--r--config/locales/fi.yml698
-rw-r--r--config/locales/ja.yml106
-rw-r--r--config/locales/pl.yml29
-rw-r--r--config/locales/simple_form.en.yml6
-rw-r--r--config/locales/simple_form.eo.yml4
-rw-r--r--config/locales/simple_form.fi.yml66
-rw-r--r--config/locales/simple_form.pl.yml6
-rw-r--r--config/locales/simple_form.sk.yml16
-rw-r--r--config/locales/simple_form.zh-HK.yml6
-rw-r--r--config/locales/sk.yml27
-rw-r--r--config/locales/zh-HK.yml63
-rw-r--r--db/migrate/20180410204633_add_fields_to_accounts.rb5
-rw-r--r--db/schema.rb5
-rw-r--r--docker-compose.yml4
-rw-r--r--package.json4
-rw-r--r--spec/controllers/stream_entries_controller_spec.rb10
-rw-r--r--spec/models/status_pin_spec.rb31
-rw-r--r--spec/services/activitypub/process_account_service_spec.rb28
-rw-r--r--streaming/index.js81
-rw-r--r--yarn.lock12
142 files changed, 2533 insertions, 1019 deletions
diff --git a/app/controllers/api/web/base_controller.rb b/app/controllers/api/web/base_controller.rb
new file mode 100644
index 000000000..8da549b3a
--- /dev/null
+++ b/app/controllers/api/web/base_controller.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class Api::Web::BaseController < Api::BaseController
+  protect_from_forgery with: :exception
+
+  rescue_from ActionController::InvalidAuthenticityToken do
+    render json: { error: "Can't verify CSRF token authenticity." }, status: 422
+  end
+end
diff --git a/app/controllers/api/web/embeds_controller.rb b/app/controllers/api/web/embeds_controller.rb
index 2ed516161..f2fe74b17 100644
--- a/app/controllers/api/web/embeds_controller.rb
+++ b/app/controllers/api/web/embeds_controller.rb
@@ -1,6 +1,6 @@
 # frozen_string_literal: true
 
-class Api::Web::EmbedsController < Api::BaseController
+class Api::Web::EmbedsController < Api::Web::BaseController
   respond_to :json
 
   before_action :require_user!
diff --git a/app/controllers/api/web/push_subscriptions_controller.rb b/app/controllers/api/web/push_subscriptions_controller.rb
index c611031ab..249e7c186 100644
--- a/app/controllers/api/web/push_subscriptions_controller.rb
+++ b/app/controllers/api/web/push_subscriptions_controller.rb
@@ -1,10 +1,9 @@
 # frozen_string_literal: true
 
-class Api::Web::PushSubscriptionsController < Api::BaseController
+class Api::Web::PushSubscriptionsController < Api::Web::BaseController
   respond_to :json
 
   before_action :require_user!
-  protect_from_forgery with: :exception
 
   def create
     active_session = current_session
diff --git a/app/controllers/api/web/settings_controller.rb b/app/controllers/api/web/settings_controller.rb
index f6739d506..e3178bf48 100644
--- a/app/controllers/api/web/settings_controller.rb
+++ b/app/controllers/api/web/settings_controller.rb
@@ -1,6 +1,6 @@
 # frozen_string_literal: true
 
-class Api::Web::SettingsController < Api::BaseController
+class Api::Web::SettingsController < Api::Web::BaseController
   respond_to :json
 
   before_action :require_user!
diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb
index a8ec0dcc9..6e331dd2d 100644
--- a/app/controllers/home_controller.rb
+++ b/app/controllers/home_controller.rb
@@ -2,7 +2,9 @@
 
 class HomeController < ApplicationController
   before_action :authenticate_user!
+
   before_action :set_pack
+  before_action :set_referrer_policy_header
   before_action :set_initial_state_json
 
   def index
@@ -67,4 +69,8 @@ class HomeController < ApplicationController
       about_path
     end
   end
+
+  def set_referrer_policy_header
+    response.headers['Referrer-Policy'] = 'origin'
+  end
 end
diff --git a/app/controllers/settings/profiles_controller.rb b/app/controllers/settings/profiles_controller.rb
index dadc3d911..2b8330f2e 100644
--- a/app/controllers/settings/profiles_controller.rb
+++ b/app/controllers/settings/profiles_controller.rb
@@ -8,7 +8,9 @@ class Settings::ProfilesController < Settings::BaseController
   obfuscate_filename [:account, :avatar]
   obfuscate_filename [:account, :header]
 
-  def show; end
+  def show
+    @account.build_fields
+  end
 
   def update
     if UpdateAccountService.new.call(@account, account_params)
@@ -22,7 +24,7 @@ class Settings::ProfilesController < Settings::BaseController
   private
 
   def account_params
-    params.require(:account).permit(:display_name, :note, :avatar, :header, :locked)
+    params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, fields_attributes: [:name, :value])
   end
 
   def set_account
diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb
index 17fbaa62c..3237a15b9 100644
--- a/app/controllers/statuses_controller.rb
+++ b/app/controllers/statuses_controller.rb
@@ -13,6 +13,7 @@ class StatusesController < ApplicationController
   before_action :set_link_headers
   before_action :check_account_suspension
   before_action :redirect_to_original, only: [:show]
+  before_action :set_referrer_policy_header, only: [:show]
   before_action :set_cache_headers
 
   def show
@@ -83,4 +84,9 @@ class StatusesController < ApplicationController
   def redirect_to_original
     redirect_to ::TagManager.instance.url_for(@status.reblog) if @status.reblog?
   end
+
+  def set_referrer_policy_header
+    return if @status.public_visibility? || @status.unlisted_visibility?
+    response.headers['Referrer-Policy'] = 'origin'
+  end
 end
diff --git a/app/controllers/stream_entries_controller.rb b/app/controllers/stream_entries_controller.rb
index e2ea45c83..44e9c0bb8 100644
--- a/app/controllers/stream_entries_controller.rb
+++ b/app/controllers/stream_entries_controller.rb
@@ -16,8 +16,7 @@ class StreamEntriesController < ApplicationController
     respond_to do |format|
       format.html do
         use_pack 'public'
-        @ancestors   = @stream_entry.activity.reply? ? cache_collection(@stream_entry.activity.ancestors(current_account), Status) : []
-        @descendants = cache_collection(@stream_entry.activity.descendants(current_account), Status)
+        redirect_to short_account_status_url(params[:account_username], @stream_entry.activity) if @type == 'status'
       end
 
       format.atom do
diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js
index 59aa6f98d..eee9c6928 100644
--- a/app/javascript/mastodon/actions/compose.js
+++ b/app/javascript/mastodon/actions/compose.js
@@ -145,6 +145,8 @@ export function submitCompose() {
       if (response.data.in_reply_to_id === null && response.data.visibility === 'public') {
         insertIfOnline('community');
         insertIfOnline('public');
+      } else if (response.data.visibility === 'direct') {
+        insertIfOnline('direct');
       }
     }).catch(function (error) {
       dispatch(submitComposeFail(error));
@@ -446,11 +448,12 @@ export function changeComposeVisibility(value) {
   };
 };
 
-export function insertEmojiCompose(position, emoji) {
+export function insertEmojiCompose(position, emoji, needsSpace) {
   return {
     type: COMPOSE_EMOJI_INSERT,
     position,
     emoji,
+    needsSpace,
   };
 };
 
diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js
index 1b09f319f..5f1274fab 100644
--- a/app/javascript/mastodon/actions/importer/normalizer.js
+++ b/app/javascript/mastodon/actions/importer/normalizer.js
@@ -10,6 +10,14 @@ export function normalizeAccount(account) {
   account.display_name_html = emojify(escapeTextContentForBrowser(displayName));
   account.note_emojified = emojify(account.note);
 
+  if (account.fields) {
+    account.fields = account.fields.map(pair => ({
+      ...pair,
+      name_emojified: emojify(escapeTextContentForBrowser(pair.name)),
+      value_emojified: emojify(pair.value),
+    }));
+  }
+
   if (account.moved) {
     account.moved = account.moved.id;
   }
diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js
index da77afbe0..7aa070f56 100644
--- a/app/javascript/mastodon/actions/notifications.js
+++ b/app/javascript/mastodon/actions/notifications.js
@@ -9,7 +9,8 @@ import {
 } from './importer';
 import { defineMessages } from 'react-intl';
 
-export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE';
+export const NOTIFICATIONS_UPDATE      = 'NOTIFICATIONS_UPDATE';
+export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP';
 
 export const NOTIFICATIONS_EXPAND_REQUEST = 'NOTIFICATIONS_EXPAND_REQUEST';
 export const NOTIFICATIONS_EXPAND_SUCCESS = 'NOTIFICATIONS_EXPAND_SUCCESS';
@@ -39,21 +40,30 @@ const unescapeHTML = (html) => {
 
 export function updateNotifications(notification, intlMessages, intlLocale) {
   return (dispatch, getState) => {
-    const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true);
-    const playSound = getState().getIn(['settings', 'notifications', 'sounds', notification.type], true);
+    const showInColumn = getState().getIn(['settings', 'notifications', 'shows', notification.type], true);
+    const showAlert    = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true);
+    const playSound    = getState().getIn(['settings', 'notifications', 'sounds', notification.type], true);
 
-    dispatch(importFetchedAccount(notification.account));
-    if (notification.status) {
-      dispatch(importFetchedStatus(notification.status));
-    }
+    if (showInColumn) {
+      dispatch(importFetchedAccount(notification.account));
 
-    dispatch({
-      type: NOTIFICATIONS_UPDATE,
-      notification,
-      meta: playSound ? { sound: 'boop' } : undefined,
-    });
+      if (notification.status) {
+        dispatch(importFetchedStatus(notification.status));
+      }
+
+      dispatch({
+        type: NOTIFICATIONS_UPDATE,
+        notification,
+        meta: playSound ? { sound: 'boop' } : undefined,
+      });
 
-    fetchRelatedRelationships(dispatch, [notification]);
+      fetchRelatedRelationships(dispatch, [notification]);
+    } else if (playSound) {
+      dispatch({
+        type: NOTIFICATIONS_UPDATE_NOOP,
+        meta: { sound: 'boop' },
+      });
+    }
 
     // Desktop notifications
     if (typeof window.Notification !== 'undefined' && showAlert) {
@@ -61,6 +71,7 @@ export function updateNotifications(notification, intlMessages, intlLocale) {
       const body  = (notification.status && notification.status.spoiler_text.length > 0) ? notification.status.spoiler_text : unescapeHTML(notification.status ? notification.status.content : '');
 
       const notify = new Notification(title, { body, icon: notification.account.avatar, tag: notification.id });
+
       notify.addEventListener('click', () => {
         window.focus();
         notify.close();
diff --git a/app/javascript/mastodon/actions/push_notifications/registerer.js b/app/javascript/mastodon/actions/push_notifications/registerer.js
index f17d929a6..60b215f02 100644
--- a/app/javascript/mastodon/actions/push_notifications/registerer.js
+++ b/app/javascript/mastodon/actions/push_notifications/registerer.js
@@ -36,7 +36,7 @@ const subscribe = (registration) =>
 const unsubscribe = ({ registration, subscription }) =>
   subscription ? subscription.unsubscribe().then(() => registration) : registration;
 
-const sendSubscriptionToBackend = (getState, subscription) => {
+const sendSubscriptionToBackend = (subscription) => {
   const params = { subscription };
 
   if (me) {
@@ -46,7 +46,7 @@ const sendSubscriptionToBackend = (getState, subscription) => {
     }
   }
 
-  return api(getState).post('/api/web/push_subscriptions', params).then(response => response.data);
+  return api().post('/api/web/push_subscriptions', params).then(response => response.data);
 };
 
 // Last one checks for payload support: https://web-push-book.gauntface.com/chapter-06/01-non-standards-browsers/#no-payload
@@ -85,13 +85,13 @@ export function register () {
             } else {
               // Something went wrong, try to subscribe again
               return unsubscribe({ registration, subscription }).then(subscribe).then(
-                subscription => sendSubscriptionToBackend(getState, subscription));
+                subscription => sendSubscriptionToBackend(subscription));
             }
           }
 
           // No subscription, try to subscribe
           return subscribe(registration).then(
-            subscription => sendSubscriptionToBackend(getState, subscription));
+            subscription => sendSubscriptionToBackend(subscription));
         })
         .then(subscription => {
           // If we got a PushSubscription (and not a subscription object from the backend)
@@ -134,7 +134,7 @@ export function saveSettings() {
     const alerts = state.get('alerts');
     const data = { alerts };
 
-    api(getState).put(`/api/web/push_subscriptions/${subscription.get('id')}`, {
+    api().put(`/api/web/push_subscriptions/${subscription.get('id')}`, {
       data,
     }).then(() => {
       if (me) {
diff --git a/app/javascript/mastodon/actions/settings.js b/app/javascript/mastodon/actions/settings.js
index 5634a11ef..6bf85e464 100644
--- a/app/javascript/mastodon/actions/settings.js
+++ b/app/javascript/mastodon/actions/settings.js
@@ -24,7 +24,7 @@ const debouncedSave = debounce((dispatch, getState) => {
 
   const data = getState().get('settings').filter((_, path) => path !== 'saved').toJS();
 
-  api(getState).put('/api/web/settings', { data })
+  api().put('/api/web/settings', { data })
     .then(() => dispatch({ type: SETTING_SAVE }))
     .catch(error => dispatch(showAlertForError(error)));
 }, 5000, { trailing: true });
diff --git a/app/javascript/mastodon/actions/streaming.js b/app/javascript/mastodon/actions/streaming.js
index f76510cdb..14215ab6d 100644
--- a/app/javascript/mastodon/actions/streaming.js
+++ b/app/javascript/mastodon/actions/streaming.js
@@ -46,4 +46,5 @@ export const connectCommunityStream = () => connectTimelineStream('community', '
 export const connectMediaStream = () => connectTimelineStream('community', 'public:local');
 export const connectPublicStream = () => connectTimelineStream('public', 'public');
 export const connectHashtagStream = (tag) => connectTimelineStream(`hashtag:${tag}`, `hashtag&tag=${tag}`);
+export const connectDirectStream = () => connectTimelineStream('direct', 'direct');
 export const connectListStream = (id) => connectTimelineStream(`list:${id}`, `list&list=${id}`);
diff --git a/app/javascript/mastodon/actions/timelines.js b/app/javascript/mastodon/actions/timelines.js
index 5be07126d..eca847ee7 100644
--- a/app/javascript/mastodon/actions/timelines.js
+++ b/app/javascript/mastodon/actions/timelines.js
@@ -87,6 +87,7 @@ export function expandTimeline(timelineId, path, params = {}) {
 export const expandHomeTimeline         = ({ maxId } = {}) => expandTimeline('home', '/api/v1/timelines/home', { max_id: maxId });
 export const expandPublicTimeline       = ({ maxId } = {}) => expandTimeline('public', '/api/v1/timelines/public', { max_id: maxId });
 export const expandCommunityTimeline    = ({ maxId } = {}) => expandTimeline('community', '/api/v1/timelines/public', { local: true, max_id: maxId });
+export const expandDirectTimeline       = ({ maxId } = {}) => expandTimeline('direct', '/api/v1/timelines/direct', { max_id: maxId });
 export const expandAccountTimeline      = (accountId, { maxId, withReplies } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies, max_id: maxId });
 export const expandAccountFeaturedTimeline = accountId => expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true });
 export const expandAccountMediaTimeline = (accountId, { maxId } = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true });
diff --git a/app/javascript/mastodon/compare_id.js b/app/javascript/mastodon/compare_id.js
new file mode 100644
index 000000000..aaff66481
--- /dev/null
+++ b/app/javascript/mastodon/compare_id.js
@@ -0,0 +1,10 @@
+export default function compareId(id1, id2) {
+  if (id1 === id2) {
+    return 0;
+  }
+  if (id1.length === id2.length) {
+    return id1 > id2 ? 1 : -1;
+  } else {
+    return id1.length > id2.length ? 1 : -1;
+  }
+}
diff --git a/app/javascript/mastodon/components/scrollable_list.js b/app/javascript/mastodon/components/scrollable_list.js
index ee07106f7..fd6858d05 100644
--- a/app/javascript/mastodon/components/scrollable_list.js
+++ b/app/javascript/mastodon/components/scrollable_list.js
@@ -34,7 +34,7 @@ export default class ScrollableList extends PureComponent {
   };
 
   state = {
-    lastMouseMove: null,
+    fullscreen: null,
   };
 
   intersectionObserverWrapper = new IntersectionObserverWrapper();
@@ -43,7 +43,6 @@ export default class ScrollableList extends PureComponent {
     if (this.node) {
       const { scrollTop, scrollHeight, clientHeight } = this.node;
       const offset = scrollHeight - scrollTop - clientHeight;
-      this._oldScrollPosition = scrollHeight - scrollTop;
 
       if (400 > offset && this.props.onLoadMore && !this.props.isLoading) {
         this.props.onLoadMore();
@@ -59,14 +58,6 @@ export default class ScrollableList extends PureComponent {
     trailing: true,
   });
 
-  handleMouseMove = throttle(() => {
-    this._lastMouseMove = new Date();
-  }, 300);
-
-  handleMouseLeave = () => {
-    this._lastMouseMove = null;
-  }
-
   componentDidMount () {
     this.attachScrollListener();
     this.attachIntersectionObserver();
@@ -76,21 +67,26 @@ export default class ScrollableList extends PureComponent {
     this.handleScroll();
   }
 
-  componentDidUpdate (prevProps) {
+  getSnapshotBeforeUpdate (prevProps) {
     const someItemInserted = React.Children.count(prevProps.children) > 0 &&
       React.Children.count(prevProps.children) < React.Children.count(this.props.children) &&
       this.getFirstChildKey(prevProps) !== this.getFirstChildKey(this.props);
+    if (someItemInserted && this.node.scrollTop > 0) {
+      return this.node.scrollHeight - this.node.scrollTop;
+    } else {
+      return null;
+    }
+  }
 
+  componentDidUpdate (prevProps, prevState, snapshot) {
     // Reset the scroll position when a new child comes in in order not to
     // jerk the scrollbar around if you're already scrolled down the page.
-    if (someItemInserted && this._oldScrollPosition && this.node.scrollTop > 0) {
-      const newScrollTop = this.node.scrollHeight - this._oldScrollPosition;
+    if (snapshot !== null) {
+      const newScrollTop = this.node.scrollHeight - snapshot;
 
       if (this.node.scrollTop !== newScrollTop) {
         this.node.scrollTop = newScrollTop;
       }
-    } else {
-      this._oldScrollPosition = this.node.scrollHeight - this.node.scrollTop;
     }
   }
 
@@ -143,10 +139,6 @@ export default class ScrollableList extends PureComponent {
     this.props.onLoadMore();
   }
 
-  _recentlyMoved () {
-    return this._lastMouseMove !== null && ((new Date()) - this._lastMouseMove < 600);
-  }
-
   render () {
     const { children, scrollKey, trackScroll, shouldUpdateScroll, isLoading, hasMore, prepend, emptyMessage, onLoadMore } = this.props;
     const { fullscreen } = this.state;
@@ -157,7 +149,7 @@ export default class ScrollableList extends PureComponent {
 
     if (isLoading || childrenCount > 0 || !emptyMessage) {
       scrollableArea = (
-        <div className={classNames('scrollable', { fullscreen })} ref={this.setRef} onMouseMove={this.handleMouseMove} onMouseLeave={this.handleMouseLeave}>
+        <div className={classNames('scrollable', { fullscreen })} ref={this.setRef}>
           <div role='feed' className='item-list'>
             {prepend}
 
diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js
index 6129b3f1e..e5f7c9399 100644
--- a/app/javascript/mastodon/components/status.js
+++ b/app/javascript/mastodon/components/status.js
@@ -122,6 +122,10 @@ export default class Status extends ImmutablePureComponent {
     this.props.onMoveDown(this.props.status.get('id'));
   }
 
+  handleHotkeyToggleHidden = () => {
+    this.props.onToggleHidden(this._properStatus());
+  }
+
   _properStatus () {
     const { status } = this.props;
 
@@ -224,6 +228,7 @@ export default class Status extends ImmutablePureComponent {
       openProfile: this.handleHotkeyOpenProfile,
       moveUp: this.handleHotkeyMoveUp,
       moveDown: this.handleHotkeyMoveDown,
+      toggleHidden: this.handleHotkeyToggleHidden,
     };
 
     return (
diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.js
index 10f34b0c7..e58625582 100644
--- a/app/javascript/mastodon/components/status_action_bar.js
+++ b/app/javascript/mastodon/components/status_action_bar.js
@@ -18,6 +18,8 @@ const messages = defineMessages({
   more: { id: 'status.more', defaultMessage: 'More' },
   replyAll: { id: 'status.replyAll', defaultMessage: 'Reply to thread' },
   reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
+  reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost to original audience' },
+  cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
   cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
   favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
   open: { id: 'status.open', defaultMessage: 'Expand this status' },
@@ -150,6 +152,8 @@ export default class StatusActionBar extends ImmutablePureComponent {
     if (status.getIn(['account', 'id']) === me) {
       if (publicStatus) {
         menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
+      } else {
+        menu.push({ text: intl.formatMessage(status.get('reblog') ? messages.reblog_private : messages.cancel_reblog_private), action: this.handleReblogClick });
       }
 
       menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
diff --git a/app/javascript/mastodon/features/account/components/header.js b/app/javascript/mastodon/features/account/components/header.js
index bb7b3b632..bbf886dca 100644
--- a/app/javascript/mastodon/features/account/components/header.js
+++ b/app/javascript/mastodon/features/account/components/header.js
@@ -130,6 +130,7 @@ export default class Header extends ImmutablePureComponent {
 
     const content         = { __html: account.get('note_emojified') };
     const displayNameHtml = { __html: account.get('display_name_html') };
+    const fields          = account.get('fields');
 
     return (
       <div className={classNames('account__header', { inactive: !!account.get('moved') })} style={{ backgroundImage: `url(${account.get('header')})` }}>
@@ -140,6 +141,19 @@ export default class Header extends ImmutablePureComponent {
           <span className='account__header__username'>@{account.get('acct')} {lockedIcon}</span>
           <div className='account__header__content' dangerouslySetInnerHTML={content} />
 
+          {fields.size > 0 && (
+            <table className='account__header__fields'>
+              <tbody>
+                {fields.map((pair, i) => (
+                  <tr key={i}>
+                    <th dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} />
+                    <td dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} />
+                  </tr>
+                ))}
+              </tbody>
+            </table>
+          )}
+
           {info}
           {mutingInfo}
           {actionBtn}
diff --git a/app/javascript/mastodon/features/compose/components/compose_form.js b/app/javascript/mastodon/features/compose/components/compose_form.js
index fe7bb1cb3..39eb02362 100644
--- a/app/javascript/mastodon/features/compose/components/compose_form.js
+++ b/app/javascript/mastodon/features/compose/components/compose_form.js
@@ -19,6 +19,8 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
 import { length } from 'stringz';
 import { countableText } from '../util/counter';
 
+const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d';
+
 const messages = defineMessages({
   placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' },
   spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Write your warning here' },
@@ -144,10 +146,13 @@ export default class ComposeForm extends ImmutablePureComponent {
   }
 
   handleEmojiPick = (data) => {
+    const { text }     = this.props;
     const position     = this.autosuggestTextarea.textarea.selectionStart;
     const emojiChar    = data.native;
-    this._restoreCaret = position + emojiChar.length + 1;
-    this.props.onPickEmoji(position, data);
+    const needsSpace   = data.custom && position > 0 && !allowedAroundShortCode.includes(text[position - 1]);
+
+    this._restoreCaret = position + emojiChar.length + 1 + (needsSpace ? 1 : 0);
+    this.props.onPickEmoji(position, data, needsSpace);
   }
 
   render () {
diff --git a/app/javascript/mastodon/features/compose/containers/compose_form_container.js b/app/javascript/mastodon/features/compose/containers/compose_form_container.js
index ede23d361..c3aa580ee 100644
--- a/app/javascript/mastodon/features/compose/containers/compose_form_container.js
+++ b/app/javascript/mastodon/features/compose/containers/compose_form_container.js
@@ -56,8 +56,8 @@ const mapDispatchToProps = (dispatch) => ({
     dispatch(uploadCompose(files));
   },
 
-  onPickEmoji (position, data) {
-    dispatch(insertEmojiCompose(position, data));
+  onPickEmoji (position, data, needsSpace) {
+    dispatch(insertEmojiCompose(position, data, needsSpace));
   },
 
 });
diff --git a/app/javascript/mastodon/features/direct_timeline/containers/column_settings_container.js b/app/javascript/mastodon/features/direct_timeline/containers/column_settings_container.js
new file mode 100644
index 000000000..1833f69e5
--- /dev/null
+++ b/app/javascript/mastodon/features/direct_timeline/containers/column_settings_container.js
@@ -0,0 +1,17 @@
+import { connect } from 'react-redux';
+import ColumnSettings from '../../community_timeline/components/column_settings';
+import { changeSetting } from '../../../actions/settings';
+
+const mapStateToProps = state => ({
+  settings: state.getIn(['settings', 'direct']),
+});
+
+const mapDispatchToProps = dispatch => ({
+
+  onChange (key, checked) {
+    dispatch(changeSetting(['direct', ...key], checked));
+  },
+
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings);
diff --git a/app/javascript/mastodon/features/direct_timeline/index.js b/app/javascript/mastodon/features/direct_timeline/index.js
new file mode 100644
index 000000000..fda57f69a
--- /dev/null
+++ b/app/javascript/mastodon/features/direct_timeline/index.js
@@ -0,0 +1,104 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
+import StatusListContainer from '../ui/containers/status_list_container';
+import Column from '../../components/column';
+import ColumnHeader from '../../components/column_header';
+import { expandDirectTimeline } from '../../actions/timelines';
+import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
+import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
+import ColumnSettingsContainer from './containers/column_settings_container';
+import { connectDirectStream } from '../../actions/streaming';
+
+const messages = defineMessages({
+  title: { id: 'column.direct', defaultMessage: 'Direct messages' },
+});
+
+const mapStateToProps = state => ({
+  hasUnread: state.getIn(['timelines', 'direct', 'unread']) > 0,
+});
+
+@connect(mapStateToProps)
+@injectIntl
+export default class DirectTimeline extends React.PureComponent {
+
+  static propTypes = {
+    dispatch: PropTypes.func.isRequired,
+    columnId: PropTypes.string,
+    intl: PropTypes.object.isRequired,
+    hasUnread: PropTypes.bool,
+    multiColumn: PropTypes.bool,
+  };
+
+  handlePin = () => {
+    const { columnId, dispatch } = this.props;
+
+    if (columnId) {
+      dispatch(removeColumn(columnId));
+    } else {
+      dispatch(addColumn('DIRECT', {}));
+    }
+  }
+
+  handleMove = (dir) => {
+    const { columnId, dispatch } = this.props;
+    dispatch(moveColumn(columnId, dir));
+  }
+
+  handleHeaderClick = () => {
+    this.column.scrollTop();
+  }
+
+  componentDidMount () {
+    const { dispatch } = this.props;
+
+    dispatch(expandDirectTimeline());
+    this.disconnect = dispatch(connectDirectStream());
+  }
+
+  componentWillUnmount () {
+    if (this.disconnect) {
+      this.disconnect();
+      this.disconnect = null;
+    }
+  }
+
+  setRef = c => {
+    this.column = c;
+  }
+
+  handleLoadMore = maxId => {
+    this.props.dispatch(expandDirectTimeline({ maxId }));
+  }
+
+  render () {
+    const { intl, hasUnread, columnId, multiColumn } = this.props;
+    const pinned = !!columnId;
+
+    return (
+      <Column ref={this.setRef}>
+        <ColumnHeader
+          icon='envelope'
+          active={hasUnread}
+          title={intl.formatMessage(messages.title)}
+          onPin={this.handlePin}
+          onMove={this.handleMove}
+          onClick={this.handleHeaderClick}
+          pinned={pinned}
+          multiColumn={multiColumn}
+        >
+          <ColumnSettingsContainer />
+        </ColumnHeader>
+
+        <StatusListContainer
+          trackScroll={!pinned}
+          scrollKey={`direct_timeline-${columnId}`}
+          timelineId='direct'
+          onLoadMore={this.handleLoadMore}
+          emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any direct messages yet. When you send or receive one, it will show up here." />}
+        />
+      </Column>
+    );
+  }
+
+}
diff --git a/app/javascript/mastodon/features/domain_blocks/index.js b/app/javascript/mastodon/features/domain_blocks/index.js
index b17c47e91..3b29e2a26 100644
--- a/app/javascript/mastodon/features/domain_blocks/index.js
+++ b/app/javascript/mastodon/features/domain_blocks/index.js
@@ -52,7 +52,7 @@ export default class Blocks extends ImmutablePureComponent {
     }
 
     return (
-      <Column icon='ban' heading={intl.formatMessage(messages.heading)}>
+      <Column icon='minus-circle' heading={intl.formatMessage(messages.heading)}>
         <ColumnBackButtonSlim />
         <ScrollableList scrollKey='domain_blocks' onLoadMore={this.handleLoadMore}>
           {domains.map(domain =>
diff --git a/app/javascript/mastodon/features/getting_started/index.js b/app/javascript/mastodon/features/getting_started/index.js
index 1a71cff94..4a249f301 100644
--- a/app/javascript/mastodon/features/getting_started/index.js
+++ b/app/javascript/mastodon/features/getting_started/index.js
@@ -19,6 +19,7 @@ const messages = defineMessages({
   navigation_subheading: { id: 'column_subheading.navigation', defaultMessage: 'Navigation' },
   settings_subheading: { id: 'column_subheading.settings', defaultMessage: 'Settings' },
   community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
+  direct: { id: 'navigation_bar.direct', defaultMessage: 'Direct messages' },
   preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
   follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
   sign_out: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
@@ -98,20 +99,24 @@ export default class GettingStarted extends ImmutablePureComponent {
       }
     }
 
+    if (!multiColumn || !columns.find(item => item.get('id') === 'DIRECT')) {
+      navItems.push(<ColumnLink key='4' icon='envelope' text={intl.formatMessage(messages.direct)} to='/timelines/direct' />);
+    }
+
     navItems.push(
-      <ColumnLink key='4' icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />,
-      <ColumnLink key='5' icon='bars' text={intl.formatMessage(messages.lists)} to='/lists' />
+      <ColumnLink key='5' icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />,
+      <ColumnLink key='6' icon='bars' text={intl.formatMessage(messages.lists)} to='/lists' />
     );
 
     if (myAccount.get('locked')) {
-      navItems.push(<ColumnLink key='6' icon='users' text={intl.formatMessage(messages.follow_requests)} badge={badgeDisplay(unreadFollowRequests, 40)} to='/follow_requests' />);
+      navItems.push(<ColumnLink key='7' icon='users' text={intl.formatMessage(messages.follow_requests)} badge={badgeDisplay(unreadFollowRequests, 40)} to='/follow_requests' />);
     }
 
     if (multiColumn) {
-      navItems.push(<ColumnLink key='7' icon='question' text={intl.formatMessage(messages.keyboard_shortcuts)} to='/keyboard-shortcuts' />);
+      navItems.push(<ColumnLink key='8' icon='question' text={intl.formatMessage(messages.keyboard_shortcuts)} to='/keyboard-shortcuts' />);
     }
 
-    navItems.push(<ColumnLink key='8' icon='book' text={intl.formatMessage(messages.info)} href='/about/more' />);
+    navItems.push(<ColumnLink key='9' icon='book' text={intl.formatMessage(messages.info)} href='/about/more' />);
 
     return (
       <Column icon='asterisk' heading={intl.formatMessage(messages.heading)} hideHeadingOnMobile>
@@ -122,7 +127,7 @@ export default class GettingStarted extends ImmutablePureComponent {
           <ColumnLink icon='thumb-tack' text={intl.formatMessage(messages.pins)} to='/pinned' />
           <ColumnLink icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' />
           <ColumnLink icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' />
-          <ColumnLink icon='ban' text={intl.formatMessage(messages.domain_blocks)} to='/domain_blocks' />
+          <ColumnLink icon='minus-circle' text={intl.formatMessage(messages.domain_blocks)} to='/domain_blocks' />
           <ColumnLink icon='cog' text={intl.formatMessage(messages.preferences)} href='/settings/preferences' />
           <ColumnLink icon='sign-out' text={intl.formatMessage(messages.sign_out)} href='/auth/sign_out' method='delete' />
         </div>
diff --git a/app/javascript/mastodon/features/keyboard_shortcuts/index.js b/app/javascript/mastodon/features/keyboard_shortcuts/index.js
index 8531e03c0..5ae7b34a2 100644
--- a/app/javascript/mastodon/features/keyboard_shortcuts/index.js
+++ b/app/javascript/mastodon/features/keyboard_shortcuts/index.js
@@ -53,6 +53,10 @@ export default class KeyboardShortcuts extends ImmutablePureComponent {
                 <td><FormattedMessage id='keyboard_shortcuts.enter' defaultMessage='to open status' /></td>
               </tr>
               <tr>
+                <td><kbd>x</kbd></td>
+                <td><FormattedMessage id='keyboard_shortcuts.toggle_hidden' defaultMessage='to show/hide text behind CW' /></td>
+              </tr>
+              <tr>
                 <td><kbd>up</kbd></td>
                 <td><FormattedMessage id='keyboard_shortcuts.up' defaultMessage='to move up in the list' /></td>
               </tr>
diff --git a/app/javascript/mastodon/features/status/components/action_bar.js b/app/javascript/mastodon/features/status/components/action_bar.js
index 4aa6b08f2..fc34c8cdc 100644
--- a/app/javascript/mastodon/features/status/components/action_bar.js
+++ b/app/javascript/mastodon/features/status/components/action_bar.js
@@ -12,6 +12,8 @@ const messages = defineMessages({
   mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
   reply: { id: 'status.reply', defaultMessage: 'Reply' },
   reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
+  reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost to original audience' },
+  cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
   cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
   favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
   mute: { id: 'status.mute', defaultMessage: 'Mute @{name}' },
@@ -120,6 +122,8 @@ export default class ActionBar extends React.PureComponent {
     if (me === status.getIn(['account', 'id'])) {
       if (publicStatus) {
         menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
+      } else {
+        menu.push({ text: intl.formatMessage(status.get('reblog') ? messages.reblog_private : messages.cancel_reblog_private), action: this.handleReblogClick });
       }
 
       menu.push(null);
diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js
index 55eff0823..d5af2a459 100644
--- a/app/javascript/mastodon/features/status/index.js
+++ b/app/javascript/mastodon/features/status/index.js
@@ -244,6 +244,10 @@ export default class Status extends ImmutablePureComponent {
     this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
   }
 
+  handleHotkeyToggleHidden = () => {
+    this.handleToggleHidden(this.props.status);
+  }
+
   handleMoveUp = id => {
     const { status, ancestorsIds, descendantsIds } = this.props;
 
@@ -354,6 +358,7 @@ export default class Status extends ImmutablePureComponent {
       boost: this.handleHotkeyBoost,
       mention: this.handleHotkeyMention,
       openProfile: this.handleHotkeyOpenProfile,
+      toggleHidden: this.handleHotkeyToggleHidden,
     };
 
     return (
diff --git a/app/javascript/mastodon/features/ui/components/actions_modal.js b/app/javascript/mastodon/features/ui/components/actions_modal.js
index 79a5a20ef..9792eba5f 100644
--- a/app/javascript/mastodon/features/ui/components/actions_modal.js
+++ b/app/javascript/mastodon/features/ui/components/actions_modal.js
@@ -27,7 +27,7 @@ export default class ActionsModal extends ImmutablePureComponent {
     return (
       <li key={`${text}-${i}`}>
         <a href={href} target='_blank' rel='noopener' onClick={this.props.onClick} data-index={i} className={classNames({ active })}>
-          {icon && <IconButton title={text} icon={icon} role='presentation' tabIndex='-1' />}
+          {icon && <IconButton title={text} icon={icon} role='presentation' tabIndex='-1' inverted />}
           <div>
             <div className={classNames({ 'actions-modal__item-label': !!meta })}>{text}</div>
             <div>{meta}</div>
diff --git a/app/javascript/mastodon/features/ui/components/columns_area.js b/app/javascript/mastodon/features/ui/components/columns_area.js
index 05cdb4e3b..0a62cbbeb 100644
--- a/app/javascript/mastodon/features/ui/components/columns_area.js
+++ b/app/javascript/mastodon/features/ui/components/columns_area.js
@@ -12,7 +12,7 @@ import BundleContainer from '../containers/bundle_container';
 import ColumnLoading from './column_loading';
 import DrawerLoading from './drawer_loading';
 import BundleColumnError from './bundle_column_error';
-import { Compose, Notifications, HomeTimeline, CommunityTimeline, PublicTimeline, HashtagTimeline, FavouritedStatuses, ListTimeline } from '../../ui/util/async-components';
+import { Compose, Notifications, HomeTimeline, CommunityTimeline, PublicTimeline, HashtagTimeline, DirectTimeline, FavouritedStatuses, ListTimeline } from '../../ui/util/async-components';
 
 import detectPassiveEvents from 'detect-passive-events';
 import { scrollRight } from '../../../scroll';
@@ -24,6 +24,7 @@ const componentMap = {
   'PUBLIC': PublicTimeline,
   'COMMUNITY': CommunityTimeline,
   'HASHTAG': HashtagTimeline,
+  'DIRECT': DirectTimeline,
   'FAVOURITES': FavouritedStatuses,
   'LIST': ListTimeline,
 };
diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js
index 8b905fa1d..adca0d617 100644
--- a/app/javascript/mastodon/features/ui/index.js
+++ b/app/javascript/mastodon/features/ui/index.js
@@ -30,6 +30,7 @@ import {
   Following,
   Reblogs,
   Favourites,
+  DirectTimeline,
   HashtagTimeline,
   Notifications,
   FollowRequests,
@@ -79,12 +80,14 @@ const keyMap = {
   goToNotifications: 'g n',
   goToLocal: 'g l',
   goToFederated: 'g t',
+  goToDirect: 'g d',
   goToStart: 'g s',
   goToFavourites: 'g f',
   goToPinned: 'g p',
   goToProfile: 'g u',
   goToBlocked: 'g b',
   goToMuted: 'g m',
+  toggleHidden: 'x',
 };
 
 class SwitchingColumnsArea extends React.PureComponent {
@@ -139,6 +142,7 @@ class SwitchingColumnsArea extends React.PureComponent {
           <WrappedRoute path='/timelines/home' component={HomeTimeline} content={children} />
           <WrappedRoute path='/timelines/public' exact component={PublicTimeline} content={children} />
           <WrappedRoute path='/timelines/public/local' component={CommunityTimeline} content={children} />
+          <WrappedRoute path='/timelines/direct' component={DirectTimeline} content={children} />
           <WrappedRoute path='/timelines/tag/:id' component={HashtagTimeline} content={children} />
           <WrappedRoute path='/timelines/list/:id' component={ListTimeline} content={children} />
 
@@ -385,6 +389,10 @@ export default class UI extends React.PureComponent {
     this.context.router.history.push('/timelines/public');
   }
 
+  handleHotkeyGoToDirect = () => {
+    this.context.router.history.push('/timelines/direct');
+  }
+
   handleHotkeyGoToStart = () => {
     this.context.router.history.push('/getting-started');
   }
@@ -424,6 +432,7 @@ export default class UI extends React.PureComponent {
       goToNotifications: this.handleHotkeyGoToNotifications,
       goToLocal: this.handleHotkeyGoToLocal,
       goToFederated: this.handleHotkeyGoToFederated,
+      goToDirect: this.handleHotkeyGoToDirect,
       goToStart: this.handleHotkeyGoToStart,
       goToFavourites: this.handleHotkeyGoToFavourites,
       goToPinned: this.handleHotkeyGoToPinned,
diff --git a/app/javascript/mastodon/features/ui/util/async-components.js b/app/javascript/mastodon/features/ui/util/async-components.js
index 19957208f..8cf2a6e7d 100644
--- a/app/javascript/mastodon/features/ui/util/async-components.js
+++ b/app/javascript/mastodon/features/ui/util/async-components.js
@@ -26,6 +26,10 @@ export function HashtagTimeline () {
   return import(/* webpackChunkName: "features/hashtag_timeline" */'../../hashtag_timeline');
 }
 
+export function DirectTimeline() {
+  return import(/* webpackChunkName: "features/direct_timeline" */'../../direct_timeline');
+}
+
 export function ListTimeline () {
   return import(/* webpackChunkName: "features/list_timeline" */'../../list_timeline');
 }
diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json
index 34e34411f..24c8a5b54 100644
--- a/app/javascript/mastodon/locales/ar.json
+++ b/app/javascript/mastodon/locales/ar.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "إعادة المحاولة",
   "column.blocks": "الحسابات المحجوبة",
   "column.community": "الخيط العام المحلي",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "المفضلة",
   "column.follow_requests": "طلبات المتابعة",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "رموز",
   "emoji_button.travel": "أماكن و أسفار",
   "empty_column.community": "الخط الزمني المحلي فارغ. أكتب شيئا ما للعامة كبداية !",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "ليس هناك بعدُ أي محتوى ذو علاقة بهذا الوسم.",
   "empty_column.home": "إنك لا تتبع بعد أي شخص إلى حد الآن. زر {public} أو استخدام حقل البحث لكي تبدأ على التعرف على مستخدمين آخرين.",
   "empty_column.home.public_timeline": "الخيط العام",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "هل تود إخفاء الإخطارات القادمة من هذا المستخدم ؟",
   "navigation_bar.blocks": "الحسابات المحجوبة",
   "navigation_bar.community_timeline": "الخيط العام المحلي",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "تعديل الملف الشخصي",
   "navigation_bar.favourites": "المفضلة",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {result} و {results}}",
   "standalone.public_title": "نظرة على ...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "تعذرت ترقية هذا المنشور",
   "status.delete": "إحذف",
+  "status.direct": "Direct message @{name}",
   "status.embed": "إدماج",
   "status.favourite": "أضف إلى المفضلة",
   "status.load_more": "حمّل المزيد",
@@ -252,6 +257,7 @@
   "status.pin": "تدبيس على الملف الشخصي",
   "status.pinned": "تبويق مثبَّت",
   "status.reblog": "رَقِّي",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} رقى",
   "status.reply": "ردّ",
   "status.replyAll": "رُد على الخيط",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "الرئيسية",
   "tabs_bar.local_timeline": "المحلي",
   "tabs_bar.notifications": "الإخطارات",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "سوف تفقد مسودتك إن تركت ماستدون.",
   "upload_area.title": "إسحب ثم أفلت للرفع",
   "upload_button.label": "إضافة وسائط",
diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json
index 9aaff0ddf..25ef6db65 100644
--- a/app/javascript/mastodon/locales/bg.json
+++ b/app/javascript/mastodon/locales/bg.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Try again",
   "column.blocks": "Blocked users",
   "column.community": "Local timeline",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favourites",
   "column.follow_requests": "Follow requests",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Symbols",
   "emoji_button.travel": "Travel & Places",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
   "empty_column.home.public_timeline": "the public timeline",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Blocked users",
   "navigation_bar.community_timeline": "Local timeline",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Редактирай профил",
   "navigation_bar.favourites": "Favourites",
@@ -238,8 +241,10 @@
   "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": "Изтриване",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Предпочитани",
   "status.load_more": "Load more",
@@ -252,6 +257,7 @@
   "status.pin": "Pin on profile",
   "status.pinned": "Pinned toot",
   "status.reblog": "Споделяне",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} сподели",
   "status.reply": "Отговор",
   "status.replyAll": "Reply to thread",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Начало",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Известия",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Drag & drop to upload",
   "upload_button.label": "Добави медия",
diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json
index b7f95a664..6a44808e0 100644
--- a/app/javascript/mastodon/locales/ca.json
+++ b/app/javascript/mastodon/locales/ca.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Torna-ho a provar",
   "column.blocks": "Usuaris blocats",
   "column.community": "Línia de temps local",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favorits",
   "column.follow_requests": "Peticions per seguir-te",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Símbols",
   "emoji_button.travel": "Viatges i Llocs",
   "empty_column.community": "La línia de temps local és buida. Escriu alguna cosa públicament per fer rodar la pilota!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Encara no hi ha res amb aquesta etiqueta.",
   "empty_column.home": "Encara no segueixes ningú. Visita {public} o fes cerca per començar i conèixer altres usuaris.",
   "empty_column.home.public_timeline": "la línia de temps pública",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Amagar notificacions d'aquest usuari?",
   "navigation_bar.blocks": "Usuaris bloquejats",
   "navigation_bar.community_timeline": "Línia de temps Local",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Editar perfil",
   "navigation_bar.favourites": "Favorits",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, un {result} altres {results}}",
   "standalone.public_title": "Una mirada a l'interior ...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Aquesta publicació no pot ser retootejada",
   "status.delete": "Esborrar",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Incrustar",
   "status.favourite": "Favorit",
   "status.load_more": "Carrega més",
@@ -252,6 +257,7 @@
   "status.pin": "Fixat en el perfil",
   "status.pinned": "Toot fixat",
   "status.reblog": "Impuls",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} ha retootejat",
   "status.reply": "Respondre",
   "status.replyAll": "Respondre al tema",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Inici",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notificacions",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "El vostre esborrany es perdrà si sortiu de Mastodon.",
   "upload_area.title": "Arrossega i deixa anar per carregar",
   "upload_button.label": "Afegir multimèdia",
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index a618b853e..69c2ae8d8 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Erneut versuchen",
   "column.blocks": "Blockierte Profile",
   "column.community": "Lokale Zeitleiste",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favoriten",
   "column.follow_requests": "Folgeanfragen",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Symbole",
   "emoji_button.travel": "Reisen und Orte",
   "empty_column.community": "Die lokale Zeitleiste ist leer. Schreibe einen öffentlichen Beitrag, um den Ball ins Rollen zu bringen!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Unter diesem Hashtag gibt es noch nichts.",
   "empty_column.home": "Deine Startseite ist leer! Besuche {public} oder nutze die Suche, um loszulegen und andere Leute zu finden.",
   "empty_column.home.public_timeline": "die öffentliche Zeitleiste",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Benachrichtigungen von diesem Account verbergen?",
   "navigation_bar.blocks": "Blockierte Profile",
   "navigation_bar.community_timeline": "Lokale Zeitleiste",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Profil bearbeiten",
   "navigation_bar.favourites": "Favoriten",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {Ergebnis} other {Ergebnisse}}",
   "standalone.public_title": "Ein kleiner Einblick …",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Dieser Beitrag kann nicht geteilt werden",
   "status.delete": "Löschen",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Einbetten",
   "status.favourite": "Favorisieren",
   "status.load_more": "Weitere laden",
@@ -252,6 +257,7 @@
   "status.pin": "Im Profil anheften",
   "status.pinned": "Pinned toot",
   "status.reblog": "Teilen",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} teilte",
   "status.reply": "Antworten",
   "status.replyAll": "Auf Thread antworten",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Startseite",
   "tabs_bar.local_timeline": "Lokal",
   "tabs_bar.notifications": "Mitteilungen",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Zum Hochladen hereinziehen",
   "upload_button.label": "Mediendatei hinzufügen",
diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json
index 6f81db13e..b400349d2 100644
--- a/app/javascript/mastodon/locales/defaultMessages.json
+++ b/app/javascript/mastodon/locales/defaultMessages.json
@@ -121,6 +121,15 @@
         "id": "status.load_more"
       }
     ],
+    "path": "app/javascript/mastodon/components/load_gap.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Load more",
+        "id": "status.load_more"
+      }
+    ],
     "path": "app/javascript/mastodon/components/load_more.json"
   },
   {
@@ -234,6 +243,14 @@
         "id": "status.reblog"
       },
       {
+        "defaultMessage": "Boost to original audience",
+        "id": "status.reblog_private"
+      },
+      {
+        "defaultMessage": "Unboost",
+        "id": "status.cancel_reblog_private"
+      },
+      {
         "defaultMessage": "This post cannot be boosted",
         "id": "status.cannot_reblog"
       },
@@ -891,6 +908,19 @@
   {
     "descriptors": [
       {
+        "defaultMessage": "Direct messages",
+        "id": "column.direct"
+      },
+      {
+        "defaultMessage": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+        "id": "empty_column.direct"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/direct_timeline/index.json"
+  },
+  {
+    "descriptors": [
+      {
         "defaultMessage": "Hidden domains",
         "id": "column.domain_blocks"
       },
@@ -963,6 +993,10 @@
         "id": "navigation_bar.community_timeline"
       },
       {
+        "defaultMessage": "Direct messages",
+        "id": "navigation_bar.direct"
+      },
+      {
         "defaultMessage": "Preferences",
         "id": "navigation_bar.preferences"
       },
@@ -1115,6 +1149,10 @@
         "id": "keyboard_shortcuts.enter"
       },
       {
+        "defaultMessage": "to show/hide text behind CW",
+        "id": "keyboard_shortcuts.toggle_hidden"
+      },
+      {
         "defaultMessage": "to move up in the list",
         "id": "keyboard_shortcuts.up"
       },
@@ -1391,6 +1429,14 @@
         "id": "status.reblog"
       },
       {
+        "defaultMessage": "Boost to original audience",
+        "id": "status.reblog_private"
+      },
+      {
+        "defaultMessage": "Unboost",
+        "id": "status.cancel_reblog_private"
+      },
+      {
         "defaultMessage": "This post cannot be boosted",
         "id": "status.cannot_reblog"
       },
@@ -1819,18 +1865,5 @@
       }
     ],
     "path": "app/javascript/mastodon/features/video/index.json"
-  },
-  {
-    "descriptors": [
-      {
-        "defaultMessage": "Oops!",
-        "id": "alert.unexpected.title"
-      },
-      {
-        "defaultMessage": "An unexpected error occurred.",
-        "id": "alert.unexpected.message"
-      }
-    ],
-    "path": "app/javascript/mastodon/middleware/errors.json"
   }
 ]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index 4802ddfd1..ad6f3b712 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -245,6 +245,7 @@
   "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
   "standalone.public_title": "A look inside...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Delete",
   "status.direct": "Direct message @{name}",
@@ -260,6 +261,7 @@
   "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.reply": "Reply",
   "status.replyAll": "Reply to thread",
diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
index 6dee6e544..e51163971 100644
--- a/app/javascript/mastodon/locales/eo.json
+++ b/app/javascript/mastodon/locales/eo.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Bonvolu reprovi",
   "column.blocks": "Blokitaj uzantoj",
   "column.community": "Loka tempolinio",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Stelumoj",
   "column.follow_requests": "Petoj de sekvado",
@@ -60,7 +61,7 @@
   "column_subheading.settings": "Agordado",
   "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.",
   "compose_form.hashtag_warning": "Ĉi tiu mesaĝo ne estos listigita per ajna kradvorto. Nur publikaj mesaĝoj estas serĉeblaj per kradvortoj.",
-  "compose_form.lock_disclaimer": "Via konta ne estas {locked}. Iu ajn povas sekvi vin por vidi viajn mesaĝojn nur por sekvantoj.",
+  "compose_form.lock_disclaimer": "Via konta ne estas {locked}. Iu ajn povas sekvi vin por vidi viajn mesaĝojn, kiuj estas nur por sekvantoj.",
   "compose_form.lock_disclaimer.lock": "ŝlosita",
   "compose_form.placeholder": "Pri kio vi pensas?",
   "compose_form.publish": "Hup",
@@ -100,9 +101,10 @@
   "emoji_button.symbols": "Simboloj",
   "emoji_button.travel": "Vojaĝoj kaj lokoj",
   "empty_column.community": "La loka tempolinio estas malplena. Skribu ion por plenigi ĝin!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Ankoraŭ estas nenio per ĉi tiu kradvorto.",
   "empty_column.home": "Via hejma tempolinio estas malplena! Vizitu {public} aŭ uzu la serĉilon por renkonti aliajn uzantojn.",
-  "empty_column.home.public_timeline": "la publika tempolinio",
+  "empty_column.home.public_timeline": "la publikan tempolinion",
   "empty_column.list": "Ankoraŭ estas nenio en ĉi tiu listo. Kiam membroj de ĉi tiu listo afiŝos novajn mesaĝojn, ili aperos ĉi tie.",
   "empty_column.notifications": "Vi ankoraŭ ne havas sciigojn. Interagu kun aliaj por komenci konversacion.",
   "empty_column.public": "Estas nenio ĉi tie! Publike skribu ion, aŭ mane sekvu uzantojn de aliaj nodoj por plenigi la publikan tempolinion",
@@ -150,10 +152,11 @@
   "loading_indicator.label": "Ŝargado…",
   "media_gallery.toggle_visible": "Baskuligi videblecon",
   "missing_indicator.label": "Ne trovita",
-  "missing_indicator.sublabel": "Ĉi tiu rimedo ne estis trovita",
-  "mute_modal.hide_notifications": "Ĉu kaŝi sciigojn el ĉi tiu uzanto?",
+  "missing_indicator.sublabel": "Ĉi tiu elemento ne estis trovita",
+  "mute_modal.hide_notifications": "Ĉu vi volas kaŝi la sciigojn el ĉi tiu uzanto?",
   "navigation_bar.blocks": "Blokitaj uzantoj",
   "navigation_bar.community_timeline": "Loka tempolinio",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Redakti profilon",
   "navigation_bar.favourites": "Stelumoj",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {rezulto} other {rezultoj}}",
   "standalone.public_title": "Enrigardo…",
   "status.block": "Bloki @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Ĉi tiu mesaĝo ne diskonigeblas",
   "status.delete": "Forigi",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Enkorpigi",
   "status.favourite": "Stelumi",
   "status.load_more": "Ŝargi pli",
@@ -248,10 +253,11 @@
   "status.more": "Pli",
   "status.mute": "Silentigi @{name}",
   "status.mute_conversation": "Silentigi konversacion",
-  "status.open": "Grandigi ĉi tiun mesaĝon",
-  "status.pin": "Alpingli en la profilo",
+  "status.open": "Grandigi",
+  "status.pin": "Alpingli profile",
   "status.pinned": "Alpinglita mesaĝo",
   "status.reblog": "Diskonigi",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} diskonigis",
   "status.reply": "Respondi",
   "status.replyAll": "Respondi al la fadeno",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Hejmo",
   "tabs_bar.local_timeline": "Loka tempolinio",
   "tabs_bar.notifications": "Sciigoj",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Via malneto perdiĝos se vi eliras de Mastodon.",
   "upload_area.title": "Altreni kaj lasi por alŝuti",
   "upload_button.label": "Aldoni aŭdovidaĵon",
diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json
index 6f9c06c5f..61ea0588d 100644
--- a/app/javascript/mastodon/locales/es.json
+++ b/app/javascript/mastodon/locales/es.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Inténtalo de nuevo",
   "column.blocks": "Usuarios bloqueados",
   "column.community": "Línea de tiempo local",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favoritos",
   "column.follow_requests": "Solicitudes de seguimiento",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Símbolos",
   "emoji_button.travel": "Viajes y lugares",
   "empty_column.community": "La línea de tiempo local está vacía. ¡Escribe algo para empezar la fiesta!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "No hay nada en este hashtag aún.",
   "empty_column.home": "No estás siguiendo a nadie aún. Visita {public} o haz búsquedas para empezar y conocer gente nueva.",
   "empty_column.home.public_timeline": "la línea de tiempo pública",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Ocultar notificaciones de este usuario?",
   "navigation_bar.blocks": "Usuarios bloqueados",
   "navigation_bar.community_timeline": "Historia local",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Editar perfil",
   "navigation_bar.favourites": "Favoritos",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}",
   "standalone.public_title": "Un pequeño vistazo...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Este toot no puede retootearse",
   "status.delete": "Borrar",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Incrustado",
   "status.favourite": "Favorito",
   "status.load_more": "Cargar más",
@@ -252,6 +257,7 @@
   "status.pin": "Fijar",
   "status.pinned": "Toot fijado",
   "status.reblog": "Retootear",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "Retooteado por {name}",
   "status.reply": "Responder",
   "status.replyAll": "Responder al hilo",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Inicio",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notificaciones",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Tu borrador se perderá si sales de Mastodon.",
   "upload_area.title": "Arrastra y suelta para subir",
   "upload_button.label": "Subir multimedia",
diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json
index 61cdcd00a..cfe93007d 100644
--- a/app/javascript/mastodon/locales/fa.json
+++ b/app/javascript/mastodon/locales/fa.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "تلاش دوباره",
   "column.blocks": "کاربران مسدودشده",
   "column.community": "نوشته‌های محلی",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "پسندیده‌ها",
   "column.follow_requests": "درخواست‌های پیگیری",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "نمادها",
   "emoji_button.travel": "سفر و مکان",
   "empty_column.community": "فهرست نوشته‌های محلی خالی است. چیزی بنویسید تا چرخش بچرخد!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "هنوز هیچ چیزی با این هشتگ نیست.",
   "empty_column.home": "شما هنوز پیگیر کسی نیستید. {public} را ببینید یا چیزی را جستجو کنید تا کاربران دیگر را ببینید.",
   "empty_column.home.public_timeline": "فهرست نوشته‌های همه‌جا",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "اعلان‌های این کاربر پنهان شود؟",
   "navigation_bar.blocks": "کاربران مسدودشده",
   "navigation_bar.community_timeline": "نوشته‌های محلی",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "ویرایش نمایه",
   "navigation_bar.favourites": "پسندیده‌ها",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {نتیجه} other {نتیجه}}",
   "standalone.public_title": "نگاهی به کاربران این سرور...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "این نوشته را نمی‌شود بازبوقید",
   "status.delete": "پاک‌کردن",
+  "status.direct": "Direct message @{name}",
   "status.embed": "جاگذاری",
   "status.favourite": "پسندیدن",
   "status.load_more": "بیشتر نشان بده",
@@ -252,6 +257,7 @@
   "status.pin": "نوشتهٔ ثابت نمایه",
   "status.pinned": "Pinned toot",
   "status.reblog": "بازبوقیدن",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "‫{name}‬ بازبوقید",
   "status.reply": "پاسخ",
   "status.replyAll": "به نوشته پاسخ دهید",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "خانه",
   "tabs_bar.local_timeline": "محلی",
   "tabs_bar.notifications": "اعلان‌ها",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "اگر از ماستدون خارج شوید پیش‌نویس شما پاک خواهد شد.",
   "upload_area.title": "برای بارگذاری به این‌جا بکشید",
   "upload_button.label": "افزودن تصویر",
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
index f4be80514..1677c3c6c 100644
--- a/app/javascript/mastodon/locales/fi.json
+++ b/app/javascript/mastodon/locales/fi.json
@@ -2,7 +2,7 @@
   "account.block": "Estä @{name}",
   "account.block_domain": "Piilota kaikki sisältö verkkotunnuksesta {domain}",
   "account.blocked": "Estetty",
-  "account.direct": "Direct Message @{name}",
+  "account.direct": "Direct message @{name}",
   "account.disclaimer_full": "Alla olevat käyttäjän profiilitiedot saattavat olla epätäydellisiä.",
   "account.domain_blocked": "Verkko-osoite piilotettu",
   "account.edit_profile": "Muokkaa",
@@ -17,37 +17,38 @@
   "account.mute": "Mykistä @{name}",
   "account.mute_notifications": "Mykistä ilmoitukset käyttäjältä @{name}",
   "account.muted": "Mykistetty",
-  "account.posts": "Töötit",
-  "account.posts_with_replies": "Töötit ja vastaukset",
-  "account.report": "Report @{name}",
-  "account.requested": "Odottaa hyväksyntää. Klikkaa peruuttaaksesi seurauspyynnön",
+  "account.posts": "Tuuttaukset",
+  "account.posts_with_replies": "Tuuttaukset ja vastaukset",
+  "account.report": "Raportoi @{name}",
+  "account.requested": "Odottaa hyväksyntää. Peruuta seuraamispyyntö klikkaamalla",
   "account.share": "Jaa käyttäjän @{name} profiili",
-  "account.show_reblogs": "Näytä boostaukset käyttäjältä @{name}",
+  "account.show_reblogs": "Näytä buustaukset käyttäjältä @{name}",
   "account.unblock": "Salli @{name}",
   "account.unblock_domain": "Näytä {domain}",
   "account.unfollow": "Lakkaa seuraamasta",
-  "account.unmute": "Poista mykistys käyttäjältä @{name}",
+  "account.unmute": "Poista käyttäjän @{name} mykistys",
   "account.unmute_notifications": "Poista mykistys käyttäjän @{name} ilmoituksilta",
   "account.view_full_profile": "Näytä koko profiili",
   "alert.unexpected.message": "An unexpected error occurred.",
   "alert.unexpected.title": "Oops!",
-  "boost_modal.combo": "Voit painaa näppäimiä {combo} ohittaaksesi tämän ensi kerralla",
-  "bundle_column_error.body": "Jokin meni vikaan tätä komponenttia ladatessa.",
+  "boost_modal.combo": "Ensi kerralla voit ohittaa tämän painamalla {combo}",
+  "bundle_column_error.body": "Jokin meni vikaan komponenttia ladattaessa.",
   "bundle_column_error.retry": "Yritä uudestaan",
-  "bundle_column_error.title": "Network error",
+  "bundle_column_error.title": "Verkkovirhe",
   "bundle_modal_error.close": "Sulje",
-  "bundle_modal_error.message": "Jokin meni vikaan tätä komponenttia ladatessa.",
+  "bundle_modal_error.message": "Jokin meni vikaan komponenttia ladattaessa.",
   "bundle_modal_error.retry": "Yritä uudestaan",
   "column.blocks": "Estetyt käyttäjät",
   "column.community": "Paikallinen aikajana",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Suosikit",
-  "column.follow_requests": "Seurauspyynnöt",
+  "column.follow_requests": "Seuraamispyynnöt",
   "column.home": "Koti",
   "column.lists": "Listat",
   "column.mutes": "Mykistetyt käyttäjät",
   "column.notifications": "Ilmoitukset",
-  "column.pins": "Pinned toot",
+  "column.pins": "Kiinnitetty tuuttaus",
   "column.public": "Yleinen aikajana",
   "column_back_button.label": "Takaisin",
   "column_header.hide_settings": "Piilota asetukset",
@@ -59,32 +60,32 @@
   "column_subheading.navigation": "Navigaatio",
   "column_subheading.settings": "Asetukset",
   "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.",
-  "compose_form.hashtag_warning": "Tämä töötti ei tule näkymään hashtag-hauissa, koska se ei näy julkisilla aikajanoilla. Vain julkisia tööttejä voi hakea hashtageilla.",
-  "compose_form.lock_disclaimer": "Tilisi ei ole {locked}. Kuka tahansa voi seurata tiliäsi ja nähdä vain seuraajille -postauksesi.",
+  "compose_form.hashtag_warning": "Tämä tuuttaus ei näy hashtag-hauissa, koska se on listaamaton. Hashtagien avulla voi hakea vain julkisia tuuttauksia.",
+  "compose_form.lock_disclaimer": "Tilisi ei ole {locked}. Kuka tahansa voi seurata tiliäsi ja nähdä vain seuraajille rajaamasi julkaisut.",
   "compose_form.lock_disclaimer.lock": "lukittu",
-  "compose_form.placeholder": "Mitä sinulla on mielessä?",
-  "compose_form.publish": "Toot",
+  "compose_form.placeholder": "Mitä mietit?",
+  "compose_form.publish": "Tuuttaa",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.sensitive.marked": "Media on merkitty arkaluontoiseksi",
   "compose_form.sensitive.unmarked": "Mediaa ei ole merkitty arkaluontoiseksi",
   "compose_form.spoiler.marked": "Teksti on piilotettu varoituksen taakse",
   "compose_form.spoiler.unmarked": "Teksti ei ole piilotettu",
-  "compose_form.spoiler_placeholder": "Content warning",
+  "compose_form.spoiler_placeholder": "Sisältövaroitus",
   "confirmation_modal.cancel": "Peruuta",
   "confirmations.block.confirm": "Estä",
-  "confirmations.block.message": "Oletko varma, että haluat estää käyttäjän {name}?",
-  "confirmations.delete.confirm": "Delete",
-  "confirmations.delete.message": "Oletko varma, että haluat poistaa tämän statuspäivityksen?",
-  "confirmations.delete_list.confirm": "Delete",
-  "confirmations.delete_list.message": "Oletko varma, että haluat poistaa tämän listan pysyvästi?",
+  "confirmations.block.message": "Haluatko varmasti estää käyttäjän {name}?",
+  "confirmations.delete.confirm": "Poista",
+  "confirmations.delete.message": "Haluatko varmasti poistaa tämän tilapäivityksen?",
+  "confirmations.delete_list.confirm": "Poista",
+  "confirmations.delete_list.message": "Haluatko varmasti poistaa tämän listan kokonaan?",
   "confirmations.domain_block.confirm": "Piilota koko verkko-osoite",
-  "confirmations.domain_block.message": "Oletko aivan oikeasti varma että haluat estää koko verkko-osoitteen {domain}? Useimmissa tapauksissa muutamat kohdistetut estot ja mykistykset ovat riittäviä ja suositeltavampia.",
+  "confirmations.domain_block.message": "Haluatko aivan varmasti estää koko verkko-osoitteen {domain}? Useimmiten jokunen kohdistettu esto ja mykistys riittää, ja se on suositeltavampi tapa toimia.",
   "confirmations.mute.confirm": "Mykistä",
-  "confirmations.mute.message": "Oletko varma että haluat mykistää käyttäjän {name}?",
+  "confirmations.mute.message": "Haluatko varmasti mykistää käyttäjän {name}?",
   "confirmations.unfollow.confirm": "Lakkaa seuraamasta",
-  "confirmations.unfollow.message": "Oletko varma, että haluat lakata seuraamasta käyttäjää {name}?",
-  "embed.instructions": "Upota tämä statuspäivitys sivullesi kopioimalla alla oleva koodi.",
-  "embed.preview": "Tältä se tulee näyttämään:",
+  "confirmations.unfollow.message": "Haluatko varmasti lakata seuraamasta käyttäjää {name}?",
+  "embed.instructions": "Upota statuspäivitys sivullesi kopioimalla alla oleva koodi.",
+  "embed.preview": "Se tulee näyttämään tältä:",
   "emoji_button.activity": "Aktiviteetit",
   "emoji_button.custom": "Mukautetut",
   "emoji_button.flags": "Liput",
@@ -92,154 +93,158 @@
   "emoji_button.label": "Lisää emoji",
   "emoji_button.nature": "Luonto",
   "emoji_button.not_found": "Ei emojeja!! (╯°□°)╯︵ ┻━┻",
-  "emoji_button.objects": "Objektit",
+  "emoji_button.objects": "Esineet",
   "emoji_button.people": "Ihmiset",
   "emoji_button.recent": "Usein käytetyt",
   "emoji_button.search": "Etsi...",
   "emoji_button.search_results": "Hakutulokset",
   "emoji_button.symbols": "Symbolit",
   "emoji_button.travel": "Matkailu",
-  "empty_column.community": "Paikallinen aikajana on tyhjä. Kirjoita jotain julkista saadaksesi pyörät pyörimään!",
-  "empty_column.hashtag": "Tässä hashtagissa ei ole vielä mitään.",
-  "empty_column.home": "Kotiaikajanasi on tyhjä! Käy vierailemassa {public}ssa tai käytä hakutoimintoa aloittaaksesi ja tavataksesi muita käyttäjiä.",
+  "empty_column.community": "Paikallinen aikajana on tyhjä. Homma lähtee käyntiin, kun kirjoitat jotain julkista!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.hashtag": "Tällä hashtagilla ei ole vielä mitään.",
+  "empty_column.home": "Kotiaikajanasi on tyhjä! {public} ja hakutoiminto auttavat alkuun ja kohtaamaan muita käyttäjiä.",
   "empty_column.home.public_timeline": "yleinen aikajana",
-  "empty_column.list": "Tämä lista on vielä tyhjä. Kun listan jäsenet julkaisevat statuspäivityksiä, ne näkyvät tässä.",
-  "empty_column.notifications": "Sinulle ei ole vielä ilmoituksia. Juttele muille aloittaaksesi keskustelun.",
-  "empty_column.public": "Täällä ei ole mitään! Kirjoita jotain julkisesti, tai käy manuaalisesti seuraamassa käyttäjiä muista instansseista saadaksesi sisältöä",
+  "empty_column.list": "Lista on vielä tyhjä. Listan jäsenten julkaisemat tilapäivitykset tulevat tähän näkyviin.",
+  "empty_column.notifications": "Sinulle ei ole vielä ilmoituksia. Aloita keskustelu juttelemalla muille.",
+  "empty_column.public": "Täällä ei ole mitään! Saat sisältöä, kun kirjoitat jotain julkisesti tai käyt manuaalisesti seuraamassa muiden instanssien käyttäjiä",
   "follow_request.authorize": "Valtuuta",
   "follow_request.reject": "Hylkää",
   "getting_started.appsshort": "Sovellukset",
   "getting_started.faq": "FAQ",
   "getting_started.heading": "Aloitus",
-  "getting_started.open_source_notice": "Mastodon on avoimen lähdekoodin ohjelma. Voit avustaa tai raportoida ongelmia GitHub palvelussa {github}.",
+  "getting_started.open_source_notice": "Mastodon on avoimen lähdekoodin ohjelma. Voit avustaa tai raportoida ongelmia GitHubissa: {github}.",
   "getting_started.userguide": "Käyttöopas",
-  "home.column_settings.advanced": "Tarkemmat asetukset",
+  "home.column_settings.advanced": "Lisäasetukset",
   "home.column_settings.basic": "Perusasetukset",
-  "home.column_settings.filter_regex": "Suodata säännöllisten lauseiden avulla",
+  "home.column_settings.filter_regex": "Suodata säännöllisillä lausekkeilla",
   "home.column_settings.show_reblogs": "Näytä buustaukset",
   "home.column_settings.show_replies": "Näytä vastaukset",
   "home.settings": "Sarakeasetukset",
-  "keyboard_shortcuts.back": "liikkuaksesi taaksepäin",
-  "keyboard_shortcuts.boost": "buustataksesi",
-  "keyboard_shortcuts.column": "keskittääksesi statuspäivitykseen yhdessä sarakkeista",
-  "keyboard_shortcuts.compose": "aktivoidaksesi tekstinkirjoitusalueen",
-  "keyboard_shortcuts.description": "Description",
-  "keyboard_shortcuts.down": "liikkuaksesi listassa alaspäin",
-  "keyboard_shortcuts.enter": "to open status",
-  "keyboard_shortcuts.favourite": "tykätäksesi",
-  "keyboard_shortcuts.heading": "Näppäinoikotiet",
+  "keyboard_shortcuts.back": "liiku taaksepäin",
+  "keyboard_shortcuts.boost": "buustaa",
+  "keyboard_shortcuts.column": "siirrä fokus tietyn sarakkeen tilapäivitykseen",
+  "keyboard_shortcuts.compose": "siirry tekstinsyöttöön",
+  "keyboard_shortcuts.description": "Kuvaus",
+  "keyboard_shortcuts.down": "siirry listassa alaspäin",
+  "keyboard_shortcuts.enter": "avaa tilapäivitys",
+  "keyboard_shortcuts.favourite": "tykkää",
+  "keyboard_shortcuts.heading": "Näppäinkomennot",
   "keyboard_shortcuts.hotkey": "Pikanäppäin",
-  "keyboard_shortcuts.legend": "näyttääksesi tämän selitteen",
-  "keyboard_shortcuts.mention": "mainitaksesi julkaisijan",
-  "keyboard_shortcuts.reply": "vastataksesi",
-  "keyboard_shortcuts.search": "aktivoidaksesi hakukentän",
-  "keyboard_shortcuts.toot": "aloittaaksesi uuden töötin kirjoittamisen",
-  "keyboard_shortcuts.unfocus": "poistaaksesi aktivoinnin tekstikentästä/hakukentästä",
-  "keyboard_shortcuts.up": "liikkuaksesi listassa ylöspäin",
+  "keyboard_shortcuts.legend": "näytä tämä selite",
+  "keyboard_shortcuts.mention": "mainitse julkaisija",
+  "keyboard_shortcuts.reply": "vastaa",
+  "keyboard_shortcuts.search": "siirry hakukenttään",
+  "keyboard_shortcuts.toot": "ala kirjoittaa uutta tuuttausta",
+  "keyboard_shortcuts.unfocus": "siirry pois tekstikentästä tai hakukentästä",
+  "keyboard_shortcuts.up": "siirry listassa ylöspäin",
   "lightbox.close": "Sulje",
   "lightbox.next": "Seuraava",
   "lightbox.previous": "Edellinen",
   "lists.account.add": "Lisää listaan",
-  "lists.account.remove": "Poista listalta",
-  "lists.delete": "Delete list",
+  "lists.account.remove": "Poista listasta",
+  "lists.delete": "Poista lista",
   "lists.edit": "Muokkaa listaa",
   "lists.new.create": "Lisää lista",
-  "lists.new.title_placeholder": "Uuden listan otsikko",
-  "lists.search": "Etsi seuraamiesi henkilöiden joukosta",
+  "lists.new.title_placeholder": "Uuden listan nimi",
+  "lists.search": "Etsi seuraamistasi henkilöistä",
   "lists.subheading": "Omat listat",
   "loading_indicator.label": "Ladataan...",
   "media_gallery.toggle_visible": "Säädä näkyvyyttä",
-  "missing_indicator.label": "Ei löydetty",
+  "missing_indicator.label": "Ei löytynyt",
   "missing_indicator.sublabel": "Tätä resurssia ei löytynyt",
-  "mute_modal.hide_notifications": "Piilota ilmoitukset tältä käyttäjältä?",
+  "mute_modal.hide_notifications": "Piilota tältä käyttäjältä tulevat ilmoitukset?",
   "navigation_bar.blocks": "Estetyt käyttäjät",
   "navigation_bar.community_timeline": "Paikallinen aikajana",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Muokkaa profiilia",
   "navigation_bar.favourites": "Suosikit",
-  "navigation_bar.follow_requests": "Seurauspyynnöt",
+  "navigation_bar.follow_requests": "Seuraamispyynnöt",
   "navigation_bar.info": "Tietoa tästä instanssista",
-  "navigation_bar.keyboard_shortcuts": "Näppäinoikotiet",
+  "navigation_bar.keyboard_shortcuts": "Näppäinkomennot",
   "navigation_bar.lists": "Listat",
   "navigation_bar.logout": "Kirjaudu ulos",
   "navigation_bar.mutes": "Mykistetyt käyttäjät",
-  "navigation_bar.pins": "Kiinnitetyt töötit",
-  "navigation_bar.preferences": "Ominaisuudet",
+  "navigation_bar.pins": "Kiinnitetyt tuuttaukset",
+  "navigation_bar.preferences": "Asetukset",
   "navigation_bar.public_timeline": "Yleinen aikajana",
-  "notification.favourite": "{name} tykkäsi statuksestasi",
+  "notification.favourite": "{name} tykkäsi tilastasi",
   "notification.follow": "{name} seurasi sinua",
   "notification.mention": "{name} mainitsi sinut",
-  "notification.reblog": "{name} buustasi statustasi",
+  "notification.reblog": "{name} buustasi tilaasi",
   "notifications.clear": "Tyhjennä ilmoitukset",
-  "notifications.clear_confirmation": "Oletko varma, että haluat lopullisesti tyhjentää kaikki ilmoituksesi?",
-  "notifications.column_settings.alert": "Työpöytä ilmoitukset",
-  "notifications.column_settings.favourite": "Tykkäyksiä:",
-  "notifications.column_settings.follow": "Uusia seuraajia:",
-  "notifications.column_settings.mention": "Mainintoja:",
+  "notifications.clear_confirmation": "Haluatko varmasti poistaa kaikki ilmoitukset pysyvästi?",
+  "notifications.column_settings.alert": "Työpöytäilmoitukset",
+  "notifications.column_settings.favourite": "Tykkäykset:",
+  "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": "Buusteja:",
+  "notifications.column_settings.reblog": "Buustit:",
   "notifications.column_settings.show": "Näytä sarakkeessa",
-  "notifications.column_settings.sound": "Soita ääni",
+  "notifications.column_settings.sound": "Äänimerkki",
   "onboarding.done": "Valmis",
   "onboarding.next": "Seuraava",
-  "onboarding.page_five.public_timelines": "Paikallinen aikajana näyttää kaikki julkiset julkaisut kaikilta, jotka ovat verkko-osoitteessa {domain}. Yleinen aikajana näyttää julkiset julkaisut kaikilta niiltä, joita käyttäjät verkko-osoitteessa {domain} seuraavat. Nämä ovat julkiset aikajanat, ja ne ovat hyviä tapoja löytää uusia ihmisiä.",
-  "onboarding.page_four.home": "Kotiaikajana näyttää julkaisut ihmisiltä joita seuraat.",
-  "onboarding.page_four.notifications": "Ilmoitukset-sarake näyttää sinulle, kun joku on viestii kanssasi.",
-  "onboarding.page_one.federation": "Mastodon on yhteisöpalvelu, joka toimii monen itsenäisen palvelimen muodostamassa verkossa. Me kutsumme näitä palvelimia instansseiksi.",
+  "onboarding.page_five.public_timelines": "Paikallisella aikajanalla näytetään instanssin {domain} kaikkien käyttäjien julkiset julkaisut. Yleisellä aikajanalla näytetään kaikkien instanssin {domain} käyttäjien seuraamien käyttäjien julkiset julkaisut. Nämä julkiset aikajanat ovat loistavia paikkoja löytää uusia ihmisiä.",
+  "onboarding.page_four.home": "Kotiaikajanalla näytetään seuraamiesi ihmisten julkaisut.",
+  "onboarding.page_four.notifications": "Ilmoitukset-sarakkeessa näytetään muiden sinuun liittyvä toiminta.",
+  "onboarding.page_one.federation": "Mastodon on usean itsenäisen palvelimen muodostama yhteisöpalvelu. Näitä palvelimia kutsutaan instansseiksi.",
   "onboarding.page_one.full_handle": "Koko käyttäjänimesi",
-  "onboarding.page_one.handle_hint": "Tämä on se, mitä voisit ehdottaa ystäviäsi etsimään.",
+  "onboarding.page_one.handle_hint": "Tällä nimellä ystäväsi löytävät sinut.",
   "onboarding.page_one.welcome": "Tervetuloa Mastodoniin!",
-  "onboarding.page_six.admin": "Instanssisi ylläpitäjä on {admin}.",
+  "onboarding.page_six.admin": "Instanssin ylläpitäjä on {admin}.",
   "onboarding.page_six.almost_done": "Melkein valmista...",
-  "onboarding.page_six.appetoot": "Bon Appetööt!",
+  "onboarding.page_six.appetoot": "Tuuttailun iloa!",
   "onboarding.page_six.apps_available": "{apps} on saatavilla iOS:lle, Androidille ja muille alustoille.",
-  "onboarding.page_six.github": "Mastodon on ilmainen, vapaan lähdekoodin ohjelma. Voit raportoida bugeja, pyytää ominaisuuksia tai osallistua kehittämiseen GitHub-palvelussa: {github}.",
+  "onboarding.page_six.github": "Mastodon on ilmainen, vapaan lähdekoodin ohjelma. Voit raportoida bugeja, ehdottaa ominaisuuksia tai osallistua kehittämiseen GitHubissa: {github}.",
   "onboarding.page_six.guidelines": "yhteisön säännöt",
   "onboarding.page_six.read_guidelines": "Ole hyvä ja lue {domain}:n {guidelines}!",
   "onboarding.page_six.various_app": "mobiilisovellukset",
-  "onboarding.page_three.profile": "Muokkaa profiiliasi muuttaaksesi kuvakettasi, esittelyäsi ja nimimerkkiäsi. Löydät sieltä myös muita henkilökohtaisia asetuksia.",
-  "onboarding.page_three.search": "Käytä hakukenttää löytääksesi ihmisiä ja etsiäksesi hashtageja, kuten {illustration} tai {introductions}. Hakeaksesi henkilöä joka on toisessa instanssissa, käytä hänen käyttäjänimeään kokonaisuudessaan.",
-  "onboarding.page_two.compose": "Kirjoita postauksia kirjoita-sarakkeessa. Voit ladata kuvia, vaihtaa yksityisyysasetuksia ja lisätä sisältövaroituksia alla olevista painikkeista.",
+  "onboarding.page_three.profile": "Voit muuttaa profiilikuvaasi, esittelyäsi ja nimimerkkiäsi sekä muita asetuksia muokkaamalla profiiliasi.",
+  "onboarding.page_three.search": "Etsi ihmisiä ja hashtageja (esimerkiksi {illustration} tai {introductions}) hakukentän avulla. Jos haet toista instanssia käyttävää henkilöä, käytä hänen koko käyttäjänimeään.",
+  "onboarding.page_two.compose": "Kirjoita julkaisuja kirjoitussarakkeessa. Voit ladata kuvia, vaihtaa näkyvyysasetuksia ja lisätä sisältövaroituksia alla olevista painikkeista.",
   "onboarding.skip": "Ohita",
-  "privacy.change": "Säädä töötin yksityisyysasetuksia",
+  "privacy.change": "Säädä tuuttauksen näkyvyyttä",
   "privacy.direct.long": "Julkaise vain mainituille käyttäjille",
-  "privacy.direct.short": "Yksityisviesti",
+  "privacy.direct.short": "Suora viesti",
   "privacy.private.long": "Julkaise vain seuraajille",
   "privacy.private.short": "Vain seuraajat",
   "privacy.public.long": "Julkaise julkisille aikajanoille",
   "privacy.public.short": "Julkinen",
-  "privacy.unlisted.long": "Älä julkaise yleisillä aikajanoilla",
-  "privacy.unlisted.short": "Julkinen, mutta älä näytä julkisella aikajanalla",
+  "privacy.unlisted.long": "Älä julkaise julkisilla aikajanoilla",
+  "privacy.unlisted.short": "Listaamaton julkinen",
   "regeneration_indicator.label": "Ladataan…",
   "regeneration_indicator.sublabel": "Kotinäkymääsi valmistellaan!",
-  "relative_time.days": "{number}d",
-  "relative_time.hours": "{number}h",
+  "relative_time.days": "{number} pv",
+  "relative_time.hours": "{number} h",
   "relative_time.just_now": "nyt",
-  "relative_time.minutes": "{number}m",
-  "relative_time.seconds": "{number}s",
+  "relative_time.minutes": "{number} m",
+  "relative_time.seconds": "{number} s",
   "reply_indicator.cancel": "Peruuta",
-  "report.forward": "Uudelleenohjaa kohteeseen {target}",
-  "report.forward_hint": "Tämä tili on toiselta serveriltä. Haluatko, että myös sinne lähetetään anonymisoitu kopio ilmiantoraportista?",
-  "report.hint": "Ilmianto lähetetään instanssisi moderaattoreille. Voit antaa kuvauksen käyttäjän ilmiantamisen syystä alle:",
+  "report.forward": "Välitä kohteeseen {target}",
+  "report.forward_hint": "Tämä tili on toisella palvelimella. Haluatko lähettää nimettömän raportin myös sinne?",
+  "report.hint": "Raportti lähetetään oman instanssisi moderaattoreille. Seuraavassa voit kertoa, miksi raportoit tästä tilistä:",
   "report.placeholder": "Lisäkommentit",
-  "report.submit": "Submit",
-  "report.target": "Reporting",
+  "report.submit": "Lähetä",
+  "report.target": "Raportoidaan {target}",
   "search.placeholder": "Hae",
   "search_popout.search_format": "Tarkennettu haku",
-  "search_popout.tips.full_text": "Tekstihaku palauttaa statuspäivitykset jotka olet kirjoittanut, lisännyt suosikkeihisi, boostannut tai joissa sinut mainitaan, sekä käyttäjänimet, nimimerkit ja hastagit jotka sisältävät tekstin.",
-  "search_popout.tips.hashtag": "hashtagi",
-  "search_popout.tips.status": "status",
-  "search_popout.tips.text": "Pelkkä tekstihaku palauttaa hakua vastaavat nimimerkit, käyttäjänimet ja hastagit",
+  "search_popout.tips.full_text": "Tekstihaku palauttaa tilapäivitykset, jotka olet kirjoittanut, lisännyt suosikkeihisi, boostannut tai joissa sinut mainitaan, sekä tekstin sisältävät käyttäjänimet, nimimerkit ja hastagit.",
+  "search_popout.tips.hashtag": "hashtag",
+  "search_popout.tips.status": "tila",
+  "search_popout.tips.text": "Tekstihaku palauttaa hakua vastaavat nimimerkit, käyttäjänimet ja hastagit",
   "search_popout.tips.user": "käyttäjä",
   "search_results.accounts": "Ihmiset",
   "search_results.hashtags": "Hashtagit",
-  "search_results.statuses": "Töötit",
+  "search_results.statuses": "Tuuttaukset",
   "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
   "standalone.public_title": "Kurkistus sisälle...",
-  "status.block": "Block @{name}",
-  "status.cannot_reblog": "Tätä postausta ei voi buustata",
+  "status.block": "Estä @{name}",
+  "status.cancel_reblog_private": "Unboost",
+  "status.cannot_reblog": "Tätä julkaisua ei voi buustata",
   "status.delete": "Poista",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Upota",
   "status.favourite": "Tykkää",
   "status.load_more": "Lataa lisää",
@@ -248,29 +253,31 @@
   "status.more": "Lisää",
   "status.mute": "Mykistä @{name}",
   "status.mute_conversation": "Mykistä keskustelu",
-  "status.open": "Laajenna statuspäivitys",
+  "status.open": "Laajenna tilapäivitys",
   "status.pin": "Kiinnitä profiiliin",
-  "status.pinned": "Kiinnitetty töötti",
+  "status.pinned": "Kiinnitetty tuuttaus",
   "status.reblog": "Buustaa",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} buustasi",
   "status.reply": "Vastaa",
   "status.replyAll": "Vastaa ketjuun",
-  "status.report": "Report @{name}",
+  "status.report": "Raportoi @{name}",
   "status.sensitive_toggle": "Klikkaa nähdäksesi",
   "status.sensitive_warning": "Arkaluontoista sisältöä",
   "status.share": "Jaa",
   "status.show_less": "Näytä vähemmän",
   "status.show_less_all": "Näytä vähemmän kaikista",
   "status.show_more": "Näytä lisää",
-  "status.show_more_all": "Näytä enemmän kaikista",
-  "status.unmute_conversation": "Poista mykistys keskustelulta",
+  "status.show_more_all": "Näytä lisää kaikista",
+  "status.unmute_conversation": "Poista keskustelun mykistys",
   "status.unpin": "Irrota profiilista",
   "tabs_bar.federated_timeline": "Yleinen",
   "tabs_bar.home": "Koti",
   "tabs_bar.local_timeline": "Paikallinen",
   "tabs_bar.notifications": "Ilmoitukset",
-  "ui.beforeunload": "Luonnoksesi menetetään, jos poistut Mastodonista.",
-  "upload_area.title": "Raahaa ja pudota tähän ladataksesi",
+  "tabs_bar.search": "Search",
+  "ui.beforeunload": "Luonnos häviää, jos poistut Mastodonista.",
+  "upload_area.title": "Lataa raahaamalla ja pudottamalla tähän",
   "upload_button.label": "Lisää mediaa",
   "upload_form.description": "Anna kuvaus näkörajoitteisia varten",
   "upload_form.focus": "Rajaa",
@@ -279,10 +286,10 @@
   "video.close": "Sulje video",
   "video.exit_fullscreen": "Poistu koko näytön tilasta",
   "video.expand": "Laajenna video",
-  "video.fullscreen": "Full screen",
+  "video.fullscreen": "Koko näyttö",
   "video.hide": "Piilota video",
   "video.mute": "Mykistä ääni",
   "video.pause": "Keskeytä",
   "video.play": "Toista",
-  "video.unmute": "Poista mykistys ääneltä"
+  "video.unmute": "Poista äänen mykistys"
 }
diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json
index 57c55c9bd..98c1c43d2 100644
--- a/app/javascript/mastodon/locales/fr.json
+++ b/app/javascript/mastodon/locales/fr.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Réessayer",
   "column.blocks": "Comptes bloqués",
   "column.community": "Fil public local",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favoris",
   "column.follow_requests": "Demandes de suivi",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Symboles",
   "emoji_button.travel": "Lieux & Voyages",
   "empty_column.community": "Le fil public local est vide. Écrivez donc quelque chose pour le remplir !",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Il n’y a encore aucun contenu associé à ce hashtag.",
   "empty_column.home": "Vous ne suivez encore personne. Visitez {public} ou bien utilisez la recherche pour vous connecter à d’autres personnes.",
   "empty_column.home.public_timeline": "le fil public",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Masquer les notifications de cette personne ?",
   "navigation_bar.blocks": "Comptes bloqués",
   "navigation_bar.community_timeline": "Fil public local",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Modifier le profil",
   "navigation_bar.favourites": "Favoris",
@@ -205,8 +208,8 @@
   "privacy.change": "Ajuster la confidentialité du message",
   "privacy.direct.long": "N'envoyer qu'aux personnes mentionnées",
   "privacy.direct.short": "Direct",
-  "privacy.private.long": "N'envoyer qu'à vos abonné⋅e⋅s",
-  "privacy.private.short": "Privé",
+  "privacy.private.long": "Seul⋅e⋅s vos abonné⋅e⋅s verront vos statuts",
+  "privacy.private.short": "Abonné⋅e⋅s uniquement",
   "privacy.public.long": "Afficher dans les fils publics",
   "privacy.public.short": "Public",
   "privacy.unlisted.long": "Ne pas afficher dans les fils publics",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {résultat} other {résultats}}",
   "standalone.public_title": "Un aperçu …",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Cette publication ne peut être boostée",
   "status.delete": "Effacer",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Intégrer",
   "status.favourite": "Ajouter aux favoris",
   "status.load_more": "Charger plus",
@@ -252,6 +257,7 @@
   "status.pin": "Épingler sur le profil",
   "status.pinned": "Pouet épinglé",
   "status.reblog": "Partager",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} a partagé :",
   "status.reply": "Répondre",
   "status.replyAll": "Répondre au fil",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Accueil",
   "tabs_bar.local_timeline": "Fil public local",
   "tabs_bar.notifications": "Notifications",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Votre brouillon sera perdu si vous quittez Mastodon.",
   "upload_area.title": "Glissez et déposez pour envoyer",
   "upload_button.label": "Joindre un média",
diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json
index 8d586404d..fca42374d 100644
--- a/app/javascript/mastodon/locales/gl.json
+++ b/app/javascript/mastodon/locales/gl.json
@@ -2,7 +2,7 @@
   "account.block": "Bloquear @{name}",
   "account.block_domain": "Ocultar calquer contido de {domain}",
   "account.blocked": "Blocked",
-  "account.direct": "Direct Message @{name}",
+  "account.direct": "Direct message @{name}",
   "account.disclaimer_full": "A información inferior podería mostrar un perfil incompleto da usuaria.",
   "account.domain_blocked": "Domain hidden",
   "account.edit_profile": "Editar perfil",
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Inténteo de novo",
   "column.blocks": "Usuarias bloqueadas",
   "column.community": "Liña temporal local",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favoritas",
   "column.follow_requests": "Peticións de seguimento",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Símbolos",
   "emoji_button.travel": "Viaxes e Lugares",
   "empty_column.community": "A liña temporal local está baldeira. Escriba algo de xeito público para que rule!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Aínda non hai nada con esta etiqueta.",
   "empty_column.home": "A súa liña temporal de inicio está baldeira! Visite {public} ou utilice a busca para atopar outras usuarias.",
   "empty_column.home.public_timeline": "a liña temporal pública",
@@ -148,12 +150,13 @@
   "lists.search": "Procurar entre a xente que segues",
   "lists.subheading": "As túas listas",
   "loading_indicator.label": "Cargando...",
-  "media_gallery.toggle_visible": "Dar visibilidade",
+  "media_gallery.toggle_visible": "Ocultar",
   "missing_indicator.label": "Non atopado",
   "missing_indicator.sublabel": "This resource could not be found",
   "mute_modal.hide_notifications": "Esconder notificacións deste usuario?",
   "navigation_bar.blocks": "Usuarias bloqueadas",
   "navigation_bar.community_timeline": "Liña temporal local",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Editar perfil",
   "navigation_bar.favourites": "Favoritas",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count,plural,one {result} outros {results}}",
   "standalone.public_title": "Ollada dentro...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Esta mensaxe non pode ser promocionada",
   "status.delete": "Eliminar",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Incrustar",
   "status.favourite": "Favorita",
   "status.load_more": "Cargar máis",
@@ -252,6 +257,7 @@
   "status.pin": "Fixar no perfil",
   "status.pinned": "Pinned toot",
   "status.reblog": "Promover",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} promoveu",
   "status.reply": "Resposta",
   "status.replyAll": "Resposta a conversa",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Inicio",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notificacións",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "O borrador perderase se sae de Mastodon.",
   "upload_area.title": "Arrastre e solte para subir",
   "upload_button.label": "Engadir medios",
diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json
index 6bec26fd8..e3e87f1d0 100644
--- a/app/javascript/mastodon/locales/he.json
+++ b/app/javascript/mastodon/locales/he.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "לנסות שוב",
   "column.blocks": "חסימות",
   "column.community": "ציר זמן מקומי",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "חיבובים",
   "column.follow_requests": "בקשות מעקב",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "סמלים",
   "emoji_button.travel": "טיולים ואתרים",
   "empty_column.community": "טור הסביבה ריק. יש לפרסם משהו כדי שדברים יתרחילו להתגלגל!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "אין כלום בהאשתג הזה עדיין.",
   "empty_column.home": "אף אחד לא במעקב עדיין. אפשר לבקר ב{public} או להשתמש בחיפוש כדי להתחיל ולהכיר חצוצרנים אחרים.",
   "empty_column.home.public_timeline": "ציר זמן בין-קהילתי",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "להסתיר הודעות מחשבון זה?",
   "navigation_bar.blocks": "חסימות",
   "navigation_bar.community_timeline": "ציר זמן מקומי",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "עריכת פרופיל",
   "navigation_bar.favourites": "חיבובים",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {תוצאה} other {תוצאות}}",
   "standalone.public_title": "הצצה פנימה...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "לא ניתן להדהד הודעה זו",
   "status.delete": "מחיקה",
+  "status.direct": "Direct message @{name}",
   "status.embed": "הטמעה",
   "status.favourite": "חיבוב",
   "status.load_more": "עוד",
@@ -252,6 +257,7 @@
   "status.pin": "לקבע באודות",
   "status.pinned": "Pinned toot",
   "status.reblog": "הדהוד",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "הודהד על ידי {name}",
   "status.reply": "תגובה",
   "status.replyAll": "תגובה לכולם",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "בבית",
   "tabs_bar.local_timeline": "ציר זמן מקומי",
   "tabs_bar.notifications": "התראות",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "הטיוטא תאבד אם תעזבו את מסטודון.",
   "upload_area.title": "ניתן להעלות על ידי Drag & drop",
   "upload_button.label": "הוספת מדיה",
diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json
index f7a5d0a3f..b41c98394 100644
--- a/app/javascript/mastodon/locales/hr.json
+++ b/app/javascript/mastodon/locales/hr.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Try again",
   "column.blocks": "Blokirani korisnici",
   "column.community": "Lokalni timeline",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favoriti",
   "column.follow_requests": "Zahtjevi za slijeđenje",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Simboli",
   "emoji_button.travel": "Putovanja & Mjesta",
   "empty_column.community": "Lokalni timeline je prazan. Napiši nešto javno kako bi pokrenuo stvari!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Još ne postoji ništa s ovim hashtagom.",
   "empty_column.home": "Još ne slijediš nikoga. Posjeti {public} ili koristi tražilicu kako bi počeo i upoznao druge korisnike.",
   "empty_column.home.public_timeline": "javni timeline",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Blokirani korisnici",
   "navigation_bar.community_timeline": "Lokalni timeline",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Uredi profil",
   "navigation_bar.favourites": "Favoriti",
@@ -238,8 +241,10 @@
   "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": "Ovaj post ne može biti boostan",
   "status.delete": "Obriši",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Označi omiljenim",
   "status.load_more": "Učitaj više",
@@ -252,6 +257,7 @@
   "status.pin": "Pin on profile",
   "status.pinned": "Pinned toot",
   "status.reblog": "Podigni",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} je podigao",
   "status.reply": "Odgovori",
   "status.replyAll": "Odgovori na temu",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Dom",
   "tabs_bar.local_timeline": "Lokalno",
   "tabs_bar.notifications": "Notifikacije",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Povuci i spusti kako bi uploadao",
   "upload_button.label": "Dodaj media",
diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json
index 8b9c14993..956accc67 100644
--- a/app/javascript/mastodon/locales/hu.json
+++ b/app/javascript/mastodon/locales/hu.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Próbálja újra",
   "column.blocks": "Letiltott felhasználók",
   "column.community": "Helyi idővonal",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Kedvencek",
   "column.follow_requests": "Követési kérések",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Szimbólumok",
   "emoji_button.travel": "Utazás és Helyek",
   "empty_column.community": "A helyi idővonal üres. Írj egy publikus stástuszt, hogy elindítsd a labdát!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Jelenleg nem található semmi ezen hashtaggel.",
   "empty_column.home": "A hazai idővonala üres! Látogasd meg a {public} vagy használd a keresőt, hogy ismerj meg más felhasználókat.",
   "empty_column.home.public_timeline": "publikus idővonal",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Értesítések elrejtése ezen felhasználótól?",
   "navigation_bar.blocks": "Tiltott felhasználók",
   "navigation_bar.community_timeline": "Helyi idővonal",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Profil szerkesztése",
   "navigation_bar.favourites": "Kedvencek",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
   "standalone.public_title": "Betekintés...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Ezen státusz nem rebloggolható",
   "status.delete": "Törlés",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Beágyaz",
   "status.favourite": "Kedvenc",
   "status.load_more": "Többet",
@@ -252,6 +257,7 @@
   "status.pin": "Kitűzés a profilra",
   "status.pinned": "Pinned toot",
   "status.reblog": "Reblog",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} reblogolta",
   "status.reply": "Válasz",
   "status.replyAll": "Válaszolj a beszélgetésre",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Kezdőlap",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Értesítések",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "A piszkozata el fog vesztődni ha elhagyja Mastodon-t.",
   "upload_area.title": "Húzza ide a feltöltéshez",
   "upload_button.label": "Média hozzáadása",
diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json
index 22ba89a43..33e079201 100644
--- a/app/javascript/mastodon/locales/hy.json
+++ b/app/javascript/mastodon/locales/hy.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Կրկին փորձել",
   "column.blocks": "Արգելափակված օգտատերեր",
   "column.community": "Տեղական հոսք",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Հավանածներ",
   "column.follow_requests": "Հետեւելու հայցեր",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Նշաններ",
   "emoji_button.travel": "Ուղեւորություն եւ տեղանքներ",
   "empty_column.community": "Տեղական հոսքը դատա՛րկ է։ Հրապարակային մի բան գրիր շարժիչը խոդ տալու համար։",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Այս պիտակով դեռ ոչինչ չկա։",
   "empty_column.home": "Քո հիմնական հոսքը դատա՛րկ է։ Այցելի՛ր {public}ը կամ օգտվիր որոնումից՝ այլ մարդկանց հանդիպելու համար։",
   "empty_column.home.public_timeline": "հրապարակային հոսք",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Թաքցնե՞լ ցանուցումներն այս օգտատիրոջից։",
   "navigation_bar.blocks": "Արգելափակված օգտատերեր",
   "navigation_bar.community_timeline": "Տեղական հոսք",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Խմբագրել անձնական էջը",
   "navigation_bar.favourites": "Հավանածներ",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
   "standalone.public_title": "Այս պահին…",
   "status.block": "Արգելափակել @{name}֊ին",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Այս թութը չի կարող տարածվել",
   "status.delete": "Ջնջել",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Ներդնել",
   "status.favourite": "Հավանել",
   "status.load_more": "Բեռնել ավելին",
@@ -252,6 +257,7 @@
   "status.pin": "Ամրացնել անձնական էջում",
   "status.pinned": "Pinned toot",
   "status.reblog": "Տարածել",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} տարածել է",
   "status.reply": "Պատասխանել",
   "status.replyAll": "Պատասխանել թելին",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Հիմնական",
   "tabs_bar.local_timeline": "Տեղական",
   "tabs_bar.notifications": "Ծանուցումներ",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Քո սեւագիրը կկորի, եթե լքես Մաստոդոնը։",
   "upload_area.title": "Քաշիր ու նետիր՝ վերբեռնելու համար",
   "upload_button.label": "Ավելացնել մեդիա",
diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json
index 1ef610fba..412ffd3a0 100644
--- a/app/javascript/mastodon/locales/id.json
+++ b/app/javascript/mastodon/locales/id.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Coba lagi",
   "column.blocks": "Pengguna diblokir",
   "column.community": "Linimasa Lokal",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favorit",
   "column.follow_requests": "Permintaan mengikuti",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Simbol",
   "emoji_button.travel": "Tempat Wisata",
   "empty_column.community": "Linimasa lokal masih kosong. Tulis sesuatu secara publik dan buat roda berputar!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Tidak ada apapun dalam hashtag ini.",
   "empty_column.home": "Linimasa anda kosong! Kunjungi {public} atau gunakan pencarian untuk memulai dan bertemu pengguna lain.",
   "empty_column.home.public_timeline": "linimasa publik",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Pengguna diblokir",
   "navigation_bar.community_timeline": "Linimasa lokal",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Ubah profil",
   "navigation_bar.favourites": "Favorit",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {hasil} other {hasil}}",
   "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": "Hapus",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Difavoritkan",
   "status.load_more": "Tampilkan semua",
@@ -252,6 +257,7 @@
   "status.pin": "Pin on profile",
   "status.pinned": "Pinned toot",
   "status.reblog": "Boost",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "di-boost {name}",
   "status.reply": "Balas",
   "status.replyAll": "Balas ke semua",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Beranda",
   "tabs_bar.local_timeline": "Lokal",
   "tabs_bar.notifications": "Notifikasi",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Naskah anda akan hilang jika anda keluar dari Mastodon.",
   "upload_area.title": "Seret & lepaskan untuk mengunggah",
   "upload_button.label": "Tambahkan media",
diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json
index 1435178a8..9730bf934 100644
--- a/app/javascript/mastodon/locales/io.json
+++ b/app/javascript/mastodon/locales/io.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Try again",
   "column.blocks": "Blokusita uzeri",
   "column.community": "Lokala tempolineo",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favorati",
   "column.follow_requests": "Demandi di sequado",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Symbols",
   "emoji_button.travel": "Travel & Places",
   "empty_column.community": "La lokala tempolineo esas vakua. Skribez ulo publike por iniciar la agiveso!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Esas ankore nulo en ta gretovorto.",
   "empty_column.home": "Tu sequas ankore nulu. Vizitez {public} od uzez la serchilo por komencar e renkontrar altra uzeri.",
   "empty_column.home.public_timeline": "la publika tempolineo",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Blokusita uzeri",
   "navigation_bar.community_timeline": "Lokala tempolineo",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Modifikar profilo",
   "navigation_bar.favourites": "Favorati",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {rezulto} other {rezulti}}",
   "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": "Efacar",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Favorizar",
   "status.load_more": "Kargar pluse",
@@ -252,6 +257,7 @@
   "status.pin": "Pin on profile",
   "status.pinned": "Pinned toot",
   "status.reblog": "Repetar",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} repetita",
   "status.reply": "Respondar",
   "status.replyAll": "Respondar a filo",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Hemo",
   "tabs_bar.local_timeline": "Lokala",
   "tabs_bar.notifications": "Savigi",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Tranar faligar por kargar",
   "upload_button.label": "Adjuntar kontenajo",
diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json
index 226127e6b..5146d7ca2 100644
--- a/app/javascript/mastodon/locales/it.json
+++ b/app/javascript/mastodon/locales/it.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Try again",
   "column.blocks": "Utenti bloccati",
   "column.community": "Timeline locale",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Apprezzati",
   "column.follow_requests": "Richieste di amicizia",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Symbols",
   "emoji_button.travel": "Travel & Places",
   "empty_column.community": "La timeline locale è vuota. Condividi qualcosa pubblicamente per dare inizio alla festa!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.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",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Utenti bloccati",
   "navigation_bar.community_timeline": "Timeline locale",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Modifica profilo",
   "navigation_bar.favourites": "Apprezzati",
@@ -238,8 +241,10 @@
   "search_results.total": "{count} {count, plural, one {risultato} other {risultati}}",
   "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": "Elimina",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Apprezzato",
   "status.load_more": "Mostra di più",
@@ -252,6 +257,7 @@
   "status.pin": "Pin on profile",
   "status.pinned": "Pinned toot",
   "status.reblog": "Condividi",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} ha condiviso",
   "status.reply": "Rispondi",
   "status.replyAll": "Reply to thread",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Home",
   "tabs_bar.local_timeline": "Locale",
   "tabs_bar.notifications": "Notifiche",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Trascina per caricare",
   "upload_button.label": "Aggiungi file multimediale",
diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json
index 0efd367e9..abd18742a 100644
--- a/app/javascript/mastodon/locales/ja.json
+++ b/app/javascript/mastodon/locales/ja.json
@@ -159,6 +159,7 @@
   "mute_modal.hide_notifications": "このユーザーからの通知を隠しますか?",
   "navigation_bar.blocks": "ブロックしたユーザー",
   "navigation_bar.community_timeline": "ローカルタイムライン",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "非表示にしたドメイン",
   "navigation_bar.edit_profile": "プロフィールを編集",
   "navigation_bar.favourites": "お気に入り",
@@ -192,7 +193,7 @@
   "onboarding.page_five.public_timelines": "連合タイムラインでは{domain}の人がフォローしているMastodon全体での公開投稿を表示します。同じくローカルタイムラインでは{domain}のみの公開投稿を表示します。",
   "onboarding.page_four.home": "「ホーム」タイムラインではあなたがフォローしている人の投稿を表示します。",
   "onboarding.page_four.notifications": "「通知」ではあなたへの他の人からの関わりを表示します。",
-  "onboarding.page_one.federation": "Mastodonは誰でも参加できるSNSです。",
+  "onboarding.page_one.federation": "Mastodonは独立したインスタンス(サーバー)の集合体です。",
   "onboarding.page_one.full_handle": "あなたのフルハンドル",
   "onboarding.page_one.handle_hint": "あなたを探している友達に伝えるといいでしょう。",
   "onboarding.page_one.welcome": "Mastodonへようこそ!",
@@ -244,8 +245,10 @@
   "search_results.total": "{count, number}件の結果",
   "standalone.public_title": "今こんな話をしています...",
   "status.block": "@{name}さんをブロック",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "この投稿はブーストできません",
   "status.delete": "削除",
+  "status.direct": "@{name}さんにダイレクトメッセージ",
   "status.embed": "埋め込み",
   "status.favourite": "お気に入り",
   "status.load_more": "もっと見る",
@@ -258,6 +261,7 @@
   "status.pin": "プロフィールに固定表示",
   "status.pinned": "固定されたトゥート",
   "status.reblog": "ブースト",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name}さんがブースト",
   "status.reply": "返信",
   "status.replyAll": "全員に返信",
@@ -275,6 +279,7 @@
   "tabs_bar.home": "ホーム",
   "tabs_bar.local_timeline": "ローカル",
   "tabs_bar.notifications": "通知",
+  "tabs_bar.search": "検索",
   "ui.beforeunload": "Mastodonから離れると送信前の投稿は失われます。",
   "upload_area.title": "ドラッグ&ドロップでアップロード",
   "upload_button.label": "メディアを追加",
diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json
index 89fde8966..92367dc95 100644
--- a/app/javascript/mastodon/locales/ko.json
+++ b/app/javascript/mastodon/locales/ko.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "다시 시도",
   "column.blocks": "차단 중인 사용자",
   "column.community": "로컬 타임라인",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "즐겨찾기",
   "column.follow_requests": "팔로우 요청",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "기호",
   "emoji_button.travel": "여행과 장소",
   "empty_column.community": "로컬 타임라인에 아무 것도 없습니다. 아무거나 적어 보세요!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "이 해시태그는 아직 사용되지 않았습니다.",
   "empty_column.home": "아직 아무도 팔로우 하고 있지 않습니다. {public}를 보러 가거나, 검색하여 다른 사용자를 찾아 보세요.",
   "empty_column.home.public_timeline": "연합 타임라인",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "이 사용자로부터의 알림을 뮤트하시겠습니까?",
   "navigation_bar.blocks": "차단한 사용자",
   "navigation_bar.community_timeline": "로컬 타임라인",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "프로필 편집",
   "navigation_bar.favourites": "즐겨찾기",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number}건의 결과",
   "standalone.public_title": "지금 이런 이야기를 하고 있습니다…",
   "status.block": "@{name} 차단",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "이 포스트는 부스트 할 수 없습니다",
   "status.delete": "삭제",
+  "status.direct": "Direct message @{name}",
   "status.embed": "공유하기",
   "status.favourite": "즐겨찾기",
   "status.load_more": "더 보기",
@@ -252,6 +257,7 @@
   "status.pin": "고정",
   "status.pinned": "고정 된 툿",
   "status.reblog": "부스트",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name}님이 부스트 했습니다",
   "status.reply": "답장",
   "status.replyAll": "전원에게 답장",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "홈",
   "tabs_bar.local_timeline": "로컬",
   "tabs_bar.notifications": "알림",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "지금 나가면 저장되지 않은 항목을 잃게 됩니다.",
   "upload_area.title": "드래그 & 드롭으로 업로드",
   "upload_button.label": "미디어 추가",
diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json
index 7bfb74a5c..c18ddbd01 100644
--- a/app/javascript/mastodon/locales/nl.json
+++ b/app/javascript/mastodon/locales/nl.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Opnieuw proberen",
   "column.blocks": "Geblokkeerde gebruikers",
   "column.community": "Lokale tijdlijn",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favorieten",
   "column.follow_requests": "Volgverzoeken",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Symbolen",
   "emoji_button.travel": "Reizen en plekken",
   "empty_column.community": "De lokale tijdlijn is nog leeg. Toot iets in het openbaar om de bal aan het rollen te krijgen!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Er is nog niks te vinden onder deze hashtag.",
   "empty_column.home": "Jij volgt nog niemand. Bezoek {public} of gebruik het zoekvenster om andere mensen te ontmoeten.",
   "empty_column.home.public_timeline": "de globale tijdlijn",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Verberg meldingen van deze persoon?",
   "navigation_bar.blocks": "Geblokkeerde gebruikers",
   "navigation_bar.community_timeline": "Lokale tijdlijn",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Profiel bewerken",
   "navigation_bar.favourites": "Favorieten",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {resultaat} other {resultaten}}",
   "standalone.public_title": "Een kijkje binnenin...",
   "status.block": "Blokkeer @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Deze toot kan niet geboost worden",
   "status.delete": "Verwijderen",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Favoriet",
   "status.load_more": "Meer laden",
@@ -252,6 +257,7 @@
   "status.pin": "Aan profielpagina vastmaken",
   "status.pinned": "Vastgemaakte toot",
   "status.reblog": "Boost",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} boostte",
   "status.reply": "Reageren",
   "status.replyAll": "Reageer op iedereen",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Start",
   "tabs_bar.local_timeline": "Lokaal",
   "tabs_bar.notifications": "Meldingen",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Je concept zal verloren gaan als je Mastodon verlaat.",
   "upload_area.title": "Hierin slepen om te uploaden",
   "upload_button.label": "Media toevoegen",
diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json
index b79277ce3..282a72acb 100644
--- a/app/javascript/mastodon/locales/no.json
+++ b/app/javascript/mastodon/locales/no.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Prøv igjen",
   "column.blocks": "Blokkerte brukere",
   "column.community": "Lokal tidslinje",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Likt",
   "column.follow_requests": "Følgeforespørsler",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Symboler",
   "emoji_button.travel": "Reise & steder",
   "empty_column.community": "Den lokale tidslinjen er tom. Skriv noe offentlig for å få snøballen til å rulle!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Det er ingenting i denne hashtagen ennå.",
   "empty_column.home": "Du har ikke fulgt noen ennå. Besøk {publlic} eller bruk søk for å komme i gang og møte andre brukere.",
   "empty_column.home.public_timeline": "en offentlig tidslinje",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Skjul varslinger fra denne brukeren?",
   "navigation_bar.blocks": "Blokkerte brukere",
   "navigation_bar.community_timeline": "Lokal tidslinje",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Rediger profil",
   "navigation_bar.favourites": "Favoritter",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {resultat} other {resultater}}",
   "standalone.public_title": "En titt inni...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Denne posten kan ikke fremheves",
   "status.delete": "Slett",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Bygge inn",
   "status.favourite": "Lik",
   "status.load_more": "Last mer",
@@ -252,6 +257,7 @@
   "status.pin": "Fest på profilen",
   "status.pinned": "Pinned toot",
   "status.reblog": "Fremhev",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "Fremhevd av {name}",
   "status.reply": "Svar",
   "status.replyAll": "Svar til samtale",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Hjem",
   "tabs_bar.local_timeline": "Lokal",
   "tabs_bar.notifications": "Varslinger",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Din kladd vil bli forkastet om du forlater Mastodon.",
   "upload_area.title": "Dra og slipp for å laste opp",
   "upload_button.label": "Legg til media",
diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json
index 5b12f8811..7170aefb8 100644
--- a/app/javascript/mastodon/locales/oc.json
+++ b/app/javascript/mastodon/locales/oc.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Tornar ensajar",
   "column.blocks": "Personas blocadas",
   "column.community": "Flux public local",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favorits",
   "column.follow_requests": "Demandas d’abonament",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Simbòls",
   "emoji_button.travel": "Viatges & lòcs",
   "empty_column.community": "Lo flux public local es void. Escrivètz quicòm per lo garnir !",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "I a pas encara de contengut ligat a aquesta etiqueta.",
   "empty_column.home": "Vòstre flux d’acuèlh es void. Visitatz {public} o utilizatz la recèrca per vos connectar a d’autras personas.",
   "empty_column.home.public_timeline": "lo flux public",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Rescondre las notificacions d’aquesta persona ?",
   "navigation_bar.blocks": "Personas blocadas",
   "navigation_bar.community_timeline": "Flux public local",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Modificar lo perfil",
   "navigation_bar.favourites": "Favorits",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {resultat} other {resultats}}",
   "standalone.public_title": "Una ulhada dedins…",
   "status.block": "Blocar @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Aqueste estatut pòt pas èsser partejat",
   "status.delete": "Escafar",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embarcar",
   "status.favourite": "Apondre als favorits",
   "status.load_more": "Cargar mai",
@@ -252,6 +257,7 @@
   "status.pin": "Penjar al perfil",
   "status.pinned": "Tut penjat",
   "status.reblog": "Partejar",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} a partejat",
   "status.reply": "Respondre",
   "status.replyAll": "Respondre a la conversacion",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Acuèlh",
   "tabs_bar.local_timeline": "Flux public local",
   "tabs_bar.notifications": "Notificacions",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Vòstre brolhon serà perdut se quitatz Mastodon.",
   "upload_area.title": "Lisatz e depausatz per mandar",
   "upload_button.label": "Ajustar un mèdia",
diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json
index 82b7070b8..08aea797d 100644
--- a/app/javascript/mastodon/locales/pl.json
+++ b/app/javascript/mastodon/locales/pl.json
@@ -2,7 +2,7 @@
   "account.block": "Blokuj @{name}",
   "account.block_domain": "Blokuj wszystko z {domain}",
   "account.blocked": "Zablokowany",
-  "account.direct": "Direct Message @{name}",
+  "account.direct": "Wyślij wiadomość bezpośrednią do @{name}",
   "account.disclaimer_full": "Poniższe informacje mogą nie odwzorowywać bezbłędnie profilu użytkownika.",
   "account.domain_blocked": "Ukryto domenę",
   "account.edit_profile": "Edytuj profil",
@@ -138,6 +138,7 @@
   "keyboard_shortcuts.mention": "aby wspomnieć o autorze",
   "keyboard_shortcuts.reply": "aby odpowiedzieć",
   "keyboard_shortcuts.search": "aby przejść do pola wyszukiwania",
+  "keyboard_shortcuts.toggle_hidden": "aby wyświetlić lub ukryć wpis spod CW",
   "keyboard_shortcuts.toot": "aby utworzyć nowy wpis",
   "keyboard_shortcuts.unfocus": "aby opuścić pole wyszukiwania/pisania",
   "keyboard_shortcuts.up": "aby przejść na górę listy",
@@ -245,8 +246,10 @@
   "search_results.total": "{count, number} {count, plural, one {wynik} few {wyniki} many {wyników} more {wyników}}",
   "standalone.public_title": "Spojrzenie w głąb…",
   "status.block": "Zablokuj @{name}",
+  "status.cancel_reblog_private": "Cofnij podbicie",
   "status.cannot_reblog": "Ten wpis nie może zostać podbity",
   "status.delete": "Usuń",
+  "status.direct": "Wyślij wiadomość bezpośrednią do @{name}",
   "status.embed": "Osadź",
   "status.favourite": "Ulubione",
   "status.load_more": "Załaduj więcej",
@@ -259,6 +262,7 @@
   "status.pin": "Przypnij do profilu",
   "status.pinned": "Przypięty wpis",
   "status.reblog": "Podbij",
+  "status.reblog_private": "Podbij dla odbiorców oryginalnego wpisu",
   "status.reblogged_by": "{name} podbił",
   "status.reply": "Odpowiedz",
   "status.replyAll": "Odpowiedz na wątek",
diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json
index 4cd2e0643..c604476c7 100644
--- a/app/javascript/mastodon/locales/pt-BR.json
+++ b/app/javascript/mastodon/locales/pt-BR.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Tente novamente",
   "column.blocks": "Usuários bloqueados",
   "column.community": "Local",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favoritos",
   "column.follow_requests": "Seguidores pendentes",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Símbolos",
   "emoji_button.travel": "Viagens & Lugares",
   "empty_column.community": "A timeline local está vazia. Escreva algo publicamente para começar!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Ainda não há qualquer conteúdo com essa hashtag.",
   "empty_column.home": "Você ainda não segue usuário algum. Visite a timeline {public} ou use o buscador para procurar e conhecer outros usuários.",
   "empty_column.home.public_timeline": "global",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Esconder notificações deste usuário?",
   "navigation_bar.blocks": "Usuários bloqueados",
   "navigation_bar.community_timeline": "Local",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Editar perfil",
   "navigation_bar.favourites": "Favoritos",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}",
   "standalone.public_title": "Dê uma espiada...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Esta postagem não pode ser compartilhada",
   "status.delete": "Excluir",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Incorporar",
   "status.favourite": "Adicionar aos favoritos",
   "status.load_more": "Carregar mais",
@@ -252,6 +257,7 @@
   "status.pin": "Fixar no perfil",
   "status.pinned": "Toot fixado",
   "status.reblog": "Compartilhar",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} compartilhou",
   "status.reply": "Responder",
   "status.replyAll": "Responder à sequência",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Página inicial",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notificações",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Seu rascunho será perdido se você sair do Mastodon.",
   "upload_area.title": "Arraste e solte para enviar",
   "upload_button.label": "Adicionar mídia",
diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json
index 7a404eaba..826785aad 100644
--- a/app/javascript/mastodon/locales/pt.json
+++ b/app/javascript/mastodon/locales/pt.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Tente de novo",
   "column.blocks": "Utilizadores Bloqueados",
   "column.community": "Local",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favoritos",
   "column.follow_requests": "Seguidores Pendentes",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Símbolos",
   "emoji_button.travel": "Viagens & Lugares",
   "empty_column.community": "Ainda não existe conteúdo local para mostrar!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Não foram encontradas publicações com essa hashtag.",
   "empty_column.home": "Ainda não segues qualquer utilizador. Visita {public} ou utiliza a pesquisa para procurar outros utilizadores.",
   "empty_column.home.public_timeline": "global",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Esconder notificações deste utilizador?",
   "navigation_bar.blocks": "Utilizadores bloqueados",
   "navigation_bar.community_timeline": "Local",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Editar perfil",
   "navigation_bar.favourites": "Favoritos",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}",
   "standalone.public_title": "Espreitar lá dentro...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Este post não pode ser partilhado",
   "status.delete": "Eliminar",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Incorporar",
   "status.favourite": "Adicionar aos favoritos",
   "status.load_more": "Carregar mais",
@@ -252,6 +257,7 @@
   "status.pin": "Fixar no perfil",
   "status.pinned": "Pinned toot",
   "status.reblog": "Partilhar",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} partilhou",
   "status.reply": "Responder",
   "status.replyAll": "Responder à conversa",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Home",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notificações",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "O teu rascunho vai ser perdido se abandonares o Mastodon.",
   "upload_area.title": "Arraste e solte para enviar",
   "upload_button.label": "Adicionar media",
diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json
index 8616ef98f..bb3cc1794 100644
--- a/app/javascript/mastodon/locales/ru.json
+++ b/app/javascript/mastodon/locales/ru.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Попробовать снова",
   "column.blocks": "Список блокировки",
   "column.community": "Локальная лента",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Скрытые домены",
   "column.favourites": "Понравившееся",
   "column.follow_requests": "Запросы на подписку",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Символы",
   "emoji_button.travel": "Путешествия",
   "empty_column.community": "Локальная лента пуста. Напишите что-нибудь, чтобы разогреть народ!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Статусов с таким хэштегом еще не существует.",
   "empty_column.home": "Пока Вы ни на кого не подписаны. Полистайте {public} или используйте поиск, чтобы освоиться и завести новые знакомства.",
   "empty_column.home.public_timeline": "публичные ленты",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Убрать уведомления от этого пользователя?",
   "navigation_bar.blocks": "Список блокировки",
   "navigation_bar.community_timeline": "Локальная лента",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Скрытые домены",
   "navigation_bar.edit_profile": "Изменить профиль",
   "navigation_bar.favourites": "Понравившееся",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {результат} few {результата} many {результатов} other {результатов}}",
   "standalone.public_title": "Прямо сейчас",
   "status.block": "Заблокировать @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Этот статус не может быть продвинут",
   "status.delete": "Удалить",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Встроить",
   "status.favourite": "Нравится",
   "status.load_more": "Показать еще",
@@ -252,6 +257,7 @@
   "status.pin": "Закрепить в профиле",
   "status.pinned": "Pinned toot",
   "status.reblog": "Продвинуть",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} продвинул(а)",
   "status.reply": "Ответить",
   "status.replyAll": "Ответить на тред",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Главная",
   "tabs_bar.local_timeline": "Локальная",
   "tabs_bar.notifications": "Уведомления",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Ваш черновик будет утерян, если вы покинете Mastodon.",
   "upload_area.title": "Перетащите сюда, чтобы загрузить",
   "upload_button.label": "Добавить медиаконтент",
diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json
index 925b46df6..58274fd2d 100644
--- a/app/javascript/mastodon/locales/sk.json
+++ b/app/javascript/mastodon/locales/sk.json
@@ -8,11 +8,11 @@
   "account.edit_profile": "Upraviť profil",
   "account.follow": "Následovať",
   "account.followers": "Sledujúci",
-  "account.follows": "Sledujete",
+  "account.follows": "Následuje",
   "account.follows_you": "Následuje ťa",
   "account.hide_reblogs": "Skryť povýšenia od @{name}",
   "account.media": "Médiá",
-  "account.mention": "Spomeňte @{name}",
+  "account.mention": "Spomeň @{name}",
   "account.moved_to": "{name} sa presunul/a na:",
   "account.mute": "Ignorovať @{name}",
   "account.mute_notifications": "Stĺmiť notifikácie od @{name}",
@@ -22,7 +22,7 @@
   "account.report": "Nahlás @{name}",
   "account.requested": "Čaká na schválenie. Kliknite pre zrušenie žiadosti",
   "account.share": "Zdieľať @{name} profil",
-  "account.show_reblogs": "Zobraziť povýšenia od @{name}",
+  "account.show_reblogs": "Ukáž povýšenia od @{name}",
   "account.unblock": "Odblokovať @{name}",
   "account.unblock_domain": "Prestať blokovať {domain}",
   "account.unfollow": "Prestať nasledovať",
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Skúsiť znova",
   "column.blocks": "Blokovaní užívatelia",
   "column.community": "Lokálna časová os",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Obľúbené",
   "column.follow_requests": "Žiadosti o sledovanie",
@@ -83,7 +84,7 @@
   "confirmations.mute.message": "Naozaj chcete ignorovať {name}?",
   "confirmations.unfollow.confirm": "Nesledovať",
   "confirmations.unfollow.message": "Naozaj chcete prestať sledovať {name}?",
-  "embed.instructions": "Umiestnite kód uvedený nižšie pre pridanie tohto statusu na vašu web stránku.",
+  "embed.instructions": "Umiestni kód uvedený nižšie pre pridanie tohto statusu na tvoju web stránku.",
   "embed.preview": "Tu je ako to bude vyzerať:",
   "emoji_button.activity": "Aktivita",
   "emoji_button.custom": "Vlastné",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Symboly",
   "emoji_button.travel": "Cestovanie a miesta",
   "empty_column.community": "Lokálna časová os je prázdna. Napíšte niečo, aby sa to tu začalo hýbať!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Pod týmto hashtagom sa ešte nič nenachádza.",
   "empty_column.home": "Vaša lokálna osa je zatiaľ prázdna! Pre začiatok pozrite {public} alebo použite vyhľadávanie a nájdite tak ostatných používateľov.",
   "empty_column.home.public_timeline": "verejná časová os",
@@ -111,13 +113,13 @@
   "getting_started.appsshort": "Aplikácie",
   "getting_started.faq": "Časté otázky",
   "getting_started.heading": "Začni tu",
-  "getting_started.open_source_notice": "Mastodon má otvorený kód. Nahlásiť chyby, alebo prispievať vlastným kódom môžete na GitHube v {github}.",
+  "getting_started.open_source_notice": "Mastodon má otvorený kód. Nahlásiť chyby, alebo prispieť môžeš na GitHube v {github}.",
   "getting_started.userguide": "Používateľská príručka",
   "home.column_settings.advanced": "Pokročilé",
   "home.column_settings.basic": "Základné",
   "home.column_settings.filter_regex": "Filtrovať použitím regulárnych výrazov",
   "home.column_settings.show_reblogs": "Zobraziť povýšené",
-  "home.column_settings.show_replies": "Zobraziť odpovede",
+  "home.column_settings.show_replies": "Ukázať odpovede",
   "home.settings": "Nastavenia stĺpcov",
   "keyboard_shortcuts.back": "dostať sa naspäť",
   "keyboard_shortcuts.boost": "vyzdvihnúť",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Skryť notifikácie od tohoto užívateľa?",
   "navigation_bar.blocks": "Blokovaní užívatelia",
   "navigation_bar.community_timeline": "Lokálna časová os",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Upraviť profil",
   "navigation_bar.favourites": "Obľúbené",
@@ -169,7 +172,7 @@
   "notification.favourite": "{name} sa páči tvoj status",
   "notification.follow": "{name} ťa začal/a následovať",
   "notification.mention": "{name} ťa spomenul/a",
-  "notification.reblog": "{name} re-tootol tvoj status",
+  "notification.reblog": "{name} zdieľal/a tvoj status",
   "notifications.clear": "Vyčistiť zoznam notifikácii",
   "notifications.clear_confirmation": "Naozaj chcete nenávratne prečistiť všetky vaše notifikácie?",
   "notifications.column_settings.alert": "Notifikácie na ploche",
@@ -235,24 +238,27 @@
   "search_results.accounts": "Ľudia",
   "search_results.hashtags": "Haštagy",
   "search_results.statuses": "Príspevky",
-  "search_results.total": "{count, number} {count, plural, one {result} ostatné {results}}",
-  "standalone.public_title": "Pohľad dovnútra...",
+  "search_results.total": "{count, number} {count, plural, jeden {výsledok} ostatné {výsledky}}",
+  "standalone.public_title": "Náhľad dovnútra...",
   "status.block": "Blokovať @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Tento príspevok nemôže byť re-tootnutý",
   "status.delete": "Zmazať",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Vložiť",
   "status.favourite": "Páči sa mi",
   "status.load_more": "Zobraz viac",
   "status.media_hidden": "Skryté médiá",
-  "status.mention": "Napísať @{name}",
+  "status.mention": "Spomeň @{name}",
   "status.more": "Viac",
   "status.mute": "Utíšiť @{name}",
   "status.mute_conversation": "Ignorovať konverzáciu",
   "status.open": "Otvoriť tento status",
-  "status.pin": "Pripnúť na profil",
+  "status.pin": "Pripni na profil",
   "status.pinned": "Pripnutý príspevok",
   "status.reblog": "Povýšiť",
-  "status.reblogged_by": "{name} povýšil",
+  "status.reblog_private": "Boost to original audience",
+  "status.reblogged_by": "{name} povýšil/a",
   "status.reply": "Odpovedať",
   "status.replyAll": "Odpovedať na diskusiu",
   "status.report": "Nahlásiť @{name}",
@@ -261,7 +267,7 @@
   "status.share": "Zdieľať",
   "status.show_less": "Zobraz menej",
   "status.show_less_all": "Všetkým ukáž menej",
-  "status.show_more": "Zobraz viac",
+  "status.show_more": "Zobraziť viac",
   "status.show_more_all": "Všetkým ukáž viac",
   "status.unmute_conversation": "Prestať ignorovať konverzáciu",
   "status.unpin": "Odopnúť z profilu",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Domov",
   "tabs_bar.local_timeline": "Lokálna",
   "tabs_bar.notifications": "Notifikácie",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Čo máte rozpísané sa stratí, ak opustíte Mastodon.",
   "upload_area.title": "Ťahaj a pusti pre nahratie",
   "upload_button.label": "Pridať médiá",
diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json
index 8fae49a03..e4d07edd1 100644
--- a/app/javascript/mastodon/locales/sr-Latn.json
+++ b/app/javascript/mastodon/locales/sr-Latn.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Pokušajte ponovo",
   "column.blocks": "Blokirani korisnici",
   "column.community": "Lokalna lajna",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Omiljeni",
   "column.follow_requests": "Zahtevi za praćenje",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Simboli",
   "emoji_button.travel": "Putovanja & mesta",
   "empty_column.community": "Lokalna lajna je prazna. Napišite nešto javno da lajna produva!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Trenutno nema ništa na ovom heštegu.",
   "empty_column.home": "Vaša lajna je prazna! Posetite {public} ili koristite pretragu da počnete i upoznajete nove ljude.",
   "empty_column.home.public_timeline": "javna lajna",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Sakrij obaveštenja od ovog korisnika?",
   "navigation_bar.blocks": "Blokirani korisnici",
   "navigation_bar.community_timeline": "Lokalna lajna",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Izmeni profil",
   "navigation_bar.favourites": "Omiljeni",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {rezultat} few {rezultata} other {rezultata}}",
   "standalone.public_title": "Pogled iznutra...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Ovaj status ne može da se podrži",
   "status.delete": "Obriši",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Ugradi na sajt",
   "status.favourite": "Omiljeno",
   "status.load_more": "Učitaj još",
@@ -252,6 +257,7 @@
   "status.pin": "Prikači na profil",
   "status.pinned": "Pinned toot",
   "status.reblog": "Podrži",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} podržao(la)",
   "status.reply": "Odgovori",
   "status.replyAll": "Odgovori na diskusiju",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Početna",
   "tabs_bar.local_timeline": "Lokalno",
   "tabs_bar.notifications": "Obaveštenja",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Ako napustite Mastodont, izgubićete napisani nacrt.",
   "upload_area.title": "Prevucite ovde da otpremite",
   "upload_button.label": "Dodaj multimediju",
diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json
index a39fea582..60c781e9d 100644
--- a/app/javascript/mastodon/locales/sr.json
+++ b/app/javascript/mastodon/locales/sr.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Покушајте поново",
   "column.blocks": "Блокирани корисници",
   "column.community": "Локална лајна",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Омиљени",
   "column.follow_requests": "Захтеви за праћење",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Симболи",
   "emoji_button.travel": "Путовања & места",
   "empty_column.community": "Локална лајна је празна. Напишите нешто јавно да лајна продува!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Тренутно нема ништа на овом хештегу.",
   "empty_column.home": "Ваша лајна је празна! Посетите {public} или користите претрагу да почнете и упознајете нове људе.",
   "empty_column.home.public_timeline": "јавна лајна",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Сакриј обавештења од овог корисника?",
   "navigation_bar.blocks": "Блокирани корисници",
   "navigation_bar.community_timeline": "Локална лајна",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Измени профил",
   "navigation_bar.favourites": "Омиљени",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {резултат} few {резултата} other {резултата}}",
   "standalone.public_title": "Поглед изнутра...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Овај статус не може да се подржи",
   "status.delete": "Обриши",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Угради на сајт",
   "status.favourite": "Омиљено",
   "status.load_more": "Учитај још",
@@ -252,6 +257,7 @@
   "status.pin": "Прикачи на профил",
   "status.pinned": "Pinned toot",
   "status.reblog": "Подржи",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} подржао(ла)",
   "status.reply": "Одговори",
   "status.replyAll": "Одговори на дискусију",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Почетна",
   "tabs_bar.local_timeline": "Локално",
   "tabs_bar.notifications": "Обавештења",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Ако напустите Мастодонт, изгубићете написани нацрт.",
   "upload_area.title": "Превуците овде да отпремите",
   "upload_button.label": "Додај мултимедију",
diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json
index 014492e19..8fa6992f1 100644
--- a/app/javascript/mastodon/locales/sv.json
+++ b/app/javascript/mastodon/locales/sv.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Försök igen",
   "column.blocks": "Blockerade användare",
   "column.community": "Lokal tidslinje",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favoriter",
   "column.follow_requests": "Följ förfrågningar",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Symboler",
   "emoji_button.travel": "Resor & Platser",
   "empty_column.community": "Den lokala tidslinjen är tom. Skriv något offentligt för att få bollen att rulla!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Det finns inget i denna hashtag ännu.",
   "empty_column.home": "Din hemma-tidslinje är tom! Besök {public} eller använd sökning för att komma igång och träffa andra användare.",
   "empty_column.home.public_timeline": "den publika tidslinjen",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Dölj notifikationer från denna användare?",
   "navigation_bar.blocks": "Blockerade användare",
   "navigation_bar.community_timeline": "Lokal tidslinje",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Redigera profil",
   "navigation_bar.favourites": "Favoriter",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, ett {result} andra {results}}",
   "standalone.public_title": "En titt inuti...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Detta inlägg kan inte knuffas",
   "status.delete": "Ta bort",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Bädda in",
   "status.favourite": "Favorit",
   "status.load_more": "Ladda fler",
@@ -252,6 +257,7 @@
   "status.pin": "Fäst i profil",
   "status.pinned": "Fäst toot",
   "status.reblog": "Knuff",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} knuffade",
   "status.reply": "Svara",
   "status.replyAll": "Svara på tråden",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Hem",
   "tabs_bar.local_timeline": "Lokal",
   "tabs_bar.notifications": "Meddelanden",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Ditt utkast kommer att förloras om du lämnar Mastodon.",
   "upload_area.title": "Dra & släpp för att ladda upp",
   "upload_button.label": "Lägg till media",
diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json
index ecfe7c9b5..3b91c0d2c 100644
--- a/app/javascript/mastodon/locales/th.json
+++ b/app/javascript/mastodon/locales/th.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Try again",
   "column.blocks": "Blocked users",
   "column.community": "Local timeline",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favourites",
   "column.follow_requests": "Follow requests",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Symbols",
   "emoji_button.travel": "Travel & Places",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
   "empty_column.home.public_timeline": "the public timeline",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Blocked users",
   "navigation_bar.community_timeline": "Local timeline",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Edit profile",
   "navigation_bar.favourites": "Favourites",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
   "standalone.public_title": "A look inside...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Delete",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Favourite",
   "status.load_more": "Load more",
@@ -252,6 +257,7 @@
   "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.reply": "Reply",
   "status.replyAll": "Reply to thread",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Home",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notifications",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Drag & drop to upload",
   "upload_button.label": "Add media",
diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json
index 58d0e5785..cdf6f46a3 100644
--- a/app/javascript/mastodon/locales/tr.json
+++ b/app/javascript/mastodon/locales/tr.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Try again",
   "column.blocks": "Engellenen kullanıcılar",
   "column.community": "Yerel zaman tüneli",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favoriler",
   "column.follow_requests": "Takip istekleri",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Semboller",
   "emoji_button.travel": "Seyahat ve Yerler",
   "empty_column.community": "Yerel zaman tüneliniz boş. Daha fazla eğlence için herkese açık bir gönderi paylaşın.",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Henüz bu hashtag’e sahip hiçbir gönderi yok.",
   "empty_column.home": "Henüz kimseyi takip etmiyorsunuz. {public} ziyaret edebilir veya arama kısmını kullanarak diğer kullanıcılarla iletişime geçebilirsiniz.",
   "empty_column.home.public_timeline": "herkese açık zaman tüneli",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Engellenen kullanıcılar",
   "navigation_bar.community_timeline": "Yerel zaman tüneli",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Profili düzenle",
   "navigation_bar.favourites": "Favoriler",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {sonuç} other {sonuçlar}}",
   "standalone.public_title": "A look inside...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Bu gönderi boost edilemez",
   "status.delete": "Sil",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Favorilere ekle",
   "status.load_more": "Daha fazla",
@@ -252,6 +257,7 @@
   "status.pin": "Pin on profile",
   "status.pinned": "Pinned toot",
   "status.reblog": "Boost'la",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} boost etti",
   "status.reply": "Cevapla",
   "status.replyAll": "Konuşmayı cevapla",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Ana sayfa",
   "tabs_bar.local_timeline": "Yerel",
   "tabs_bar.notifications": "Bildirimler",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Upload için sürükle bırak yapınız",
   "upload_button.label": "Görsel ekle",
diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json
index 63866d339..261e5795e 100644
--- a/app/javascript/mastodon/locales/uk.json
+++ b/app/javascript/mastodon/locales/uk.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "Try again",
   "column.blocks": "Заблоковані користувачі",
   "column.community": "Локальна стрічка",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Вподобане",
   "column.follow_requests": "Запити на підписку",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "Символи",
   "emoji_button.travel": "Подорожі",
   "empty_column.community": "Локальна стрічка пуста. Напишіть щось, щоб розігріти народ!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "Дописів з цим хештегом поки не існує.",
   "empty_column.home": "Ви поки ні на кого не підписані. Погортайте {public}, або скористуйтесь пошуком, щоб освоїтися та познайомитися з іншими користувачами.",
   "empty_column.home.public_timeline": "публічні стрічки",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "navigation_bar.blocks": "Заблоковані користувачі",
   "navigation_bar.community_timeline": "Локальна стрічка",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Редагувати профіль",
   "navigation_bar.favourites": "Вподобане",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} {count, plural, one {результат} few {результати} many {результатів} other {результатів}}",
   "standalone.public_title": "A look inside...",
   "status.block": "Block @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "Цей допис не може бути передмухнутий",
   "status.delete": "Видалити",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "Подобається",
   "status.load_more": "Завантажити більше",
@@ -252,6 +257,7 @@
   "status.pin": "Pin on profile",
   "status.pinned": "Pinned toot",
   "status.reblog": "Передмухнути",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} передмухнув(-ла)",
   "status.reply": "Відповісти",
   "status.replyAll": "Відповісти на тред",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "Головна",
   "tabs_bar.local_timeline": "Локальна",
   "tabs_bar.notifications": "Сповіщення",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "upload_area.title": "Перетягніть сюди, щоб завантажити",
   "upload_button.label": "Додати медіаконтент",
diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json
index f7cb49632..aba0bde83 100644
--- a/app/javascript/mastodon/locales/zh-CN.json
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "重试",
   "column.blocks": "屏蔽用户",
   "column.community": "本站时间轴",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "收藏过的嘟文",
   "column.follow_requests": "关注请求",
@@ -100,6 +101,7 @@
   "emoji_button.symbols": "符号",
   "emoji_button.travel": "旅行和地点",
   "empty_column.community": "本站时间轴暂时没有内容,快嘟几个来抢头香啊!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "这个话题标签下暂时没有内容。",
   "empty_column.home": "你还没有关注任何用户。快看看{public},向其他用户搭讪吧。",
   "empty_column.home.public_timeline": "公共时间轴",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "同时隐藏来自这个用户的通知",
   "navigation_bar.blocks": "被屏蔽的用户",
   "navigation_bar.community_timeline": "本站时间轴",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "修改个人资料",
   "navigation_bar.favourites": "收藏的内容",
@@ -238,8 +241,10 @@
   "search_results.total": "共 {count, number} 个结果",
   "standalone.public_title": "大家都在干啥?",
   "status.block": "屏蔽 @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "无法转嘟这条嘟文",
   "status.delete": "删除",
+  "status.direct": "Direct message @{name}",
   "status.embed": "嵌入",
   "status.favourite": "收藏",
   "status.load_more": "加载更多",
@@ -252,6 +257,7 @@
   "status.pin": "在个人资料页面置顶",
   "status.pinned": "Pinned toot",
   "status.reblog": "转嘟",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} 转嘟了",
   "status.reply": "回复",
   "status.replyAll": "回复所有人",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "主页",
   "tabs_bar.local_timeline": "本站",
   "tabs_bar.notifications": "通知",
+  "tabs_bar.search": "Search",
   "ui.beforeunload": "如果你现在离开 Mastodon,你的草稿内容将会被丢弃。",
   "upload_area.title": "将文件拖放到此处开始上传",
   "upload_button.label": "上传媒体文件",
diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json
index 1cbc9f1c5..b5ebd20fc 100644
--- a/app/javascript/mastodon/locales/zh-HK.json
+++ b/app/javascript/mastodon/locales/zh-HK.json
@@ -40,6 +40,7 @@
   "bundle_modal_error.retry": "重試",
   "column.blocks": "封鎖用戶",
   "column.community": "本站時間軸",
+  "column.direct": "Direct messages",
   "column.domain_blocks": "隱藏的服務站",
   "column.favourites": "最愛的文章",
   "column.follow_requests": "關注請求",
@@ -74,7 +75,7 @@
   "confirmations.block.confirm": "封鎖",
   "confirmations.block.message": "你確定要封鎖{name}嗎?",
   "confirmations.delete.confirm": "刪除",
-  "confirmations.delete.message": "你確定要刪除{name}嗎?",
+  "confirmations.delete.message": "你確定要刪除這文章嗎?",
   "confirmations.delete_list.confirm": "刪除",
   "confirmations.delete_list.message": "你確定要永久刪除這列表嗎?",
   "confirmations.domain_block.confirm": "隱藏整個網站",
@@ -99,11 +100,12 @@
   "emoji_button.search_results": "搜尋結果",
   "emoji_button.symbols": "符號",
   "emoji_button.travel": "旅遊景物",
-  "empty_column.community": "本站時間軸暫時未有內容,快文章來搶頭香啊!",
+  "empty_column.community": "本站時間軸暫時未有內容,快寫一點東西來搶頭香啊!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "這個標籤暫時未有內容。",
   "empty_column.home": "你還沒有關注任何用戶。快看看{public},向其他用戶搭訕吧。",
   "empty_column.home.public_timeline": "公共時間軸",
-  "empty_column.list": "There is nothing in this list yet.",
+  "empty_column.list": "這個列表暫時未有內容。",
   "empty_column.notifications": "你沒有任何通知紀錄,快向其他用戶搭訕吧。",
   "empty_column.public": "跨站時間軸暫時沒有內容!快寫一些公共的文章,或者關注另一些服務站的用戶吧!你和本站、友站的交流,將決定這裏出現的內容。",
   "follow_request.authorize": "批准",
@@ -154,6 +156,7 @@
   "mute_modal.hide_notifications": "隱藏來自這用戶的通知嗎?",
   "navigation_bar.blocks": "被你封鎖的用戶",
   "navigation_bar.community_timeline": "本站時間軸",
+  "navigation_bar.direct": "Direct messages",
   "navigation_bar.domain_blocks": "隱藏的服務站",
   "navigation_bar.edit_profile": "修改個人資料",
   "navigation_bar.favourites": "最愛的內容",
@@ -238,8 +241,10 @@
   "search_results.total": "{count, number} 項結果",
   "standalone.public_title": "站點一瞥…",
   "status.block": "封鎖 @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "這篇文章無法被轉推",
   "status.delete": "刪除",
+  "status.direct": "私訊 @{name}",
   "status.embed": "鑲嵌",
   "status.favourite": "收藏",
   "status.load_more": "載入更多",
@@ -252,6 +257,7 @@
   "status.pin": "置頂到資料頁",
   "status.pinned": "置頂文章",
   "status.reblog": "轉推",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} 轉推",
   "status.reply": "回應",
   "status.replyAll": "回應所有人",
@@ -269,6 +275,7 @@
   "tabs_bar.home": "主頁",
   "tabs_bar.local_timeline": "本站",
   "tabs_bar.notifications": "通知",
+  "tabs_bar.search": "搜尋",
   "ui.beforeunload": "如果你現在離開 Mastodon,你的草稿內容將會被丟棄。",
   "upload_area.title": "將檔案拖放至此上載",
   "upload_button.label": "上載媒體檔案",
diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json
index c7925829f..28d634600 100644
--- a/app/javascript/mastodon/locales/zh-TW.json
+++ b/app/javascript/mastodon/locales/zh-TW.json
@@ -1,24 +1,24 @@
 {
   "account.block": "封鎖 @{name}",
   "account.block_domain": "隱藏來自 {domain} 的一切貼文",
-  "account.blocked": "Blocked",
-  "account.direct": "Direct message @{name}",
+  "account.blocked": "已被封鎖的",
+  "account.direct": "給 @{name} 私人訊息",
   "account.disclaimer_full": "下列資料不一定完整。",
-  "account.domain_blocked": "Domain hidden",
-  "account.edit_profile": "編輯用者資訊",
+  "account.domain_blocked": "域名隱藏",
+  "account.edit_profile": "編輯使用者資訊",
   "account.follow": "關注",
-  "account.followers": "專注者",
+  "account.followers": "關注者",
   "account.follows": "正關注",
   "account.follows_you": "關注你",
-  "account.hide_reblogs": "Hide boosts from @{name}",
+  "account.hide_reblogs": "隱藏來自 @{name} 的轉推",
   "account.media": "媒體",
   "account.mention": "提到 @{name}",
-  "account.moved_to": "{name} has moved to:",
+  "account.moved_to": "{name} 已經移至:",
   "account.mute": "消音 @{name}",
-  "account.mute_notifications": "Mute notifications from @{name}",
-  "account.muted": "Muted",
+  "account.mute_notifications": "消音來自 @{name} 的通知",
+  "account.muted": "消音的",
   "account.posts": "貼文",
-  "account.posts_with_replies": "Toots with replies",
+  "account.posts_with_replies": "貼文及回覆",
   "account.report": "檢舉 @{name}",
   "account.requested": "正在等待許可",
   "account.share": "分享 @{name} 的用者資訊",
@@ -27,10 +27,10 @@
   "account.unblock_domain": "不再隱藏 {domain}",
   "account.unfollow": "取消關注",
   "account.unmute": "不再消音 @{name}",
-  "account.unmute_notifications": "Unmute notifications from @{name}",
+  "account.unmute_notifications": "不再對來自 @{name} 的通知消音",
   "account.view_full_profile": "查看完整資訊",
-  "alert.unexpected.message": "An unexpected error occurred.",
-  "alert.unexpected.title": "Oops!",
+  "alert.unexpected.message": "發生非預期的錯誤。",
+  "alert.unexpected.title": "哎呀!",
   "boost_modal.combo": "下次你可以按 {combo} 來跳過",
   "bundle_column_error.body": "加載本組件出錯。",
   "bundle_column_error.retry": "重試",
@@ -40,11 +40,12 @@
   "bundle_modal_error.retry": "重試",
   "column.blocks": "封鎖的使用者",
   "column.community": "本地時間軸",
-  "column.domain_blocks": "Hidden domains",
+  "column.direct": "Direct messages",
+  "column.domain_blocks": "隱藏域名",
   "column.favourites": "最愛",
   "column.follow_requests": "關注請求",
   "column.home": "家",
-  "column.lists": "Lists",
+  "column.lists": "名單",
   "column.mutes": "消音的使用者",
   "column.notifications": "通知",
   "column.pins": "置頂貼文",
@@ -58,17 +59,17 @@
   "column_header.unpin": "取下",
   "column_subheading.navigation": "瀏覽",
   "column_subheading.settings": "設定",
-  "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.",
-  "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
+  "compose_form.direct_message_warning": "此則推文只會被所有提到的使用者看見。",
+  "compose_form.hashtag_warning": "此則推文將不會在任何主題標籤中看見,只有公開的推文可以用主題標籤來搜尋。",
   "compose_form.lock_disclaimer": "你的帳號沒有{locked}。任何人都可以關注你,看到發給關注者的貼文。",
   "compose_form.lock_disclaimer.lock": "上鎖",
   "compose_form.placeholder": "在想些什麼?",
   "compose_form.publish": "貼掉",
   "compose_form.publish_loud": "{publish}!",
-  "compose_form.sensitive.marked": "Media is marked as sensitive",
-  "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
-  "compose_form.spoiler.marked": "Text is hidden behind warning",
-  "compose_form.spoiler.unmarked": "Text is not hidden",
+  "compose_form.sensitive.marked": "此媒體已被標註為敏感的",
+  "compose_form.sensitive.unmarked": "此媒體未被標註為敏感的",
+  "compose_form.spoiler.marked": "文字隱藏在警告後",
+  "compose_form.spoiler.unmarked": "文字不是隱藏的",
   "compose_form.spoiler_placeholder": "內容警告",
   "confirmation_modal.cancel": "取消",
   "confirmations.block.confirm": "封鎖",
@@ -86,20 +87,21 @@
   "embed.instructions": "要內嵌此貼文,請將以下代碼貼進你的網站。",
   "embed.preview": "看上去會變成這樣:",
   "emoji_button.activity": "活動",
-  "emoji_button.custom": "Custom",
+  "emoji_button.custom": "自訂",
   "emoji_button.flags": "旗幟",
-  "emoji_button.food": "食物與飲料",
+  "emoji_button.food": "飲食",
   "emoji_button.label": "插入表情符號",
   "emoji_button.nature": "自然",
-  "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
+  "emoji_button.not_found": "沒有表情符號吼!! (╯°□°)╯︵ ┻━┻",
   "emoji_button.objects": "物件",
   "emoji_button.people": "人",
-  "emoji_button.recent": "Frequently used",
+  "emoji_button.recent": "常用",
   "emoji_button.search": "搜尋…",
-  "emoji_button.search_results": "Search results",
+  "emoji_button.search_results": "搜尋結果",
   "emoji_button.symbols": "符號",
   "emoji_button.travel": "旅遊與地點",
   "empty_column.community": "本地時間軸是空的。公開寫點什麼吧!",
+  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
   "empty_column.hashtag": "這個主題標籤下什麼都沒有。",
   "empty_column.home": "你還沒關注任何人。造訪{public}或利用搜尋功能找到其他用者。",
   "empty_column.home.public_timeline": "公開時間軸",
@@ -108,8 +110,8 @@
   "empty_column.public": "這裡什麼都沒有!公開寫些什麼,或是關注其他副本的使用者。",
   "follow_request.authorize": "授權",
   "follow_request.reject": "拒絕",
-  "getting_started.appsshort": "Apps",
-  "getting_started.faq": "FAQ",
+  "getting_started.appsshort": "應用程式",
+  "getting_started.faq": "常見問答",
   "getting_started.heading": "馬上開始",
   "getting_started.open_source_notice": "Mastodon 是開源軟體。你可以在 GitHub {github} 上做出貢獻或是回報問題。",
   "getting_started.userguide": "使用者指南",
@@ -119,19 +121,19 @@
   "home.column_settings.show_reblogs": "顯示轉推",
   "home.column_settings.show_replies": "顯示回應",
   "home.settings": "欄位設定",
-  "keyboard_shortcuts.back": "to navigate back",
-  "keyboard_shortcuts.boost": "to boost",
+  "keyboard_shortcuts.back": "回到上一個",
+  "keyboard_shortcuts.boost": "到轉推",
   "keyboard_shortcuts.column": "to focus a status in one of the columns",
-  "keyboard_shortcuts.compose": "to focus the compose textarea",
-  "keyboard_shortcuts.description": "Description",
+  "keyboard_shortcuts.compose": "焦點移至撰寫文字區塊",
+  "keyboard_shortcuts.description": "描述",
   "keyboard_shortcuts.down": "to move down in the list",
   "keyboard_shortcuts.enter": "to open status",
-  "keyboard_shortcuts.favourite": "to favourite",
-  "keyboard_shortcuts.heading": "Keyboard Shortcuts",
-  "keyboard_shortcuts.hotkey": "Hotkey",
+  "keyboard_shortcuts.favourite": "收藏",
+  "keyboard_shortcuts.heading": "鍵盤快速鍵",
+  "keyboard_shortcuts.hotkey": "快速鍵",
   "keyboard_shortcuts.legend": "to display this legend",
-  "keyboard_shortcuts.mention": "to mention author",
-  "keyboard_shortcuts.reply": "to reply",
+  "keyboard_shortcuts.mention": "到提到的作者",
+  "keyboard_shortcuts.reply": "到回應",
   "keyboard_shortcuts.search": "to focus search",
   "keyboard_shortcuts.toot": "to start a brand new toot",
   "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
@@ -150,11 +152,12 @@
   "loading_indicator.label": "讀取中...",
   "media_gallery.toggle_visible": "切換可見性",
   "missing_indicator.label": "找不到",
-  "missing_indicator.sublabel": "This resource could not be found",
-  "mute_modal.hide_notifications": "Hide notifications from this user?",
+  "missing_indicator.sublabel": "找不到此資源",
+  "mute_modal.hide_notifications": "隱藏來自這個使用者的通知?",
   "navigation_bar.blocks": "封鎖的使用者",
   "navigation_bar.community_timeline": "本地時間軸",
-  "navigation_bar.domain_blocks": "Hidden domains",
+  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.domain_blocks": "隱藏的域名",
   "navigation_bar.edit_profile": "編輯用者資訊",
   "navigation_bar.favourites": "最愛",
   "navigation_bar.follow_requests": "關注請求",
@@ -197,7 +200,7 @@
   "onboarding.page_six.github": "Mastodon 是自由的開源軟體。你可以在 {github} 上回報臭蟲、請求新功能或是做出貢獻。",
   "onboarding.page_six.guidelines": "社群指南",
   "onboarding.page_six.read_guidelines": "請閱讀 {domain} 的 {guidelines} !",
-  "onboarding.page_six.various_app": "行動 apps",
+  "onboarding.page_six.various_app": "行動版應用程式",
   "onboarding.page_three.profile": "編輯你的大頭貼、自傳和顯示名稱。你也可以在這邊找到其他設定。",
   "onboarding.page_three.search": "利用搜尋列來找到其他人或是主題標籤,像是 {illustration} 或 {introductions} 。用完整的帳號名稱來找不在這個副本上的使用者。",
   "onboarding.page_two.compose": "在編輯欄寫些什麼。可以上傳圖片、改變隱私設定或是用下面的圖示加上內容警告。",
@@ -211,13 +214,13 @@
   "privacy.public.short": "公開貼",
   "privacy.unlisted.long": "不要貼到公開時間軸",
   "privacy.unlisted.short": "不列出來",
-  "regeneration_indicator.label": "Loading…",
+  "regeneration_indicator.label": "載入中…",
   "regeneration_indicator.sublabel": "Your home feed is being prepared!",
-  "relative_time.days": "{number}d",
-  "relative_time.hours": "{number}h",
-  "relative_time.just_now": "now",
-  "relative_time.minutes": "{number}m",
-  "relative_time.seconds": "{number}s",
+  "relative_time.days": "{number} 天",
+  "relative_time.hours": "{number} 時",
+  "relative_time.just_now": "現在",
+  "relative_time.minutes": "{number} 分",
+  "relative_time.seconds": "{number} 秒",
   "reply_indicator.cancel": "取消",
   "report.forward": "Forward to {target}",
   "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
@@ -226,20 +229,22 @@
   "report.submit": "送出",
   "report.target": "通報中",
   "search.placeholder": "搜尋",
-  "search_popout.search_format": "Advanced search format",
+  "search_popout.search_format": "進階搜尋格式",
   "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
-  "search_popout.tips.hashtag": "hashtag",
-  "search_popout.tips.status": "status",
+  "search_popout.tips.hashtag": "主題標籤",
+  "search_popout.tips.status": "狀態",
   "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
-  "search_popout.tips.user": "user",
-  "search_results.accounts": "People",
-  "search_results.hashtags": "Hashtags",
-  "search_results.statuses": "Toots",
+  "search_popout.tips.user": "使用者",
+  "search_results.accounts": "人",
+  "search_results.hashtags": "主題標籤",
+  "search_results.statuses": "推文",
   "search_results.total": "{count, number} 項結果",
   "standalone.public_title": "站點一瞥…",
-  "status.block": "Block @{name}",
+  "status.block": "封鎖 @{name}",
+  "status.cancel_reblog_private": "Unboost",
   "status.cannot_reblog": "此貼文無法轉推",
   "status.delete": "刪除",
+  "status.direct": "Direct message @{name}",
   "status.embed": "Embed",
   "status.favourite": "收藏",
   "status.load_more": "載入更多",
@@ -250,15 +255,16 @@
   "status.mute_conversation": "消音對話",
   "status.open": "展開這個狀態",
   "status.pin": "置頂到個人資訊頁",
-  "status.pinned": "Pinned toot",
+  "status.pinned": "置頂的推文",
   "status.reblog": "轉推",
+  "status.reblog_private": "Boost to original audience",
   "status.reblogged_by": "{name} 轉推了",
   "status.reply": "回應",
   "status.replyAll": "回應這串",
   "status.report": "通報 @{name}",
   "status.sensitive_toggle": "點來看",
   "status.sensitive_warning": "敏感內容",
-  "status.share": "Share",
+  "status.share": "分享",
   "status.show_less": "看少點",
   "status.show_less_all": "Show less for all",
   "status.show_more": "看更多",
@@ -269,17 +275,18 @@
   "tabs_bar.home": "家",
   "tabs_bar.local_timeline": "本地",
   "tabs_bar.notifications": "通知",
-  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
+  "tabs_bar.search": "Search",
+  "ui.beforeunload": "如果離開 Mastodon,你的草稿將會不見。",
   "upload_area.title": "拖放來上傳",
   "upload_button.label": "增加媒體",
-  "upload_form.description": "Describe for the visually impaired",
-  "upload_form.focus": "Crop",
+  "upload_form.description": "爲視障者加上描述",
+  "upload_form.focus": "裁切",
   "upload_form.undo": "復原",
   "upload_progress.label": "上傳中...",
   "video.close": "關閉影片",
-  "video.exit_fullscreen": "退出全熒幕",
+  "video.exit_fullscreen": "退出全螢幕",
   "video.expand": "展開影片",
-  "video.fullscreen": "全熒幕",
+  "video.fullscreen": "全螢幕",
   "video.hide": "隱藏影片",
   "video.mute": "消音",
   "video.pause": "暫停",
diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js
index 87049ea79..46d9d6c8e 100644
--- a/app/javascript/mastodon/reducers/compose.js
+++ b/app/javascript/mastodon/reducers/compose.js
@@ -36,8 +36,6 @@ import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrde
 import uuid from '../uuid';
 import { me } from '../initial_state';
 
-const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d';
-
 const initialState = ImmutableMap({
   mounted: 0,
   sensitive: false,
@@ -137,9 +135,8 @@ const updateSuggestionTags = (state, token) => {
   });
 };
 
-const insertEmoji = (state, position, emojiData) => {
+const insertEmoji = (state, position, emojiData, needsSpace) => {
   const oldText = state.get('text');
-  const needsSpace = emojiData.custom && position > 0 && !allowedAroundShortCode.includes(oldText[position - 1]);
   const emoji = needsSpace ? ' ' + emojiData.native : emojiData.native;
 
   return state.merge({
@@ -288,7 +285,7 @@ export default function compose(state = initialState, action) {
       return state;
     }
   case COMPOSE_EMOJI_INSERT:
-    return insertEmoji(state, action.position, action.emoji);
+    return insertEmoji(state, action.position, action.emoji, action.needsSpace);
   case COMPOSE_UPLOAD_CHANGE_SUCCESS:
     return state
       .set('is_submitting', false)
diff --git a/app/javascript/mastodon/reducers/contexts.js b/app/javascript/mastodon/reducers/contexts.js
index fe8308d0c..ebd01e532 100644
--- a/app/javascript/mastodon/reducers/contexts.js
+++ b/app/javascript/mastodon/reducers/contexts.js
@@ -21,24 +21,31 @@ const normalizeContext = (state, id, ancestors, descendants) => {
   });
 };
 
-const deleteFromContexts = (state, id) => {
-  state.getIn(['descendants', id], ImmutableList()).forEach(descendantId => {
-    state = state.updateIn(['ancestors', descendantId], ImmutableList(), list => list.filterNot(itemId => itemId === id));
-  });
+const deleteFromContexts = (immutableState, ids) => immutableState.withMutations(state => {
+  state.update('ancestors', immutableAncestors => immutableAncestors.withMutations(ancestors => {
+    state.update('descendants', immutableDescendants => immutableDescendants.withMutations(descendants => {
+      ids.forEach(id => {
+        descendants.get(id, ImmutableList()).forEach(descendantId => {
+          ancestors.update(descendantId, ImmutableList(), list => list.filterNot(itemId => itemId === id));
+        });
 
-  state.getIn(['ancestors', id], ImmutableList()).forEach(ancestorId => {
-    state = state.updateIn(['descendants', ancestorId], ImmutableList(), list => list.filterNot(itemId => itemId === id));
-  });
+        ancestors.get(id, ImmutableList()).forEach(ancestorId => {
+          descendants.update(ancestorId, ImmutableList(), list => list.filterNot(itemId => itemId === id));
+        });
 
-  state = state.deleteIn(['descendants', id]).deleteIn(['ancestors', id]);
+        descendants.delete(id);
+        ancestors.delete(id);
+      });
+    }));
+  }));
+});
 
-  return state;
-};
+const filterContexts = (state, relationship, statuses) => {
+  const ownedStatusIds = statuses
+    .filter(status => status.get('account') === relationship.id)
+    .map(status => status.get('id'));
 
-const filterContexts = (state, relationship) => {
-  return state.map(
-    statuses => statuses.filter(
-      status => status.get('account') !== relationship.id));
+  return deleteFromContexts(state, ownedStatusIds);
 };
 
 const updateContext = (state, status, references) => {
@@ -61,11 +68,11 @@ export default function contexts(state = initialState, action) {
   switch(action.type) {
   case ACCOUNT_BLOCK_SUCCESS:
   case ACCOUNT_MUTE_SUCCESS:
-    return filterContexts(state, action.relationship);
+    return filterContexts(state, action.relationship, action.statuses);
   case CONTEXT_FETCH_SUCCESS:
     return normalizeContext(state, action.id, action.ancestors, action.descendants);
   case TIMELINE_DELETE:
-    return deleteFromContexts(state, action.id);
+    return deleteFromContexts(state, [action.id]);
   case TIMELINE_CONTEXT_UPDATE:
     return updateContext(state, action.status, action.references);
   default:
diff --git a/app/javascript/mastodon/reducers/notifications.js b/app/javascript/mastodon/reducers/notifications.js
index 1ac7eb706..da9b8c420 100644
--- a/app/javascript/mastodon/reducers/notifications.js
+++ b/app/javascript/mastodon/reducers/notifications.js
@@ -12,6 +12,7 @@ import {
 } from '../actions/accounts';
 import { TIMELINE_DELETE, TIMELINE_DISCONNECT } from '../actions/timelines';
 import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
+import compareId from '../compare_id';
 
 const initialState = ImmutableMap({
   items: ImmutableList(),
@@ -44,13 +45,6 @@ const normalizeNotification = (state, notification) => {
   });
 };
 
-const newer = (m, n) => {
-  const mId = m.get('id');
-  const nId = n.get('id');
-
-  return mId.length === nId.length ? mId > nId : mId.length > nId.length;
-};
-
 const expandNormalizedNotifications = (state, notifications, next) => {
   let items = ImmutableList();
 
@@ -62,11 +56,11 @@ const expandNormalizedNotifications = (state, notifications, next) => {
     if (!items.isEmpty()) {
       mutable.update('items', list => {
         const lastIndex = 1 + list.findLastIndex(
-          item => item !== null && (newer(item, items.last()) || item.get('id') === items.last().get('id'))
+          item => item !== null && (compareId(item.get('id'), items.last().get('id')) > 0 || item.get('id') === items.last().get('id'))
         );
 
         const firstIndex = 1 + list.take(lastIndex).findLastIndex(
-          item => item !== null && newer(item, items.first())
+          item => item !== null && compareId(item.get('id'), items.first().get('id')) > 0
         );
 
         return list.take(firstIndex).concat(items, list.skip(lastIndex));
diff --git a/app/javascript/mastodon/reducers/settings.js b/app/javascript/mastodon/reducers/settings.js
index 390b2a13a..9ec52a7fa 100644
--- a/app/javascript/mastodon/reducers/settings.js
+++ b/app/javascript/mastodon/reducers/settings.js
@@ -58,6 +58,12 @@ const initialState = ImmutableMap({
       body: '',
     }),
   }),
+
+  direct: ImmutableMap({
+    regex: ImmutableMap({
+      body: '',
+    }),
+  }),
 });
 
 const defaultColumns = fromJS([
diff --git a/app/javascript/mastodon/reducers/timelines.js b/app/javascript/mastodon/reducers/timelines.js
index f795e7e08..ad897bcc9 100644
--- a/app/javascript/mastodon/reducers/timelines.js
+++ b/app/javascript/mastodon/reducers/timelines.js
@@ -13,6 +13,7 @@ import {
   ACCOUNT_UNFOLLOW_SUCCESS,
 } from '../actions/accounts';
 import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
+import compareId from '../compare_id';
 
 const initialState = ImmutableMap();
 
@@ -32,8 +33,8 @@ const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial) =>
     if (!statuses.isEmpty()) {
       mMap.update('items', ImmutableList(), oldIds => {
         const newIds = statuses.map(status => status.get('id'));
-        const lastIndex = oldIds.findLastIndex(id => id !== null && id >= newIds.last()) + 1;
-        const firstIndex = oldIds.take(lastIndex).findLastIndex(id => id !== null && id > newIds.first());
+        const lastIndex = oldIds.findLastIndex(id => id !== null && compareId(id, newIds.last()) >= 0) + 1;
+        const firstIndex = oldIds.take(lastIndex).findLastIndex(id => id !== null && compareId(id, newIds.first()) >= 0);
 
         if (firstIndex < 0) {
           return (isPartial ? newIds.unshift(null) : newIds).concat(oldIds.skip(lastIndex));
diff --git a/app/javascript/styles/mastodon/about.scss b/app/javascript/styles/mastodon/about.scss
index 034c35e8a..0a09a38d2 100644
--- a/app/javascript/styles/mastodon/about.scss
+++ b/app/javascript/styles/mastodon/about.scss
@@ -169,7 +169,7 @@ $small-breakpoint: 960px;
       background: $ui-base-color;
       font-size: 12px;
       font-weight: 500;
-      color: $ui-primary-color;
+      color: $darker-text-color;
       text-transform: uppercase;
       position: relative;
       z-index: 1;
@@ -186,10 +186,10 @@ $small-breakpoint: 960px;
     font-size: 16px;
     line-height: 30px;
     margin-bottom: 12px;
-    color: $ui-primary-color;
+    color: $darker-text-color;
 
     a {
-      color: $ui-highlight-color;
+      color: $highlight-text-color;
       text-decoration: underline;
     }
   }
@@ -202,11 +202,11 @@ $small-breakpoint: 960px;
       text-align: center;
       font-size: 12px;
       line-height: 18px;
-      color: $ui-primary-color;
+      color: $darker-text-color;
       margin-bottom: 0;
 
       a {
-        color: $ui-highlight-color;
+        color: $highlight-text-color;
         text-decoration: underline;
       }
     }
@@ -225,7 +225,7 @@ $small-breakpoint: 960px;
     font-family: inherit;
     font-size: inherit;
     line-height: inherit;
-    color: lighten($ui-primary-color, 10%);
+    color: transparentize($darker-text-color, 0.1);
   }
 
   h1 {
@@ -234,14 +234,14 @@ $small-breakpoint: 960px;
     line-height: 30px;
     font-weight: 500;
     margin-bottom: 20px;
-    color: $ui-secondary-color;
+    color: $primary-text-color;
 
     small {
       font-family: 'mastodon-font-sans-serif', sans-serif;
       display: block;
       font-size: 18px;
       font-weight: 400;
-      color: $ui-base-lighter-color;
+      color: opacify($darker-text-color, 0.1);
     }
   }
 
@@ -251,7 +251,7 @@ $small-breakpoint: 960px;
     line-height: 26px;
     font-weight: 500;
     margin-bottom: 20px;
-    color: $ui-secondary-color;
+    color: $primary-text-color;
   }
 
   h3 {
@@ -260,7 +260,7 @@ $small-breakpoint: 960px;
     line-height: 24px;
     font-weight: 500;
     margin-bottom: 20px;
-    color: $ui-secondary-color;
+    color: $primary-text-color;
   }
 
   h4 {
@@ -269,7 +269,7 @@ $small-breakpoint: 960px;
     line-height: 24px;
     font-weight: 500;
     margin-bottom: 20px;
-    color: $ui-secondary-color;
+    color: $primary-text-color;
   }
 
   h5 {
@@ -278,7 +278,7 @@ $small-breakpoint: 960px;
     line-height: 24px;
     font-weight: 500;
     margin-bottom: 20px;
-    color: $ui-secondary-color;
+    color: $primary-text-color;
   }
 
   h6 {
@@ -287,7 +287,7 @@ $small-breakpoint: 960px;
     line-height: 24px;
     font-weight: 500;
     margin-bottom: 20px;
-    color: $ui-secondary-color;
+    color: $primary-text-color;
   }
 
   ul,
@@ -354,10 +354,10 @@ $small-breakpoint: 960px;
         font-weight: 400;
         font-size: 16px;
         line-height: 30px;
-        color: $ui-primary-color;
+        color: $darker-text-color;
 
         a {
-          color: $ui-highlight-color;
+          color: $highlight-text-color;
           text-decoration: underline;
         }
       }
@@ -405,7 +405,7 @@ $small-breakpoint: 960px;
         font-size: 14px;
 
         &:hover {
-          color: $ui-secondary-color;
+          color: $darker-text-color;
         }
       }
 
@@ -478,10 +478,10 @@ $small-breakpoint: 960px;
     font-weight: 400;
     font-size: 16px;
     line-height: 30px;
-    color: $ui-primary-color;
+    color: $darker-text-color;
 
     a {
-      color: $ui-highlight-color;
+      color: $highlight-text-color;
       text-decoration: underline;
     }
   }
@@ -517,7 +517,7 @@ $small-breakpoint: 960px;
 
       span {
         &:last-child {
-          color: $ui-secondary-color;
+          color: $darker-text-color;
         }
       }
 
@@ -548,7 +548,7 @@ $small-breakpoint: 960px;
         font-size: 14px;
         line-height: 24px;
         font-weight: 500;
-        color: $ui-primary-color;
+        color: $darker-text-color;
         padding-bottom: 5px;
         margin-bottom: 15px;
         border-bottom: 1px solid lighten($ui-base-color, 4%);
@@ -559,7 +559,7 @@ $small-breakpoint: 960px;
         a,
         span {
           font-weight: 400;
-          color: darken($ui-primary-color, 10%);
+          color: opacify($darker-text-color, 0.1);
         }
 
         a {
@@ -602,7 +602,7 @@ $small-breakpoint: 960px;
 
         .username {
           display: block;
-          color: $ui-primary-color;
+          color: $darker-text-color;
         }
       }
     }
@@ -775,7 +775,7 @@ $small-breakpoint: 960px;
     }
 
     p a {
-      color: $ui-secondary-color;
+      color: $darker-text-color;
     }
 
     h1 {
@@ -784,10 +784,10 @@ $small-breakpoint: 960px;
       margin-bottom: 0;
 
       small {
-        color: $ui-primary-color;
+        color: $darker-text-color;
 
         span {
-          color: $ui-secondary-color;
+          color: $darker-text-color;
         }
       }
     }
@@ -896,7 +896,7 @@ $small-breakpoint: 960px;
       }
 
       a {
-        color: $ui-secondary-color;
+        color: $darker-text-color;
         text-decoration: none;
       }
     }
@@ -935,7 +935,7 @@ $small-breakpoint: 960px;
 
         .fa {
           display: block;
-          color: $ui-primary-color;
+          color: $darker-text-color;
           font-size: 48px;
         }
       }
@@ -943,7 +943,7 @@ $small-breakpoint: 960px;
       .text {
         font-size: 16px;
         line-height: 30px;
-        color: $ui-primary-color;
+        color: $darker-text-color;
 
         h6 {
           font-size: inherit;
@@ -969,10 +969,10 @@ $small-breakpoint: 960px;
     font-weight: 400;
     font-size: 16px;
     line-height: 30px;
-    color: $ui-primary-color;
+    color: $darker-text-color;
 
     a {
-      color: $ui-highlight-color;
+      color: $highlight-text-color;
       text-decoration: underline;
     }
   }
@@ -980,7 +980,7 @@ $small-breakpoint: 960px;
   .footer-links {
     padding-bottom: 50px;
     text-align: right;
-    color: $ui-base-lighter-color;
+    color: $darker-text-color;
 
     p {
       font-size: 14px;
@@ -995,7 +995,7 @@ $small-breakpoint: 960px;
   &__footer {
     margin-top: 10px;
     text-align: center;
-    color: $ui-base-lighter-color;
+    color: $darker-text-color;
 
     p {
       font-size: 14px;
diff --git a/app/javascript/styles/mastodon/accounts.scss b/app/javascript/styles/mastodon/accounts.scss
index dd82ab375..f9af6f288 100644
--- a/app/javascript/styles/mastodon/accounts.scss
+++ b/app/javascript/styles/mastodon/accounts.scss
@@ -75,7 +75,7 @@
     small {
       display: block;
       font-size: 14px;
-      color: $ui-highlight-color;
+      color: $highlight-text-color;
       font-weight: 400;
       overflow: hidden;
       text-overflow: ellipsis;
@@ -113,7 +113,7 @@
     width: 33.3%;
     box-sizing: border-box;
     flex: 0 0 auto;
-    color: $ui-primary-color;
+    color: $darker-text-color;
     padding: 5px 10px 0;
     margin-bottom: 10px;
     border-right: 1px solid lighten($ui-base-color, 4%);
@@ -143,7 +143,7 @@
 
     &.active {
       &::after {
-        border-bottom: 4px solid $ui-highlight-color;
+        border-bottom: 4px solid $highlight-text-color;
         opacity: 1;
       }
     }
@@ -178,7 +178,7 @@
     font-size: 14px;
     line-height: 18px;
     padding: 0 15px;
-    color: $ui-secondary-color;
+    color: $darker-text-color;
   }
 
   @media screen and (max-width: 480px) {
@@ -256,7 +256,7 @@
   .current {
     background: $simple-background-color;
     border-radius: 100px;
-    color: $ui-base-color;
+    color: $lighter-text-color;
     cursor: default;
     margin: 0 10px;
   }
@@ -268,7 +268,7 @@
   .older,
   .newer {
     text-transform: uppercase;
-    color: $ui-secondary-color;
+    color: $primary-text-color;
   }
 
   .older {
@@ -293,7 +293,7 @@
 
   .disabled {
     cursor: default;
-    color: lighten($ui-base-color, 10%);
+    color: opacify($lighter-text-color, 0.1);
   }
 
   @media screen and (max-width: 700px) {
@@ -332,7 +332,7 @@
     width: 335px;
     background: $simple-background-color;
     border-radius: 4px;
-    color: $ui-base-color;
+    color: $lighter-text-color;
     margin: 0 5px 10px;
     position: relative;
 
@@ -344,7 +344,7 @@
       overflow: hidden;
       height: 100px;
       border-radius: 4px 4px 0 0;
-      background-color: lighten($ui-base-color, 4%);
+      background-color: opacify($lighter-text-color, 0.04);
       background-size: cover;
       background-position: center;
       position: relative;
@@ -392,7 +392,7 @@
 
       a {
         display: block;
-        color: $ui-base-color;
+        color: $inverted-text-color;
         text-decoration: none;
         text-overflow: ellipsis;
         overflow: hidden;
@@ -414,7 +414,7 @@
     }
 
     .username {
-      color: lighten($ui-base-color, 34%);
+      color: $lighter-text-color;
       font-size: 14px;
       font-weight: 400;
     }
@@ -422,7 +422,7 @@
     .account__header__content {
       padding: 10px 15px;
       padding-top: 15px;
-      color: lighten($ui-base-color, 26%);
+      color: transparentize($lighter-text-color, 0.1);
       word-wrap: break-word;
       overflow: hidden;
       text-overflow: ellipsis;
@@ -434,7 +434,7 @@
 .nothing-here {
   width: 100%;
   display: block;
-  color: $ui-primary-color;
+  color: $lighter-text-color;
   font-size: 14px;
   font-weight: 500;
   text-align: center;
@@ -493,7 +493,7 @@
 
       span {
         font-size: 14px;
-        color: $ui-primary-color;
+        color: $inverted-text-color;
       }
     }
 
@@ -508,7 +508,7 @@
 
   .account__header__content {
     font-size: 14px;
-    color: $ui-base-color;
+    color: $darker-text-color;
   }
 }
 
@@ -522,18 +522,18 @@
     display: inline-block;
     padding: 15px;
     text-decoration: none;
-    color: $ui-highlight-color;
+    color: $highlight-text-color;
     text-transform: uppercase;
     font-weight: 500;
 
     &:hover,
     &:active,
     &:focus {
-      color: lighten($ui-highlight-color, 8%);
+      color: lighten($highlight-text-color, 8%);
     }
 
     &.active {
-      color: $ui-base-color;
+      color: $inverted-text-color;
       cursor: default;
     }
   }
@@ -563,3 +563,57 @@
     border-color: rgba(lighten($error-red, 12%), 0.5);
   }
 }
+
+.account__header__fields {
+  border-collapse: collapse;
+  padding: 0;
+  margin: 15px -15px -15px;
+  border: 0 none;
+  border-top: 1px solid lighten($ui-base-color, 4%);
+  border-bottom: 1px solid lighten($ui-base-color, 4%);
+
+  th,
+  td {
+    padding: 15px;
+    padding-left: 15px;
+    border: 0 none;
+    border-bottom: 1px solid lighten($ui-base-color, 4%);
+    vertical-align: middle;
+  }
+
+  th {
+    padding-left: 15px;
+    font-weight: 500;
+    text-align: center;
+    width: 94px;
+    color: opacify($darker-text-color, 0.1);
+    background: rgba(darken($ui-base-color, 8%), 0.5);
+  }
+
+  td {
+    color: $darker-text-color;
+    text-align: center;
+    width: 100%;
+    padding-left: 0;
+  }
+
+  a {
+    color: $highlight-text-color;
+    text-decoration: none;
+
+    &:hover,
+    &:focus,
+    &:active {
+      text-decoration: underline;
+    }
+  }
+
+  tr {
+    &:last-child {
+      th,
+      td {
+        border-bottom: 0;
+      }
+    }
+  }
+}
diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss
index 6bd659030..348f72078 100644
--- a/app/javascript/styles/mastodon/admin.scss
+++ b/app/javascript/styles/mastodon/admin.scss
@@ -33,7 +33,7 @@
       a {
         display: block;
         padding: 15px;
-        color: rgba($primary-text-color, 0.7);
+        color: $darker-text-color;
         text-decoration: none;
         transition: all 200ms linear;
         border-radius: 4px 0 0 4px;
@@ -90,7 +90,7 @@
     padding-left: 25px;
 
     h2 {
-      color: $ui-secondary-color;
+      color: $primary-text-color;
       font-size: 24px;
       line-height: 28px;
       font-weight: 400;
@@ -98,7 +98,7 @@
     }
 
     h3 {
-      color: $ui-secondary-color;
+      color: $primary-text-color;
       font-size: 20px;
       line-height: 28px;
       font-weight: 400;
@@ -109,7 +109,7 @@
       text-transform: uppercase;
       font-size: 13px;
       font-weight: 500;
-      color: $ui-primary-color;
+      color: $primary-text-color;
       padding-bottom: 8px;
       margin-bottom: 8px;
       border-bottom: 1px solid lighten($ui-base-color, 8%);
@@ -117,7 +117,7 @@
 
     h6 {
       font-size: 16px;
-      color: $ui-secondary-color;
+      color: $primary-text-color;
       line-height: 28px;
       font-weight: 400;
     }
@@ -125,7 +125,7 @@
     & > p {
       font-size: 14px;
       line-height: 18px;
-      color: $ui-secondary-color;
+      color: $darker-text-color;
       margin-bottom: 20px;
 
       strong {
@@ -153,10 +153,10 @@
     }
 
     .muted-hint {
-      color: $ui-primary-color;
+      color: $darker-text-color;
 
       a {
-        color: $ui-highlight-color;
+        color: $highlight-text-color;
       }
     }
 
@@ -253,7 +253,7 @@
 
     a {
       display: inline-block;
-      color: rgba($primary-text-color, 0.7);
+      color: $darker-text-color;
       text-decoration: none;
       text-transform: uppercase;
       font-size: 12px;
@@ -266,7 +266,7 @@
       }
 
       &.selected {
-        color: $ui-highlight-color;
+        color: $highlight-text-color;
         border-bottom: 2px solid $ui-highlight-color;
       }
     }
@@ -291,7 +291,7 @@
     font-weight: 500;
     font-size: 14px;
     line-height: 18px;
-    color: $ui-secondary-color;
+    color: $primary-text-color;
 
     @each $lang in $cjk-langs {
       &:lang(#{$lang}) {
@@ -348,7 +348,7 @@
     padding: 7px 4px;
     margin-bottom: 10px;
     font-size: 16px;
-    color: $ui-base-color;
+    color: $inverted-text-color;
     display: block;
     width: 100%;
     outline: 0;
@@ -402,7 +402,7 @@
   font-size: 14px;
 
   a {
-    color: $classic-highlight-color;
+    color: $highlight-text-color;
     text-decoration: none;
 
     &:hover {
@@ -425,7 +425,7 @@
     align-items: center;
     padding: 10px;
     background: $ui-base-color;
-    color: $ui-primary-color;
+    color: $darker-text-color;
     border-radius: 4px 4px 0 0;
     font-size: 14px;
     position: relative;
@@ -452,14 +452,14 @@
   }
 
   &__timestamp {
-    color: lighten($ui-base-color, 34%);
+    color: $darker-text-color;
   }
 
   &__extras {
     background: lighten($ui-base-color, 6%);
     border-radius: 0 0 4px 4px;
     padding: 10px;
-    color: $ui-primary-color;
+    color: $darker-text-color;
     font-family: 'mastodon-font-monospace', monospace;
     font-size: 12px;
     word-wrap: break-word;
@@ -469,7 +469,7 @@
   &__icon {
     font-size: 28px;
     margin-right: 10px;
-    color: lighten($ui-base-color, 34%);
+    color: $darker-text-color;
   }
 
   &__icon__overlay {
@@ -485,7 +485,7 @@
     }
 
     &.negative {
-      background: $error-red;
+      background: lighten($error-red, 12%);
     }
 
     &.neutral {
@@ -496,17 +496,17 @@
   a,
   .username,
   .target {
-    color: $ui-secondary-color;
+    color: $primary-text-color;
     text-decoration: none;
     font-weight: 500;
   }
 
   .diff-old {
-    color: $error-red;
+    color: lighten($error-red, 12%);
   }
 
   .diff-neutral {
-    color: $ui-secondary-color;
+    color: $darker-text-color;
   }
 
   .diff-new {
diff --git a/app/javascript/styles/mastodon/basics.scss b/app/javascript/styles/mastodon/basics.scss
index bec0d4d91..c52e069be 100644
--- a/app/javascript/styles/mastodon/basics.scss
+++ b/app/javascript/styles/mastodon/basics.scss
@@ -75,7 +75,7 @@ body {
   &.error {
     position: absolute;
     text-align: center;
-    color: $ui-primary-color;
+    color: $darker-text-color;
     background: $ui-base-color;
     width: 100%;
     height: 100%;
diff --git a/app/javascript/styles/mastodon/boost.scss b/app/javascript/styles/mastodon/boost.scss
index 31053decc..8e11cb596 100644
--- a/app/javascript/styles/mastodon/boost.scss
+++ b/app/javascript/styles/mastodon/boost.scss
@@ -6,13 +6,13 @@
 }
 
 button.icon-button i.fa-retweet {
-  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='209'><path d='M4.97 3.16c-.1.03-.17.1-.22.18L.8 8.24c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77L5.5 3.35c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.02-2.4.02H7.1l2.32 2.85.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($ui-base-lighter-color)}' stroke-width='0'/><path d='M7.78 19.66c-.24.02-.44.25-.44.5v2.46h-.06c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v4.47c0 4.26-.56 3.62 3.65 3.62H8.5l-1.3-1.06c-.1-.08-.18-.2-.2-.3-.02-.17.06-.35.2-.45l1.33-1.1H7.28c-.44 0-.72-.3-.72-.7v-4.48c0-.44.28-.72.72-.72h.06v2.5c0 .38.54.63.82.38l4.9-3.93c.25-.18.25-.6 0-.78l-4.9-3.92c-.1-.1-.24-.14-.38-.12zm9.34 2.93c-.54-.02-1.3.02-2.4.02h-1.25l1.3 1.07c.1.07.18.2.2.33.02.16-.06.3-.2.4l-1.33 1.1h1.28c.42 0 .72.28.72.72v4.47c0 .42-.3.72-.72.72h-.1v-2.47c0-.3-.3-.53-.6-.47-.07 0-.14.05-.2.1l-4.9 3.93c-.26.18-.26.6 0 .78l4.9 3.92c.27.25.82 0 .8-.38v-2.5h.1c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.15.4-3.62-1.25-3.66zM10.34 38.66c-.24.02-.44.25-.43.5v2.47H7.3c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.47c0 3.66-.23 3.7 2.34 3.66l-1.34-1.1c-.1-.08-.18-.2-.2-.3 0-.17.07-.35.2-.45l1.96-1.6c-.03-.06-.04-.13-.04-.2v-4.48c0-.44.28-.72.72-.72H9.9v2.5c0 .36.5.6.8.38l4.93-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.08-.23-.13-.36-.12zm5.63 2.93l1.34 1.1c.1.07.18.2.2.33.02.16-.03.3-.16.4l-1.96 1.6c.02.07.06.13.06.22v4.47c0 .42-.3.72-.72.72h-2.66v-2.47c0-.3-.3-.53-.6-.47-.06.02-.12.05-.18.1l-4.94 3.93c-.24.18-.24.6 0 .78l4.94 3.92c.28.22.78-.02.78-.38v-2.5h2.66c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.66.34-3.7-2.4-3.66zM13.06 57.66c-.23.03-.4.26-.4.5v2.47H7.28c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.87l2.93-2.37v-2.5c0-.44.28-.72.72-.72h5.38v2.5c0 .36.5.6.78.38l4.94-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.1-.24-.14-.38-.12zm5.3 6.15l-2.92 2.4v2.52c0 .42-.3.72-.72.72h-5.4v-2.47c0-.3-.32-.53-.6-.47-.07.02-.13.05-.2.1L3.6 70.52c-.25.18-.25.6 0 .78l4.93 3.92c.28.22.78-.02.78-.38v-2.5h5.42c4.27 0 3.65.67 3.65-3.62v-4.47-.44zM19.25 78.8c-.1.03-.2.1-.28.17l-.9.9c-.44-.3-1.36-.25-3.35-.25H7.28c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v.7l2.93.3v-1c0-.44.28-.72.72-.72h7.44c.2 0 .37.08.5.2l-1.8 1.8c-.25.26-.08.76.27.8l6.27.7c.28.03.56-.25.53-.53l-.7-6.25c0-.27-.3-.48-.55-.44zm-17.2 6.1c-.2.07-.36.3-.33.54l.7 6.25c.02.36.58.55.83.27l.8-.8c.02 0 .04-.02.04 0 .46.24 1.37.17 3.18.17h7.44c4.27 0 3.65.67 3.65-3.62v-.75l-2.93-.3v1.05c0 .42-.3.72-.72.72H7.28c-.15 0-.3-.03-.4-.1L8.8 86.4c.3-.24.1-.8-.27-.84l-6.28-.65h-.2zM4.88 98.6c-1.33 0-1.34.48-1.3 2.3l1.14-1.37c.08-.1.22-.17.34-.2.16 0 .34.08.44.2l1.66 2.03c.04 0 .07-.03.12-.03h7.44c.34 0 .57.2.65.5h-2.43c-.34.05-.53.52-.3.78l3.92 4.95c.18.24.6.24.78 0l3.94-4.94c.22-.27-.02-.76-.37-.77H18.4c.02-3.9.6-3.4-3.66-3.4H7.28c-1.08 0-1.86-.04-2.4-.04zm.15 2.46c-.1.03-.2.1-.28.2l-3.94 4.9c-.2.28.03.77.4.78H3.6c-.02 3.94-.45 3.4 3.66 3.4h7.44c3.65 0 3.74.3 3.7-2.25l-1.1 1.34c-.1.1-.2.17-.32.2-.16 0-.34-.08-.44-.2l-1.65-2.03c-.06.02-.1.04-.18.04H7.28c-.35 0-.57-.2-.66-.5h2.44c.37 0 .63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.23-.47-.2zM4.88 117.6c-1.16 0-1.3.3-1.3 1.56l1.14-1.38c.08-.1.22-.14.34-.16.16 0 .34.04.44.16l2.22 2.75h7c.42 0 .72.28.72.72v.53h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-.53c0-4.2.72-3.63-3.66-3.63H7.28c-1.08 0-1.86-.03-2.4-.03zm.1 1.74c-.1.03-.17.1-.23.16L.8 124.44c-.2.28.03.77.4.78H3.6v.5c0 4.26-.55 3.62 3.66 3.62h7.44c1.03 0 1.74.02 2.28 0-.16.02-.34-.03-.44-.15l-2.22-2.76H7.28c-.44 0-.72-.3-.72-.72v-.5h2.5c.37.02.63-.5.4-.78L5.5 119.5c-.12-.15-.34-.22-.53-.16zm12.02 10c1.2-.02 1.4-.25 1.4-1.53l-1.1 1.36c-.07.1-.17.17-.3.18zM5.94 136.6l2.37 2.93h6.42c.42 0 .72.28.72.72v1.25h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.25c0-4.2.72-3.63-3.66-3.63H7.28c-.6 0-.92-.02-1.34-.03zm-1.72.06c-.4.08-.54.3-.6.75l.6-.74zm.84.93c-.12 0-.24.08-.3.18l-3.95 4.9c-.24.3 0 .83.4.82H3.6v1.22c0 4.26-.55 3.62 3.66 3.62h7.44c.63 0 .97.02 1.4.03l-2.37-2.93H7.28c-.44 0-.72-.3-.72-.72v-1.22h2.5c.4.04.67-.53.4-.8l-3.96-4.92c-.1-.13-.27-.2-.44-.2zm13.28 10.03l-.56.7c.36-.07.5-.3.56-.7zM17.13 155.6c-.55-.02-1.32.03-2.4.03h-8.2l2.38 2.9h5.82c.42 0 .72.28.72.72v1.97H12.9c-.32.06-.48.52-.28.78l3.94 4.94c.2.23.6.22.78-.03l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.97c0-3.15.4-3.62-1.25-3.66zm-12.1.28c-.1.02-.2.1-.28.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v1.96c0 4.26-.55 3.62 3.66 3.62h8.24l-2.36-2.9H7.28c-.44 0-.72-.3-.72-.72v-1.97h2.5c.37.02.63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.22-.47-.2zM5.13 174.5c-.15 0-.3.07-.38.2L.8 179.6c-.24.27 0 .82.4.8H3.6v2.32c0 4.26-.55 3.62 3.66 3.62h7.94l-2.35-2.9h-5.6c-.43 0-.7-.3-.7-.72v-2.3h2.5c.38.03.66-.54.4-.83l-3.97-4.9c-.1-.13-.23-.2-.38-.2zm12 .1c-.55-.02-1.32.03-2.4.03H6.83l2.35 2.9h5.52c.42 0 .72.28.72.72v2.34h-2.6c-.3.1-.43.53-.2.78l3.92 4.9c.18.24.6.24.78 0l3.94-4.9c.22-.3-.02-.78-.37-.8H18.4v-2.33c0-3.15.4-3.62-1.25-3.66zM4.97 193.16c-.1.03-.17.1-.22.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77l-3.96-4.9c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.03-2.4.03H7.1l2.32 2.84.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($ui-highlight-color)}' stroke-width='0'/></svg>");
+  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='209'><path d='M4.97 3.16c-.1.03-.17.1-.22.18L.8 8.24c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77L5.5 3.35c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.02-2.4.02H7.1l2.32 2.85.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($action-button-color)}' stroke-width='0'/><path d='M7.78 19.66c-.24.02-.44.25-.44.5v2.46h-.06c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v4.47c0 4.26-.56 3.62 3.65 3.62H8.5l-1.3-1.06c-.1-.08-.18-.2-.2-.3-.02-.17.06-.35.2-.45l1.33-1.1H7.28c-.44 0-.72-.3-.72-.7v-4.48c0-.44.28-.72.72-.72h.06v2.5c0 .38.54.63.82.38l4.9-3.93c.25-.18.25-.6 0-.78l-4.9-3.92c-.1-.1-.24-.14-.38-.12zm9.34 2.93c-.54-.02-1.3.02-2.4.02h-1.25l1.3 1.07c.1.07.18.2.2.33.02.16-.06.3-.2.4l-1.33 1.1h1.28c.42 0 .72.28.72.72v4.47c0 .42-.3.72-.72.72h-.1v-2.47c0-.3-.3-.53-.6-.47-.07 0-.14.05-.2.1l-4.9 3.93c-.26.18-.26.6 0 .78l4.9 3.92c.27.25.82 0 .8-.38v-2.5h.1c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.15.4-3.62-1.25-3.66zM10.34 38.66c-.24.02-.44.25-.43.5v2.47H7.3c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.47c0 3.66-.23 3.7 2.34 3.66l-1.34-1.1c-.1-.08-.18-.2-.2-.3 0-.17.07-.35.2-.45l1.96-1.6c-.03-.06-.04-.13-.04-.2v-4.48c0-.44.28-.72.72-.72H9.9v2.5c0 .36.5.6.8.38l4.93-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.08-.23-.13-.36-.12zm5.63 2.93l1.34 1.1c.1.07.18.2.2.33.02.16-.03.3-.16.4l-1.96 1.6c.02.07.06.13.06.22v4.47c0 .42-.3.72-.72.72h-2.66v-2.47c0-.3-.3-.53-.6-.47-.06.02-.12.05-.18.1l-4.94 3.93c-.24.18-.24.6 0 .78l4.94 3.92c.28.22.78-.02.78-.38v-2.5h2.66c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.66.34-3.7-2.4-3.66zM13.06 57.66c-.23.03-.4.26-.4.5v2.47H7.28c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.87l2.93-2.37v-2.5c0-.44.28-.72.72-.72h5.38v2.5c0 .36.5.6.78.38l4.94-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.1-.24-.14-.38-.12zm5.3 6.15l-2.92 2.4v2.52c0 .42-.3.72-.72.72h-5.4v-2.47c0-.3-.32-.53-.6-.47-.07.02-.13.05-.2.1L3.6 70.52c-.25.18-.25.6 0 .78l4.93 3.92c.28.22.78-.02.78-.38v-2.5h5.42c4.27 0 3.65.67 3.65-3.62v-4.47-.44zM19.25 78.8c-.1.03-.2.1-.28.17l-.9.9c-.44-.3-1.36-.25-3.35-.25H7.28c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v.7l2.93.3v-1c0-.44.28-.72.72-.72h7.44c.2 0 .37.08.5.2l-1.8 1.8c-.25.26-.08.76.27.8l6.27.7c.28.03.56-.25.53-.53l-.7-6.25c0-.27-.3-.48-.55-.44zm-17.2 6.1c-.2.07-.36.3-.33.54l.7 6.25c.02.36.58.55.83.27l.8-.8c.02 0 .04-.02.04 0 .46.24 1.37.17 3.18.17h7.44c4.27 0 3.65.67 3.65-3.62v-.75l-2.93-.3v1.05c0 .42-.3.72-.72.72H7.28c-.15 0-.3-.03-.4-.1L8.8 86.4c.3-.24.1-.8-.27-.84l-6.28-.65h-.2zM4.88 98.6c-1.33 0-1.34.48-1.3 2.3l1.14-1.37c.08-.1.22-.17.34-.2.16 0 .34.08.44.2l1.66 2.03c.04 0 .07-.03.12-.03h7.44c.34 0 .57.2.65.5h-2.43c-.34.05-.53.52-.3.78l3.92 4.95c.18.24.6.24.78 0l3.94-4.94c.22-.27-.02-.76-.37-.77H18.4c.02-3.9.6-3.4-3.66-3.4H7.28c-1.08 0-1.86-.04-2.4-.04zm.15 2.46c-.1.03-.2.1-.28.2l-3.94 4.9c-.2.28.03.77.4.78H3.6c-.02 3.94-.45 3.4 3.66 3.4h7.44c3.65 0 3.74.3 3.7-2.25l-1.1 1.34c-.1.1-.2.17-.32.2-.16 0-.34-.08-.44-.2l-1.65-2.03c-.06.02-.1.04-.18.04H7.28c-.35 0-.57-.2-.66-.5h2.44c.37 0 .63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.23-.47-.2zM4.88 117.6c-1.16 0-1.3.3-1.3 1.56l1.14-1.38c.08-.1.22-.14.34-.16.16 0 .34.04.44.16l2.22 2.75h7c.42 0 .72.28.72.72v.53h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-.53c0-4.2.72-3.63-3.66-3.63H7.28c-1.08 0-1.86-.03-2.4-.03zm.1 1.74c-.1.03-.17.1-.23.16L.8 124.44c-.2.28.03.77.4.78H3.6v.5c0 4.26-.55 3.62 3.66 3.62h7.44c1.03 0 1.74.02 2.28 0-.16.02-.34-.03-.44-.15l-2.22-2.76H7.28c-.44 0-.72-.3-.72-.72v-.5h2.5c.37.02.63-.5.4-.78L5.5 119.5c-.12-.15-.34-.22-.53-.16zm12.02 10c1.2-.02 1.4-.25 1.4-1.53l-1.1 1.36c-.07.1-.17.17-.3.18zM5.94 136.6l2.37 2.93h6.42c.42 0 .72.28.72.72v1.25h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.25c0-4.2.72-3.63-3.66-3.63H7.28c-.6 0-.92-.02-1.34-.03zm-1.72.06c-.4.08-.54.3-.6.75l.6-.74zm.84.93c-.12 0-.24.08-.3.18l-3.95 4.9c-.24.3 0 .83.4.82H3.6v1.22c0 4.26-.55 3.62 3.66 3.62h7.44c.63 0 .97.02 1.4.03l-2.37-2.93H7.28c-.44 0-.72-.3-.72-.72v-1.22h2.5c.4.04.67-.53.4-.8l-3.96-4.92c-.1-.13-.27-.2-.44-.2zm13.28 10.03l-.56.7c.36-.07.5-.3.56-.7zM17.13 155.6c-.55-.02-1.32.03-2.4.03h-8.2l2.38 2.9h5.82c.42 0 .72.28.72.72v1.97H12.9c-.32.06-.48.52-.28.78l3.94 4.94c.2.23.6.22.78-.03l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.97c0-3.15.4-3.62-1.25-3.66zm-12.1.28c-.1.02-.2.1-.28.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v1.96c0 4.26-.55 3.62 3.66 3.62h8.24l-2.36-2.9H7.28c-.44 0-.72-.3-.72-.72v-1.97h2.5c.37.02.63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.22-.47-.2zM5.13 174.5c-.15 0-.3.07-.38.2L.8 179.6c-.24.27 0 .82.4.8H3.6v2.32c0 4.26-.55 3.62 3.66 3.62h7.94l-2.35-2.9h-5.6c-.43 0-.7-.3-.7-.72v-2.3h2.5c.38.03.66-.54.4-.83l-3.97-4.9c-.1-.13-.23-.2-.38-.2zm12 .1c-.55-.02-1.32.03-2.4.03H6.83l2.35 2.9h5.52c.42 0 .72.28.72.72v2.34h-2.6c-.3.1-.43.53-.2.78l3.92 4.9c.18.24.6.24.78 0l3.94-4.9c.22-.3-.02-.78-.37-.8H18.4v-2.33c0-3.15.4-3.62-1.25-3.66zM4.97 193.16c-.1.03-.17.1-.22.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77l-3.96-4.9c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.03-2.4.03H7.1l2.32 2.84.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($highlight-text-color)}' stroke-width='0'/></svg>");
 
   &:hover {
-    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='209'><path d='M4.97 3.16c-.1.03-.17.1-.22.18L.8 8.24c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77L5.5 3.35c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.02-2.4.02H7.1l2.32 2.85.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color(lighten($ui-base-color, 33%))}' stroke-width='0'/><path d='M7.78 19.66c-.24.02-.44.25-.44.5v2.46h-.06c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v4.47c0 4.26-.56 3.62 3.65 3.62H8.5l-1.3-1.06c-.1-.08-.18-.2-.2-.3-.02-.17.06-.35.2-.45l1.33-1.1H7.28c-.44 0-.72-.3-.72-.7v-4.48c0-.44.28-.72.72-.72h.06v2.5c0 .38.54.63.82.38l4.9-3.93c.25-.18.25-.6 0-.78l-4.9-3.92c-.1-.1-.24-.14-.38-.12zm9.34 2.93c-.54-.02-1.3.02-2.4.02h-1.25l1.3 1.07c.1.07.18.2.2.33.02.16-.06.3-.2.4l-1.33 1.1h1.28c.42 0 .72.28.72.72v4.47c0 .42-.3.72-.72.72h-.1v-2.47c0-.3-.3-.53-.6-.47-.07 0-.14.05-.2.1l-4.9 3.93c-.26.18-.26.6 0 .78l4.9 3.92c.27.25.82 0 .8-.38v-2.5h.1c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.15.4-3.62-1.25-3.66zM10.34 38.66c-.24.02-.44.25-.43.5v2.47H7.3c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.47c0 3.66-.23 3.7 2.34 3.66l-1.34-1.1c-.1-.08-.18-.2-.2-.3 0-.17.07-.35.2-.45l1.96-1.6c-.03-.06-.04-.13-.04-.2v-4.48c0-.44.28-.72.72-.72H9.9v2.5c0 .36.5.6.8.38l4.93-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.08-.23-.13-.36-.12zm5.63 2.93l1.34 1.1c.1.07.18.2.2.33.02.16-.03.3-.16.4l-1.96 1.6c.02.07.06.13.06.22v4.47c0 .42-.3.72-.72.72h-2.66v-2.47c0-.3-.3-.53-.6-.47-.06.02-.12.05-.18.1l-4.94 3.93c-.24.18-.24.6 0 .78l4.94 3.92c.28.22.78-.02.78-.38v-2.5h2.66c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.66.34-3.7-2.4-3.66zM13.06 57.66c-.23.03-.4.26-.4.5v2.47H7.28c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.87l2.93-2.37v-2.5c0-.44.28-.72.72-.72h5.38v2.5c0 .36.5.6.78.38l4.94-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.1-.24-.14-.38-.12zm5.3 6.15l-2.92 2.4v2.52c0 .42-.3.72-.72.72h-5.4v-2.47c0-.3-.32-.53-.6-.47-.07.02-.13.05-.2.1L3.6 70.52c-.25.18-.25.6 0 .78l4.93 3.92c.28.22.78-.02.78-.38v-2.5h5.42c4.27 0 3.65.67 3.65-3.62v-4.47-.44zM19.25 78.8c-.1.03-.2.1-.28.17l-.9.9c-.44-.3-1.36-.25-3.35-.25H7.28c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v.7l2.93.3v-1c0-.44.28-.72.72-.72h7.44c.2 0 .37.08.5.2l-1.8 1.8c-.25.26-.08.76.27.8l6.27.7c.28.03.56-.25.53-.53l-.7-6.25c0-.27-.3-.48-.55-.44zm-17.2 6.1c-.2.07-.36.3-.33.54l.7 6.25c.02.36.58.55.83.27l.8-.8c.02 0 .04-.02.04 0 .46.24 1.37.17 3.18.17h7.44c4.27 0 3.65.67 3.65-3.62v-.75l-2.93-.3v1.05c0 .42-.3.72-.72.72H7.28c-.15 0-.3-.03-.4-.1L8.8 86.4c.3-.24.1-.8-.27-.84l-6.28-.65h-.2zM4.88 98.6c-1.33 0-1.34.48-1.3 2.3l1.14-1.37c.08-.1.22-.17.34-.2.16 0 .34.08.44.2l1.66 2.03c.04 0 .07-.03.12-.03h7.44c.34 0 .57.2.65.5h-2.43c-.34.05-.53.52-.3.78l3.92 4.95c.18.24.6.24.78 0l3.94-4.94c.22-.27-.02-.76-.37-.77H18.4c.02-3.9.6-3.4-3.66-3.4H7.28c-1.08 0-1.86-.04-2.4-.04zm.15 2.46c-.1.03-.2.1-.28.2l-3.94 4.9c-.2.28.03.77.4.78H3.6c-.02 3.94-.45 3.4 3.66 3.4h7.44c3.65 0 3.74.3 3.7-2.25l-1.1 1.34c-.1.1-.2.17-.32.2-.16 0-.34-.08-.44-.2l-1.65-2.03c-.06.02-.1.04-.18.04H7.28c-.35 0-.57-.2-.66-.5h2.44c.37 0 .63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.23-.47-.2zM4.88 117.6c-1.16 0-1.3.3-1.3 1.56l1.14-1.38c.08-.1.22-.14.34-.16.16 0 .34.04.44.16l2.22 2.75h7c.42 0 .72.28.72.72v.53h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-.53c0-4.2.72-3.63-3.66-3.63H7.28c-1.08 0-1.86-.03-2.4-.03zm.1 1.74c-.1.03-.17.1-.23.16L.8 124.44c-.2.28.03.77.4.78H3.6v.5c0 4.26-.55 3.62 3.66 3.62h7.44c1.03 0 1.74.02 2.28 0-.16.02-.34-.03-.44-.15l-2.22-2.76H7.28c-.44 0-.72-.3-.72-.72v-.5h2.5c.37.02.63-.5.4-.78L5.5 119.5c-.12-.15-.34-.22-.53-.16zm12.02 10c1.2-.02 1.4-.25 1.4-1.53l-1.1 1.36c-.07.1-.17.17-.3.18zM5.94 136.6l2.37 2.93h6.42c.42 0 .72.28.72.72v1.25h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.25c0-4.2.72-3.63-3.66-3.63H7.28c-.6 0-.92-.02-1.34-.03zm-1.72.06c-.4.08-.54.3-.6.75l.6-.74zm.84.93c-.12 0-.24.08-.3.18l-3.95 4.9c-.24.3 0 .83.4.82H3.6v1.22c0 4.26-.55 3.62 3.66 3.62h7.44c.63 0 .97.02 1.4.03l-2.37-2.93H7.28c-.44 0-.72-.3-.72-.72v-1.22h2.5c.4.04.67-.53.4-.8l-3.96-4.92c-.1-.13-.27-.2-.44-.2zm13.28 10.03l-.56.7c.36-.07.5-.3.56-.7zM17.13 155.6c-.55-.02-1.32.03-2.4.03h-8.2l2.38 2.9h5.82c.42 0 .72.28.72.72v1.97H12.9c-.32.06-.48.52-.28.78l3.94 4.94c.2.23.6.22.78-.03l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.97c0-3.15.4-3.62-1.25-3.66zm-12.1.28c-.1.02-.2.1-.28.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v1.96c0 4.26-.55 3.62 3.66 3.62h8.24l-2.36-2.9H7.28c-.44 0-.72-.3-.72-.72v-1.97h2.5c.37.02.63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.22-.47-.2zM5.13 174.5c-.15 0-.3.07-.38.2L.8 179.6c-.24.27 0 .82.4.8H3.6v2.32c0 4.26-.55 3.62 3.66 3.62h7.94l-2.35-2.9h-5.6c-.43 0-.7-.3-.7-.72v-2.3h2.5c.38.03.66-.54.4-.83l-3.97-4.9c-.1-.13-.23-.2-.38-.2zm12 .1c-.55-.02-1.32.03-2.4.03H6.83l2.35 2.9h5.52c.42 0 .72.28.72.72v2.34h-2.6c-.3.1-.43.53-.2.78l3.92 4.9c.18.24.6.24.78 0l3.94-4.9c.22-.3-.02-.78-.37-.8H18.4v-2.33c0-3.15.4-3.62-1.25-3.66zM4.97 193.16c-.1.03-.17.1-.22.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77l-3.96-4.9c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.03-2.4.03H7.1l2.32 2.84.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($ui-highlight-color)}' stroke-width='0'/></svg>");
+    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='209'><path d='M4.97 3.16c-.1.03-.17.1-.22.18L.8 8.24c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77L5.5 3.35c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.02-2.4.02H7.1l2.32 2.85.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color(lighten($action-button-color, 7%))}' stroke-width='0'/><path d='M7.78 19.66c-.24.02-.44.25-.44.5v2.46h-.06c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v4.47c0 4.26-.56 3.62 3.65 3.62H8.5l-1.3-1.06c-.1-.08-.18-.2-.2-.3-.02-.17.06-.35.2-.45l1.33-1.1H7.28c-.44 0-.72-.3-.72-.7v-4.48c0-.44.28-.72.72-.72h.06v2.5c0 .38.54.63.82.38l4.9-3.93c.25-.18.25-.6 0-.78l-4.9-3.92c-.1-.1-.24-.14-.38-.12zm9.34 2.93c-.54-.02-1.3.02-2.4.02h-1.25l1.3 1.07c.1.07.18.2.2.33.02.16-.06.3-.2.4l-1.33 1.1h1.28c.42 0 .72.28.72.72v4.47c0 .42-.3.72-.72.72h-.1v-2.47c0-.3-.3-.53-.6-.47-.07 0-.14.05-.2.1l-4.9 3.93c-.26.18-.26.6 0 .78l4.9 3.92c.27.25.82 0 .8-.38v-2.5h.1c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.15.4-3.62-1.25-3.66zM10.34 38.66c-.24.02-.44.25-.43.5v2.47H7.3c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.47c0 3.66-.23 3.7 2.34 3.66l-1.34-1.1c-.1-.08-.18-.2-.2-.3 0-.17.07-.35.2-.45l1.96-1.6c-.03-.06-.04-.13-.04-.2v-4.48c0-.44.28-.72.72-.72H9.9v2.5c0 .36.5.6.8.38l4.93-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.08-.23-.13-.36-.12zm5.63 2.93l1.34 1.1c.1.07.18.2.2.33.02.16-.03.3-.16.4l-1.96 1.6c.02.07.06.13.06.22v4.47c0 .42-.3.72-.72.72h-2.66v-2.47c0-.3-.3-.53-.6-.47-.06.02-.12.05-.18.1l-4.94 3.93c-.24.18-.24.6 0 .78l4.94 3.92c.28.22.78-.02.78-.38v-2.5h2.66c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.66.34-3.7-2.4-3.66zM13.06 57.66c-.23.03-.4.26-.4.5v2.47H7.28c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.87l2.93-2.37v-2.5c0-.44.28-.72.72-.72h5.38v2.5c0 .36.5.6.78.38l4.94-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.1-.24-.14-.38-.12zm5.3 6.15l-2.92 2.4v2.52c0 .42-.3.72-.72.72h-5.4v-2.47c0-.3-.32-.53-.6-.47-.07.02-.13.05-.2.1L3.6 70.52c-.25.18-.25.6 0 .78l4.93 3.92c.28.22.78-.02.78-.38v-2.5h5.42c4.27 0 3.65.67 3.65-3.62v-4.47-.44zM19.25 78.8c-.1.03-.2.1-.28.17l-.9.9c-.44-.3-1.36-.25-3.35-.25H7.28c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v.7l2.93.3v-1c0-.44.28-.72.72-.72h7.44c.2 0 .37.08.5.2l-1.8 1.8c-.25.26-.08.76.27.8l6.27.7c.28.03.56-.25.53-.53l-.7-6.25c0-.27-.3-.48-.55-.44zm-17.2 6.1c-.2.07-.36.3-.33.54l.7 6.25c.02.36.58.55.83.27l.8-.8c.02 0 .04-.02.04 0 .46.24 1.37.17 3.18.17h7.44c4.27 0 3.65.67 3.65-3.62v-.75l-2.93-.3v1.05c0 .42-.3.72-.72.72H7.28c-.15 0-.3-.03-.4-.1L8.8 86.4c.3-.24.1-.8-.27-.84l-6.28-.65h-.2zM4.88 98.6c-1.33 0-1.34.48-1.3 2.3l1.14-1.37c.08-.1.22-.17.34-.2.16 0 .34.08.44.2l1.66 2.03c.04 0 .07-.03.12-.03h7.44c.34 0 .57.2.65.5h-2.43c-.34.05-.53.52-.3.78l3.92 4.95c.18.24.6.24.78 0l3.94-4.94c.22-.27-.02-.76-.37-.77H18.4c.02-3.9.6-3.4-3.66-3.4H7.28c-1.08 0-1.86-.04-2.4-.04zm.15 2.46c-.1.03-.2.1-.28.2l-3.94 4.9c-.2.28.03.77.4.78H3.6c-.02 3.94-.45 3.4 3.66 3.4h7.44c3.65 0 3.74.3 3.7-2.25l-1.1 1.34c-.1.1-.2.17-.32.2-.16 0-.34-.08-.44-.2l-1.65-2.03c-.06.02-.1.04-.18.04H7.28c-.35 0-.57-.2-.66-.5h2.44c.37 0 .63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.23-.47-.2zM4.88 117.6c-1.16 0-1.3.3-1.3 1.56l1.14-1.38c.08-.1.22-.14.34-.16.16 0 .34.04.44.16l2.22 2.75h7c.42 0 .72.28.72.72v.53h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-.53c0-4.2.72-3.63-3.66-3.63H7.28c-1.08 0-1.86-.03-2.4-.03zm.1 1.74c-.1.03-.17.1-.23.16L.8 124.44c-.2.28.03.77.4.78H3.6v.5c0 4.26-.55 3.62 3.66 3.62h7.44c1.03 0 1.74.02 2.28 0-.16.02-.34-.03-.44-.15l-2.22-2.76H7.28c-.44 0-.72-.3-.72-.72v-.5h2.5c.37.02.63-.5.4-.78L5.5 119.5c-.12-.15-.34-.22-.53-.16zm12.02 10c1.2-.02 1.4-.25 1.4-1.53l-1.1 1.36c-.07.1-.17.17-.3.18zM5.94 136.6l2.37 2.93h6.42c.42 0 .72.28.72.72v1.25h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.25c0-4.2.72-3.63-3.66-3.63H7.28c-.6 0-.92-.02-1.34-.03zm-1.72.06c-.4.08-.54.3-.6.75l.6-.74zm.84.93c-.12 0-.24.08-.3.18l-3.95 4.9c-.24.3 0 .83.4.82H3.6v1.22c0 4.26-.55 3.62 3.66 3.62h7.44c.63 0 .97.02 1.4.03l-2.37-2.93H7.28c-.44 0-.72-.3-.72-.72v-1.22h2.5c.4.04.67-.53.4-.8l-3.96-4.92c-.1-.13-.27-.2-.44-.2zm13.28 10.03l-.56.7c.36-.07.5-.3.56-.7zM17.13 155.6c-.55-.02-1.32.03-2.4.03h-8.2l2.38 2.9h5.82c.42 0 .72.28.72.72v1.97H12.9c-.32.06-.48.52-.28.78l3.94 4.94c.2.23.6.22.78-.03l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.97c0-3.15.4-3.62-1.25-3.66zm-12.1.28c-.1.02-.2.1-.28.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v1.96c0 4.26-.55 3.62 3.66 3.62h8.24l-2.36-2.9H7.28c-.44 0-.72-.3-.72-.72v-1.97h2.5c.37.02.63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.22-.47-.2zM5.13 174.5c-.15 0-.3.07-.38.2L.8 179.6c-.24.27 0 .82.4.8H3.6v2.32c0 4.26-.55 3.62 3.66 3.62h7.94l-2.35-2.9h-5.6c-.43 0-.7-.3-.7-.72v-2.3h2.5c.38.03.66-.54.4-.83l-3.97-4.9c-.1-.13-.23-.2-.38-.2zm12 .1c-.55-.02-1.32.03-2.4.03H6.83l2.35 2.9h5.52c.42 0 .72.28.72.72v2.34h-2.6c-.3.1-.43.53-.2.78l3.92 4.9c.18.24.6.24.78 0l3.94-4.9c.22-.3-.02-.78-.37-.8H18.4v-2.33c0-3.15.4-3.62-1.25-3.66zM4.97 193.16c-.1.03-.17.1-.22.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77l-3.96-4.9c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.03-2.4.03H7.1l2.32 2.84.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($highlight-text-color)}' stroke-width='0'/></svg>");
   }
 }
 
 button.icon-button.disabled i.fa-retweet {
-  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='209'><path d='M4.97 3.16c-.1.03-.17.1-.22.18L.8 8.24c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77L5.5 3.35c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.02-2.4.02H7.1l2.32 2.85.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color(lighten($ui-base-color, 13%))}' stroke-width='0'/><path d='M7.78 19.66c-.24.02-.44.25-.44.5v2.46h-.06c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v4.47c0 4.26-.56 3.62 3.65 3.62H8.5l-1.3-1.06c-.1-.08-.18-.2-.2-.3-.02-.17.06-.35.2-.45l1.33-1.1H7.28c-.44 0-.72-.3-.72-.7v-4.48c0-.44.28-.72.72-.72h.06v2.5c0 .38.54.63.82.38l4.9-3.93c.25-.18.25-.6 0-.78l-4.9-3.92c-.1-.1-.24-.14-.38-.12zm9.34 2.93c-.54-.02-1.3.02-2.4.02h-1.25l1.3 1.07c.1.07.18.2.2.33.02.16-.06.3-.2.4l-1.33 1.1h1.28c.42 0 .72.28.72.72v4.47c0 .42-.3.72-.72.72h-.1v-2.47c0-.3-.3-.53-.6-.47-.07 0-.14.05-.2.1l-4.9 3.93c-.26.18-.26.6 0 .78l4.9 3.92c.27.25.82 0 .8-.38v-2.5h.1c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.15.4-3.62-1.25-3.66zM10.34 38.66c-.24.02-.44.25-.43.5v2.47H7.3c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.47c0 3.66-.23 3.7 2.34 3.66l-1.34-1.1c-.1-.08-.18-.2-.2-.3 0-.17.07-.35.2-.45l1.96-1.6c-.03-.06-.04-.13-.04-.2v-4.48c0-.44.28-.72.72-.72H9.9v2.5c0 .36.5.6.8.38l4.93-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.08-.23-.13-.36-.12zm5.63 2.93l1.34 1.1c.1.07.18.2.2.33.02.16-.03.3-.16.4l-1.96 1.6c.02.07.06.13.06.22v4.47c0 .42-.3.72-.72.72h-2.66v-2.47c0-.3-.3-.53-.6-.47-.06.02-.12.05-.18.1l-4.94 3.93c-.24.18-.24.6 0 .78l4.94 3.92c.28.22.78-.02.78-.38v-2.5h2.66c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.66.34-3.7-2.4-3.66zM13.06 57.66c-.23.03-.4.26-.4.5v2.47H7.28c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.87l2.93-2.37v-2.5c0-.44.28-.72.72-.72h5.38v2.5c0 .36.5.6.78.38l4.94-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.1-.24-.14-.38-.12zm5.3 6.15l-2.92 2.4v2.52c0 .42-.3.72-.72.72h-5.4v-2.47c0-.3-.32-.53-.6-.47-.07.02-.13.05-.2.1L3.6 70.52c-.25.18-.25.6 0 .78l4.93 3.92c.28.22.78-.02.78-.38v-2.5h5.42c4.27 0 3.65.67 3.65-3.62v-4.47-.44zM19.25 78.8c-.1.03-.2.1-.28.17l-.9.9c-.44-.3-1.36-.25-3.35-.25H7.28c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v.7l2.93.3v-1c0-.44.28-.72.72-.72h7.44c.2 0 .37.08.5.2l-1.8 1.8c-.25.26-.08.76.27.8l6.27.7c.28.03.56-.25.53-.53l-.7-6.25c0-.27-.3-.48-.55-.44zm-17.2 6.1c-.2.07-.36.3-.33.54l.7 6.25c.02.36.58.55.83.27l.8-.8c.02 0 .04-.02.04 0 .46.24 1.37.17 3.18.17h7.44c4.27 0 3.65.67 3.65-3.62v-.75l-2.93-.3v1.05c0 .42-.3.72-.72.72H7.28c-.15 0-.3-.03-.4-.1L8.8 86.4c.3-.24.1-.8-.27-.84l-6.28-.65h-.2zM4.88 98.6c-1.33 0-1.34.48-1.3 2.3l1.14-1.37c.08-.1.22-.17.34-.2.16 0 .34.08.44.2l1.66 2.03c.04 0 .07-.03.12-.03h7.44c.34 0 .57.2.65.5h-2.43c-.34.05-.53.52-.3.78l3.92 4.95c.18.24.6.24.78 0l3.94-4.94c.22-.27-.02-.76-.37-.77H18.4c.02-3.9.6-3.4-3.66-3.4H7.28c-1.08 0-1.86-.04-2.4-.04zm.15 2.46c-.1.03-.2.1-.28.2l-3.94 4.9c-.2.28.03.77.4.78H3.6c-.02 3.94-.45 3.4 3.66 3.4h7.44c3.65 0 3.74.3 3.7-2.25l-1.1 1.34c-.1.1-.2.17-.32.2-.16 0-.34-.08-.44-.2l-1.65-2.03c-.06.02-.1.04-.18.04H7.28c-.35 0-.57-.2-.66-.5h2.44c.37 0 .63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.23-.47-.2zM4.88 117.6c-1.16 0-1.3.3-1.3 1.56l1.14-1.38c.08-.1.22-.14.34-.16.16 0 .34.04.44.16l2.22 2.75h7c.42 0 .72.28.72.72v.53h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-.53c0-4.2.72-3.63-3.66-3.63H7.28c-1.08 0-1.86-.03-2.4-.03zm.1 1.74c-.1.03-.17.1-.23.16L.8 124.44c-.2.28.03.77.4.78H3.6v.5c0 4.26-.55 3.62 3.66 3.62h7.44c1.03 0 1.74.02 2.28 0-.16.02-.34-.03-.44-.15l-2.22-2.76H7.28c-.44 0-.72-.3-.72-.72v-.5h2.5c.37.02.63-.5.4-.78L5.5 119.5c-.12-.15-.34-.22-.53-.16zm12.02 10c1.2-.02 1.4-.25 1.4-1.53l-1.1 1.36c-.07.1-.17.17-.3.18zM5.94 136.6l2.37 2.93h6.42c.42 0 .72.28.72.72v1.25h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.25c0-4.2.72-3.63-3.66-3.63H7.28c-.6 0-.92-.02-1.34-.03zm-1.72.06c-.4.08-.54.3-.6.75l.6-.74zm.84.93c-.12 0-.24.08-.3.18l-3.95 4.9c-.24.3 0 .83.4.82H3.6v1.22c0 4.26-.55 3.62 3.66 3.62h7.44c.63 0 .97.02 1.4.03l-2.37-2.93H7.28c-.44 0-.72-.3-.72-.72v-1.22h2.5c.4.04.67-.53.4-.8l-3.96-4.92c-.1-.13-.27-.2-.44-.2zm13.28 10.03l-.56.7c.36-.07.5-.3.56-.7zM17.13 155.6c-.55-.02-1.32.03-2.4.03h-8.2l2.38 2.9h5.82c.42 0 .72.28.72.72v1.97H12.9c-.32.06-.48.52-.28.78l3.94 4.94c.2.23.6.22.78-.03l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.97c0-3.15.4-3.62-1.25-3.66zm-12.1.28c-.1.02-.2.1-.28.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v1.96c0 4.26-.55 3.62 3.66 3.62h8.24l-2.36-2.9H7.28c-.44 0-.72-.3-.72-.72v-1.97h2.5c.37.02.63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.22-.47-.2zM5.13 174.5c-.15 0-.3.07-.38.2L.8 179.6c-.24.27 0 .82.4.8H3.6v2.32c0 4.26-.55 3.62 3.66 3.62h7.94l-2.35-2.9h-5.6c-.43 0-.7-.3-.7-.72v-2.3h2.5c.38.03.66-.54.4-.83l-3.97-4.9c-.1-.13-.23-.2-.38-.2zm12 .1c-.55-.02-1.32.03-2.4.03H6.83l2.35 2.9h5.52c.42 0 .72.28.72.72v2.34h-2.6c-.3.1-.43.53-.2.78l3.92 4.9c.18.24.6.24.78 0l3.94-4.9c.22-.3-.02-.78-.37-.8H18.4v-2.33c0-3.15.4-3.62-1.25-3.66zM4.97 193.16c-.1.03-.17.1-.22.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77l-3.96-4.9c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.03-2.4.03H7.1l2.32 2.84.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($ui-highlight-color)}' stroke-width='0'/></svg>");
+  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='209'><path d='M4.97 3.16c-.1.03-.17.1-.22.18L.8 8.24c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77L5.5 3.35c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.02-2.4.02H7.1l2.32 2.85.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color(darken($action-button-color, 13%))}' stroke-width='0'/><path d='M7.78 19.66c-.24.02-.44.25-.44.5v2.46h-.06c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v4.47c0 4.26-.56 3.62 3.65 3.62H8.5l-1.3-1.06c-.1-.08-.18-.2-.2-.3-.02-.17.06-.35.2-.45l1.33-1.1H7.28c-.44 0-.72-.3-.72-.7v-4.48c0-.44.28-.72.72-.72h.06v2.5c0 .38.54.63.82.38l4.9-3.93c.25-.18.25-.6 0-.78l-4.9-3.92c-.1-.1-.24-.14-.38-.12zm9.34 2.93c-.54-.02-1.3.02-2.4.02h-1.25l1.3 1.07c.1.07.18.2.2.33.02.16-.06.3-.2.4l-1.33 1.1h1.28c.42 0 .72.28.72.72v4.47c0 .42-.3.72-.72.72h-.1v-2.47c0-.3-.3-.53-.6-.47-.07 0-.14.05-.2.1l-4.9 3.93c-.26.18-.26.6 0 .78l4.9 3.92c.27.25.82 0 .8-.38v-2.5h.1c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.15.4-3.62-1.25-3.66zM10.34 38.66c-.24.02-.44.25-.43.5v2.47H7.3c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.47c0 3.66-.23 3.7 2.34 3.66l-1.34-1.1c-.1-.08-.18-.2-.2-.3 0-.17.07-.35.2-.45l1.96-1.6c-.03-.06-.04-.13-.04-.2v-4.48c0-.44.28-.72.72-.72H9.9v2.5c0 .36.5.6.8.38l4.93-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.08-.23-.13-.36-.12zm5.63 2.93l1.34 1.1c.1.07.18.2.2.33.02.16-.03.3-.16.4l-1.96 1.6c.02.07.06.13.06.22v4.47c0 .42-.3.72-.72.72h-2.66v-2.47c0-.3-.3-.53-.6-.47-.06.02-.12.05-.18.1l-4.94 3.93c-.24.18-.24.6 0 .78l4.94 3.92c.28.22.78-.02.78-.38v-2.5h2.66c4.27 0 3.65.67 3.65-3.62v-4.47c0-3.66.34-3.7-2.4-3.66zM13.06 57.66c-.23.03-.4.26-.4.5v2.47H7.28c-1.08 0-1.86-.04-2.4-.04-1.64 0-1.25.43-1.25 3.65v4.87l2.93-2.37v-2.5c0-.44.28-.72.72-.72h5.38v2.5c0 .36.5.6.78.38l4.94-3.93c.24-.18.24-.6 0-.78l-4.94-3.92c-.1-.1-.24-.14-.38-.12zm5.3 6.15l-2.92 2.4v2.52c0 .42-.3.72-.72.72h-5.4v-2.47c0-.3-.32-.53-.6-.47-.07.02-.13.05-.2.1L3.6 70.52c-.25.18-.25.6 0 .78l4.93 3.92c.28.22.78-.02.78-.38v-2.5h5.42c4.27 0 3.65.67 3.65-3.62v-4.47-.44zM19.25 78.8c-.1.03-.2.1-.28.17l-.9.9c-.44-.3-1.36-.25-3.35-.25H7.28c-1.08 0-1.86-.03-2.4-.03-1.64 0-1.25.43-1.25 3.65v.7l2.93.3v-1c0-.44.28-.72.72-.72h7.44c.2 0 .37.08.5.2l-1.8 1.8c-.25.26-.08.76.27.8l6.27.7c.28.03.56-.25.53-.53l-.7-6.25c0-.27-.3-.48-.55-.44zm-17.2 6.1c-.2.07-.36.3-.33.54l.7 6.25c.02.36.58.55.83.27l.8-.8c.02 0 .04-.02.04 0 .46.24 1.37.17 3.18.17h7.44c4.27 0 3.65.67 3.65-3.62v-.75l-2.93-.3v1.05c0 .42-.3.72-.72.72H7.28c-.15 0-.3-.03-.4-.1L8.8 86.4c.3-.24.1-.8-.27-.84l-6.28-.65h-.2zM4.88 98.6c-1.33 0-1.34.48-1.3 2.3l1.14-1.37c.08-.1.22-.17.34-.2.16 0 .34.08.44.2l1.66 2.03c.04 0 .07-.03.12-.03h7.44c.34 0 .57.2.65.5h-2.43c-.34.05-.53.52-.3.78l3.92 4.95c.18.24.6.24.78 0l3.94-4.94c.22-.27-.02-.76-.37-.77H18.4c.02-3.9.6-3.4-3.66-3.4H7.28c-1.08 0-1.86-.04-2.4-.04zm.15 2.46c-.1.03-.2.1-.28.2l-3.94 4.9c-.2.28.03.77.4.78H3.6c-.02 3.94-.45 3.4 3.66 3.4h7.44c3.65 0 3.74.3 3.7-2.25l-1.1 1.34c-.1.1-.2.17-.32.2-.16 0-.34-.08-.44-.2l-1.65-2.03c-.06.02-.1.04-.18.04H7.28c-.35 0-.57-.2-.66-.5h2.44c.37 0 .63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.23-.47-.2zM4.88 117.6c-1.16 0-1.3.3-1.3 1.56l1.14-1.38c.08-.1.22-.14.34-.16.16 0 .34.04.44.16l2.22 2.75h7c.42 0 .72.28.72.72v.53h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-.53c0-4.2.72-3.63-3.66-3.63H7.28c-1.08 0-1.86-.03-2.4-.03zm.1 1.74c-.1.03-.17.1-.23.16L.8 124.44c-.2.28.03.77.4.78H3.6v.5c0 4.26-.55 3.62 3.66 3.62h7.44c1.03 0 1.74.02 2.28 0-.16.02-.34-.03-.44-.15l-2.22-2.76H7.28c-.44 0-.72-.3-.72-.72v-.5h2.5c.37.02.63-.5.4-.78L5.5 119.5c-.12-.15-.34-.22-.53-.16zm12.02 10c1.2-.02 1.4-.25 1.4-1.53l-1.1 1.36c-.07.1-.17.17-.3.18zM5.94 136.6l2.37 2.93h6.42c.42 0 .72.28.72.72v1.25h-2.6c-.3.1-.43.54-.2.78l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.25c0-4.2.72-3.63-3.66-3.63H7.28c-.6 0-.92-.02-1.34-.03zm-1.72.06c-.4.08-.54.3-.6.75l.6-.74zm.84.93c-.12 0-.24.08-.3.18l-3.95 4.9c-.24.3 0 .83.4.82H3.6v1.22c0 4.26-.55 3.62 3.66 3.62h7.44c.63 0 .97.02 1.4.03l-2.37-2.93H7.28c-.44 0-.72-.3-.72-.72v-1.22h2.5c.4.04.67-.53.4-.8l-3.96-4.92c-.1-.13-.27-.2-.44-.2zm13.28 10.03l-.56.7c.36-.07.5-.3.56-.7zM17.13 155.6c-.55-.02-1.32.03-2.4.03h-8.2l2.38 2.9h5.82c.42 0 .72.28.72.72v1.97H12.9c-.32.06-.48.52-.28.78l3.94 4.94c.2.23.6.22.78-.03l3.94-4.9c.22-.28-.02-.77-.37-.78H18.4v-1.97c0-3.15.4-3.62-1.25-3.66zm-12.1.28c-.1.02-.2.1-.28.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v1.96c0 4.26-.55 3.62 3.66 3.62h8.24l-2.36-2.9H7.28c-.44 0-.72-.3-.72-.72v-1.97h2.5c.37.02.63-.5.4-.78l-3.96-4.9c-.1-.15-.3-.22-.47-.2zM5.13 174.5c-.15 0-.3.07-.38.2L.8 179.6c-.24.27 0 .82.4.8H3.6v2.32c0 4.26-.55 3.62 3.66 3.62h7.94l-2.35-2.9h-5.6c-.43 0-.7-.3-.7-.72v-2.3h2.5c.38.03.66-.54.4-.83l-3.97-4.9c-.1-.13-.23-.2-.38-.2zm12 .1c-.55-.02-1.32.03-2.4.03H6.83l2.35 2.9h5.52c.42 0 .72.28.72.72v2.34h-2.6c-.3.1-.43.53-.2.78l3.92 4.9c.18.24.6.24.78 0l3.94-4.9c.22-.3-.02-.78-.37-.8H18.4v-2.33c0-3.15.4-3.62-1.25-3.66zM4.97 193.16c-.1.03-.17.1-.22.18l-3.94 4.9c-.2.3.03.78.4.8H3.6v2.68c0 4.26-.55 3.62 3.66 3.62h7.66l-2.3-2.84c-.03-.02-.03-.04-.05-.06H7.27c-.44 0-.72-.3-.72-.72v-2.7h2.5c.37.03.63-.48.4-.77l-3.96-4.9c-.12-.17-.34-.25-.53-.2zm12.16.43c-.55-.02-1.32.03-2.4.03H7.1l2.32 2.84.03.06h5.25c.42 0 .72.28.72.72v2.7h-2.5c-.36.02-.56.54-.3.8l3.92 4.9c.18.25.6.25.78 0l3.94-4.9c.26-.28 0-.83-.37-.8H18.4v-2.7c0-3.15.4-3.62-1.25-3.66z' fill='#{hex-color($highlight-text-color)}' stroke-width='0'/></svg>");
 }
diff --git a/app/javascript/styles/mastodon/compact_header.scss b/app/javascript/styles/mastodon/compact_header.scss
index 90d98cc8c..83ac7a8d0 100644
--- a/app/javascript/styles/mastodon/compact_header.scss
+++ b/app/javascript/styles/mastodon/compact_header.scss
@@ -2,7 +2,7 @@
   h1 {
     font-size: 24px;
     line-height: 28px;
-    color: $ui-primary-color;
+    color: $primary-text-color;
     font-weight: 500;
     margin-bottom: 20px;
     padding: 0 10px;
@@ -20,7 +20,7 @@
 
     small {
       font-weight: 400;
-      color: $ui-secondary-color;
+      color: $darker-text-color;
     }
 
     img {
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 94e3089f8..ce9f316a0 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -4,7 +4,7 @@
 }
 
 .button {
-  background-color: darken($ui-highlight-color, 3%);
+  background-color: $ui-highlight-color;
   border: 10px none;
   border-radius: 4px;
   box-sizing: border-box;
@@ -31,7 +31,7 @@
   &:active,
   &:focus,
   &:hover {
-    background-color: lighten($ui-highlight-color, 7%);
+    background-color: lighten($ui-highlight-color, 4%);
     transition: all 200ms ease-out;
   }
 
@@ -52,7 +52,7 @@
   }
 
   &.button-alternative {
-    color: $ui-base-color;
+    color: $inverted-text-color;
     background: $ui-primary-color;
 
     &:active,
@@ -98,26 +98,10 @@
   position: relative;
 }
 
-.column-icon {
-  background: lighten($ui-base-color, 4%);
-  color: $ui-primary-color;
-  cursor: pointer;
-  font-size: 16px;
-  padding: 15px;
-  position: absolute;
-  right: 0;
-  top: -48px;
-  z-index: 3;
-
-  &:hover {
-    color: lighten($ui-primary-color, 7%);
-  }
-}
-
 .icon-button {
   display: inline-block;
   padding: 0;
-  color: $ui-base-lighter-color;
+  color: $action-button-color;
   border: none;
   background: transparent;
   cursor: pointer;
@@ -126,17 +110,17 @@
   &:hover,
   &:active,
   &:focus {
-    color: lighten($ui-base-color, 33%);
+    color: lighten($action-button-color, 7%);
     transition: color 200ms ease-out;
   }
 
   &.disabled {
-    color: lighten($ui-base-color, 13%);
+    color: darken($action-button-color, 13%);
     cursor: default;
   }
 
   &.active {
-    color: $ui-highlight-color;
+    color: $highlight-text-color;
   }
 
   &::-moz-focus-inner {
@@ -150,23 +134,23 @@
   }
 
   &.inverted {
-    color: lighten($ui-base-color, 33%);
+    color: $lighter-text-color;
 
     &:hover,
     &:active,
     &:focus {
-      color: $ui-base-lighter-color;
+      color: transparentize($lighter-text-color, 0.07);
     }
 
     &.disabled {
-      color: $ui-primary-color;
+      color: opacify($lighter-text-color, 0.07);
     }
 
     &.active {
-      color: $ui-highlight-color;
+      color: $highlight-text-color;
 
       &.disabled {
-        color: lighten($ui-highlight-color, 13%);
+        color: opacify($lighter-text-color, 0.13);
       }
     }
   }
@@ -185,7 +169,7 @@
 }
 
 .text-icon-button {
-  color: lighten($ui-base-color, 33%);
+  color: $lighter-text-color;
   border: none;
   background: transparent;
   cursor: pointer;
@@ -199,17 +183,17 @@
   &:hover,
   &:active,
   &:focus {
-    color: $ui-base-lighter-color;
+    color: opacify($lighter-text-color, 0.07);
     transition: color 200ms ease-out;
   }
 
   &.disabled {
-    color: lighten($ui-base-color, 13%);
+    color: transparentize($lighter-text-color, 0.2);
     cursor: default;
   }
 
   &.active {
-    color: $ui-highlight-color;
+    color: $highlight-text-color;
   }
 
   &::-moz-focus-inner {
@@ -228,25 +212,6 @@
   transform-origin: 50% 0;
 }
 
-.dropdown--active .icon-button {
-  color: $ui-highlight-color;
-}
-
-.dropdown--active::after {
-  @media screen and (min-width: 631px) {
-    content: "";
-    display: block;
-    position: absolute;
-    width: 0;
-    height: 0;
-    border-style: solid;
-    border-width: 0 4.5px 7.8px;
-    border-color: transparent transparent $ui-secondary-color;
-    bottom: 8px;
-    right: 104px;
-  }
-}
-
 .invisible {
   font-size: 0;
   line-height: 0;
@@ -271,15 +236,11 @@
   }
 }
 
-.lightbox .icon-button {
-  color: $ui-base-color;
-}
-
 .compose-form {
   padding: 10px;
 
   .compose-form__warning {
-    color: darken($ui-secondary-color, 65%);
+    color: $inverted-text-color;
     margin-bottom: 15px;
     background: $ui-primary-color;
     box-shadow: 0 2px 6px rgba($base-shadow-color, 0.3);
@@ -289,7 +250,7 @@
     font-weight: 400;
 
     strong {
-      color: darken($ui-secondary-color, 65%);
+      color: $inverted-text-color;
       font-weight: 500;
 
       @each $lang in $cjk-langs {
@@ -300,7 +261,7 @@
     }
 
     a {
-      color: darken($ui-primary-color, 33%);
+      color: $lighter-text-color;
       font-weight: 500;
       text-decoration: underline;
 
@@ -333,7 +294,7 @@
     box-sizing: border-box;
     width: 100%;
     margin: 0;
-    color: $ui-base-color;
+    color: $inverted-text-color;
     background: $simple-background-color;
     padding: 10px;
     font-family: inherit;
@@ -378,7 +339,7 @@
     box-shadow: 4px 4px 6px rgba($base-shadow-color, 0.4);
     background: $ui-secondary-color;
     border-radius: 0 0 4px 4px;
-    color: $ui-base-color;
+    color: $lighter-text-color;
     font-size: 14px;
     padding: 6px;
 
@@ -419,11 +380,11 @@
   }
 
   .autosuggest-account .display-name__account {
-    color: lighten($ui-base-color, 36%);
+    color: $lighter-text-color;
   }
 
   .compose-form__modifiers {
-    color: $ui-base-color;
+    color: $inverted-text-color;
     font-family: inherit;
     font-size: 14px;
     background: $simple-background-color;
@@ -454,7 +415,7 @@
 
         .icon-button {
           flex: 0 1 auto;
-          color: $ui-secondary-color;
+          color: $action-button-color;
           font-size: 14px;
           font-weight: 500;
           padding: 10px;
@@ -463,7 +424,7 @@
           &:hover,
           &:focus,
           &:active {
-            color: lighten($ui-secondary-color, 4%);
+            color: lighten($action-button-color, 7%);
           }
         }
 
@@ -486,7 +447,7 @@
 
         input {
           background: transparent;
-          color: $ui-secondary-color;
+          color: $primary-text-color;
           border: 0;
           padding: 0;
           margin: 0;
@@ -501,7 +462,7 @@
 
           &::placeholder {
             opacity: 0.54;
-            color: $ui-secondary-color;
+            color: $darker-text-color;
           }
         }
 
@@ -563,7 +524,7 @@
         font-family: 'mastodon-font-sans-serif', sans-serif;
         font-size: 14px;
         font-weight: 600;
-        color: lighten($ui-base-color, 12%);
+        color: $lighter-text-color;
 
         &.character-counter--over {
           color: $warning-red;
@@ -617,7 +578,7 @@
 }
 
 .reply-indicator__display-name {
-  color: $ui-base-color;
+  color: $lighter-text-color;
   display: block;
   max-width: 100%;
   line-height: 24px;
@@ -679,7 +640,7 @@
       text-decoration: underline;
 
       .fa {
-        color: lighten($ui-base-color, 40%);
+        color: lighten($action-button-color, 7%);
       }
     }
 
@@ -694,15 +655,15 @@
     }
 
     .fa {
-      color: lighten($ui-base-color, 30%);
+      color: $action-button-color;
     }
   }
 
   .status__content__spoiler-link {
-    background: lighten($ui-base-color, 30%);
+    background: $action-button-color;
 
     &:hover {
-      background: lighten($ui-base-color, 33%);
+      background: lighten($action-button-color, 7%);
       text-decoration: none;
     }
   }
@@ -721,7 +682,7 @@
   border-radius: 2px;
   background: transparent;
   border: 0;
-  color: lighten($ui-base-color, 8%);
+  color: $lighter-text-color;
   font-weight: 700;
   font-size: 11px;
   padding: 0 6px;
@@ -784,36 +745,32 @@
 
   &.status-direct {
     background: lighten($ui-base-color, 8%);
-
-    .icon-button.disabled {
-      color: lighten($ui-base-color, 16%);
-    }
   }
 
   &.light {
     .status__relative-time {
-      color: $ui-primary-color;
+      color: $lighter-text-color;
     }
 
     .status__display-name {
-      color: $ui-base-color;
+      color: $inverted-text-color;
     }
 
     .display-name {
       strong {
-        color: $ui-base-color;
+        color: $inverted-text-color;
       }
 
       span {
-        color: $ui-primary-color;
+        color: $lighter-text-color;
       }
     }
 
     .status__content {
-      color: $ui-base-color;
+      color: $inverted-text-color;
 
       a {
-        color: $ui-highlight-color;
+        color: $highlight-text-color;
       }
 
       a.status__content__spoiler-link {
@@ -833,19 +790,19 @@
     background: transparent;
 
     .icon-button.disabled {
-      color: lighten($ui-base-color, 13%);
+      color: lighten($action-button-color, 13%);
     }
   }
 }
 
 .status__relative-time {
-  color: $ui-base-lighter-color;
+  color: $darker-text-color;
   float: right;
   font-size: 14px;
 }
 
 .status__display-name {
-  color: $ui-base-lighter-color;
+  color: $darker-text-color;
 }
 
 .status__info .status__display-name {
@@ -896,14 +853,14 @@
 
 .status__prepend {
   margin-left: 68px;
-  color: $ui-base-lighter-color;
+  color: $darker-text-color;
   padding: 8px 0;
   padding-bottom: 2px;
   font-size: 14px;
   position: relative;
 
   .status__display-name strong {
-    color: $ui-base-lighter-color;
+    color: $darker-text-color;
   }
 
   > span {
@@ -965,7 +922,7 @@
 
 .detailed-status__meta {
   margin-top: 15px;
-  color: $ui-base-lighter-color;
+  color: $darker-text-color;
   font-size: 14px;
   line-height: 18px;
 }
@@ -993,11 +950,11 @@
 }
 
 .reply-indicator__content {
-  color: $ui-base-color;
+  color: $inverted-text-color;
   font-size: 14px;
 
   a {
-    color: lighten($ui-base-color, 20%);
+    color: $lighter-text-color;
   }
 }
 
@@ -1032,7 +989,7 @@
   .account__display-name {
     flex: 1 1 auto;
     display: block;
-    color: $ui-primary-color;
+    color: $darker-text-color;
     overflow: hidden;
     text-decoration: none;
     font-size: 14px;
@@ -1102,7 +1059,7 @@
     }
 
     .account__header__username {
-      color: $ui-primary-color;
+      color: $darker-text-color;
     }
   }
 
@@ -1112,7 +1069,7 @@
   }
 
   .account__header__content {
-    color: $ui-secondary-color;
+    color: $darker-text-color;
   }
 
   .account__header__display-name {
@@ -1127,7 +1084,7 @@
   }
 
   .account__header__username {
-    color: $ui-highlight-color;
+    color: $highlight-text-color;
     font-size: 14px;
     font-weight: 400;
     display: block;
@@ -1140,7 +1097,7 @@
 .account__disclaimer {
   padding: 10px;
   border-top: 1px solid lighten($ui-base-color, 8%);
-  color: $ui-base-lighter-color;
+  color: $darker-text-color;
 
   strong {
     font-weight: 500;
@@ -1166,7 +1123,7 @@
 }
 
 .account__header__content {
-  color: $ui-primary-color;
+  color: $darker-text-color;
   font-size: 14px;
   font-weight: 400;
   overflow: hidden;
@@ -1243,7 +1200,7 @@
     display: block;
     text-transform: uppercase;
     font-size: 11px;
-    color: $ui-primary-color;
+    color: $darker-text-color;
   }
 
   strong {
@@ -1258,10 +1215,6 @@
       }
     }
   }
-
-  abbr {
-    color: $ui-base-lighter-color;
-  }
 }
 
 .account__header__avatar {
@@ -1331,7 +1284,7 @@
 }
 
 .detailed-status__display-name {
-  color: $ui-secondary-color;
+  color: $darker-text-color;
   display: block;
   line-height: 24px;
   margin-bottom: 15px;
@@ -1366,11 +1319,11 @@
 .muted {
   .status__content p,
   .status__content a {
-    color: $ui-base-lighter-color;
+    color: $darker-text-color;
   }
 
   .status__display-name strong {
-    color: $ui-base-lighter-color;
+    color: $darker-text-color;
   }
 
   .status__avatar {
@@ -1378,11 +1331,11 @@
   }
 
   a.status__content__spoiler-link {
-    background: $ui-base-lighter-color;
+    background: $darker-text-color;
     color: lighten($ui-base-color, 4%);
 
     &:hover {
-      background: lighten($ui-base-color, 29%);
+      background: transparentize($darker-text-color, 0.07);
       text-decoration: none;
     }
   }
@@ -1398,7 +1351,7 @@
   position: relative;
 
   .fa {
-    color: $ui-highlight-color;
+    color: $highlight-text-color;
   }
 
   > span {
@@ -1501,7 +1454,7 @@
   display: flex;
   flex-shrink: 0;
   cursor: default;
-  color: $ui-primary-color;
+  color: $darker-text-color;
 
   strong {
     color: $primary-text-color;
@@ -1609,7 +1562,7 @@
     box-sizing: border-box;
     text-decoration: none;
     background: $ui-secondary-color;
-    color: $ui-base-color;
+    color: $inverted-text-color;
     overflow: hidden;
     text-overflow: ellipsis;
     white-space: nowrap;
@@ -1618,7 +1571,7 @@
     &:hover,
     &:active {
       background: $ui-highlight-color;
-      color: $ui-secondary-color;
+      color: $primary-text-color;
       outline: 0;
     }
   }
@@ -1660,7 +1613,7 @@
     box-sizing: border-box;
     text-decoration: none;
     background: $ui-secondary-color;
-    color: $ui-base-color;
+    color: $inverted-text-color;
     overflow: hidden;
     text-overflow: ellipsis;
     white-space: nowrap;
@@ -1671,7 +1624,7 @@
 
     &:hover {
       background: $ui-highlight-color;
-      color: $ui-secondary-color;
+      color: $primary-text-color;
     }
   }
 }
@@ -1683,7 +1636,7 @@
 .static-content {
   padding: 10px;
   padding-top: 20px;
-  color: $ui-base-lighter-color;
+  color: $darker-text-color;
 
   h1 {
     font-size: 16px;
@@ -1935,8 +1888,8 @@
   }
 
   &.active {
-    border-bottom: 2px solid $ui-highlight-color;
-    color: $ui-highlight-color;
+    border-bottom: 2px solid $highlight-text-color;
+    color: $highlight-text-color;
   }
 
   &:hover,
@@ -1991,7 +1944,7 @@
 
 .column-back-button {
   background: lighten($ui-base-color, 4%);
-  color: $ui-highlight-color;
+  color: $highlight-text-color;
   cursor: pointer;
   flex: 0 0 auto;
   font-size: 16px;
@@ -2010,7 +1963,7 @@
   background: lighten($ui-base-color, 4%);
   border: 0;
   font-family: inherit;
-  color: $ui-highlight-color;
+  color: $highlight-text-color;
   cursor: pointer;
   white-space: nowrap;
   font-size: 16px;
@@ -2182,7 +2135,7 @@
 
 .column-subheading {
   background: $ui-base-color;
-  color: $ui-base-lighter-color;
+  color: $darker-text-color;
   padding: 8px 20px;
   font-size: 12px;
   font-weight: 500;
@@ -2205,11 +2158,11 @@
   flex: 1 0 auto;
 
   p {
-    color: $ui-secondary-color;
+    color: $darker-text-color;
   }
 
   a {
-    color: $ui-base-lighter-color;
+    color: opacify($darker-text-color, 0.07);
   }
 }
 
@@ -2235,7 +2188,7 @@
 }
 
 .setting-text {
-  color: $ui-primary-color;
+  color: $darker-text-color;
   background: transparent;
   border: none;
   border-bottom: 2px solid $ui-primary-color;
@@ -2249,23 +2202,12 @@
   &:focus,
   &:active {
     color: $primary-text-color;
-    border-bottom-color: $ui-highlight-color;
+    border-bottom-color: $highlight-text-color;
   }
 
   @media screen and (max-width: 600px) {
     font-size: 16px;
   }
-
-  &.light {
-    color: $ui-base-color;
-    border-bottom: 2px solid lighten($ui-base-color, 27%);
-
-    &:focus,
-    &:active {
-      color: $ui-base-color;
-      border-bottom-color: $ui-highlight-color;
-    }
-  }
 }
 
 .no-reduce-motion button.icon-button i.fa-retweet {
@@ -2288,12 +2230,12 @@
 }
 
 .reduce-motion button.icon-button i.fa-retweet {
-  color: $ui-base-lighter-color;
+  color: $action-button-color;
   transition: color 100ms ease-in;
 }
 
 .reduce-motion button.icon-button.active i.fa-retweet {
-  color: $ui-highlight-color;
+  color: $highlight-text-color;
 }
 
 .status-card {
@@ -2301,7 +2243,7 @@
   font-size: 14px;
   border: 1px solid lighten($ui-base-color, 8%);
   border-radius: 4px;
-  color: $ui-base-lighter-color;
+  color: $darker-text-color;
   margin-top: 14px;
   text-decoration: none;
   overflow: hidden;
@@ -2439,7 +2381,7 @@ a.status-card {
 
 .load-more {
   display: block;
-  color: $ui-base-lighter-color;
+  color: $darker-text-color;
   background-color: transparent;
   border: 0;
   font-size: inherit;
@@ -2463,7 +2405,7 @@ a.status-card {
   text-align: center;
   font-size: 16px;
   font-weight: 500;
-  color: lighten($ui-base-color, 16%);
+  color: opacify($darker-text-color, 0.07);
   background: $ui-base-color;
   cursor: default;
   display: flex;
@@ -2503,7 +2445,7 @@ a.status-card {
     strong {
       display: block;
       margin-bottom: 10px;
-      color: lighten($ui-base-color, 34%);
+      color: $darker-text-color;
     }
 
     span {
@@ -2561,15 +2503,15 @@ a.status-card {
   }
 
   & > .column-header__back-button {
-    color: $ui-highlight-color;
+    color: $highlight-text-color;
   }
 
   &.active {
-    box-shadow: 0 1px 0 rgba($ui-highlight-color, 0.3);
+    box-shadow: 0 1px 0 rgba($highlight-text-color, 0.3);
 
     .column-header__icon {
-      color: $ui-highlight-color;
-      text-shadow: 0 0 10px rgba($ui-highlight-color, 0.4);
+      color: $highlight-text-color;
+      text-shadow: 0 0 10px rgba($highlight-text-color, 0.4);
     }
   }
 
@@ -2615,7 +2557,7 @@ a.status-card {
   max-height: 70vh;
   overflow: hidden;
   overflow-y: auto;
-  color: $ui-primary-color;
+  color: $darker-text-color;
   transition: max-height 150ms ease-in-out, opacity 300ms linear;
   opacity: 1;
 
@@ -2644,7 +2586,7 @@ a.status-card {
 
 .column-header__setting-btn {
   &:hover {
-    color: lighten($ui-primary-color, 4%);
+    color: $darker-text-color;
     text-decoration: underline;
   }
 }
@@ -2678,7 +2620,7 @@ a.status-card {
 }
 
 .loading-indicator {
-  color: lighten($ui-base-color, 26%);
+  color: $darker-text-color;
   font-size: 12px;
   font-weight: 400;
   text-transform: uppercase;
@@ -2763,7 +2705,7 @@ a.status-card {
 
 .media-spoiler {
   background: $base-overlay-background;
-  color: $ui-primary-color;
+  color: $darker-text-color;
   border: 0;
   padding: 0;
   width: 100%;
@@ -2775,7 +2717,7 @@ a.status-card {
   &:active,
   &:focus {
     padding: 0;
-    color: lighten($ui-primary-color, 8%);
+    color: transparentize($darker-text-color, 0.07);
   }
 }
 
@@ -2828,7 +2770,7 @@ a.status-card {
 }
 
 .column-settings__section {
-  color: $ui-primary-color;
+  color: $darker-text-color;
   cursor: default;
   display: block;
   font-weight: 500;
@@ -2886,7 +2828,7 @@ a.status-card {
 
 .setting-toggle__label,
 .setting-meta__label {
-  color: $ui-primary-color;
+  color: $darker-text-color;
   display: inline-block;
   margin-bottom: 14px;
   margin-left: 8px;
@@ -2894,13 +2836,12 @@ a.status-card {
 }
 
 .setting-meta__label {
-  color: $ui-primary-color;
   float: right;
 }
 
 .empty-column-indicator,
 .error-column {
-  color: lighten($ui-base-color, 20%);
+  color: $darker-text-color;
   background: $ui-base-color;
   text-align: center;
   padding: 20px;
@@ -2917,7 +2858,7 @@ a.status-card {
   }
 
   a {
-    color: $ui-highlight-color;
+    color: $highlight-text-color;
     text-decoration: none;
 
     &:hover {
@@ -3102,7 +3043,7 @@ a.status-card {
   display: flex;
   align-items: center;
   justify-content: center;
-  color: $ui-secondary-color;
+  color: $primary-text-color;
   font-size: 18px;
   font-weight: 500;
   border: 2px dashed $ui-base-lighter-color;
@@ -3111,7 +3052,7 @@ a.status-card {
 
 .upload-progress {
   padding: 10px;
-  color: $ui-base-lighter-color;
+  color: $lighter-text-color;
   overflow: hidden;
   display: flex;
 
@@ -3200,7 +3141,7 @@ a.status-card {
 }
 
 .privacy-dropdown__option {
-  color: $ui-base-color;
+  color: $lighter-text-color;
   padding: 10px;
   cursor: pointer;
   display: flex;
@@ -3233,12 +3174,12 @@ a.status-card {
 
 .privacy-dropdown__option__content {
   flex: 1 1 auto;
-  color: darken($ui-primary-color, 24%);
+  color: $lighter-text-color;
 
   strong {
     font-weight: 500;
     display: block;
-    color: $ui-base-color;
+    color: $inverted-text-color;
 
     @each $lang in $cjk-langs {
       &:lang(#{$lang}) {
@@ -3287,7 +3228,7 @@ a.status-card {
   padding-right: 30px;
   font-family: inherit;
   background: $ui-base-color;
-  color: $ui-primary-color;
+  color: $darker-text-color;
   font-size: 14px;
   margin: 0;
 
@@ -3344,6 +3285,7 @@ a.status-card {
   .fa-times-circle {
     top: 11px;
     transform: rotate(0deg);
+    color: $action-button-color;
     cursor: pointer;
 
     &.active {
@@ -3351,13 +3293,13 @@ a.status-card {
     }
 
     &:hover {
-      color: $primary-text-color;
+      color: lighten($action-button-color, 7%);
     }
   }
 }
 
 .search-results__header {
-  color: $ui-base-lighter-color;
+  color: $darker-text-color;
   background: lighten($ui-base-color, 2%);
   border-bottom: 1px solid darken($ui-base-color, 4%);
   padding: 15px 10px;
@@ -3386,7 +3328,7 @@ a.status-card {
     span {
       display: inline-block;
       background: $ui-base-color;
-      color: $ui-primary-color;
+      color: $darker-text-color;
       font-size: 14px;
       font-weight: 500;
       padding: 10px;
@@ -3405,13 +3347,13 @@ a.status-card {
 .search-results__hashtag {
   display: block;
   padding: 10px;
-  color: $ui-secondary-color;
+  color: darken($primary-text-color, 4%);
   text-decoration: none;
 
   &:hover,
   &:active,
   &:focus {
-    color: lighten($ui-secondary-color, 4%);
+    color: $primary-text-color;
     text-decoration: underline;
   }
 }
@@ -3549,7 +3491,7 @@ a.status-card {
 }
 
 .media-modal__button {
-  background-color: $white;
+  background-color: $primary-text-color;
   height: 12px;
   width: 12px;
   border-radius: 6px;
@@ -3560,7 +3502,7 @@ a.status-card {
 }
 
 .media-modal__button--active {
-  background-color: $ui-highlight-color;
+  background-color: $highlight-text-color;
 }
 
 .media-modal__close {
@@ -3574,7 +3516,7 @@ a.status-card {
 .error-modal,
 .embed-modal {
   background: $ui-secondary-color;
-  color: $ui-base-color;
+  color: $inverted-text-color;
   border-radius: 8px;
   overflow: hidden;
   display: flex;
@@ -3662,7 +3604,7 @@ a.status-card {
 
   .onboarding-modal__nav,
   .error-modal__nav {
-    color: darken($ui-secondary-color, 34%);
+    color: $lighter-text-color;
     border: 0;
     font-size: 14px;
     font-weight: 500;
@@ -3676,18 +3618,18 @@ a.status-card {
     &:hover,
     &:focus,
     &:active {
-      color: darken($ui-secondary-color, 38%);
+      color: transparentize($lighter-text-color, 0.04);
       background-color: darken($ui-secondary-color, 16%);
     }
 
     &.onboarding-modal__done,
     &.onboarding-modal__next {
-      color: $ui-base-color;
+      color: $inverted-text-color;
 
       &:hover,
       &:focus,
       &:active {
-        color: darken($ui-base-color, 4%);
+        color: lighten($inverted-text-color, 4%);
       }
     }
   }
@@ -3739,17 +3681,17 @@ a.status-card {
   h1 {
     font-size: 18px;
     font-weight: 500;
-    color: $ui-base-color;
+    color: $inverted-text-color;
     margin-bottom: 20px;
   }
 
   a {
-    color: $ui-highlight-color;
+    color: $highlight-text-color;
 
     &:hover,
     &:focus,
     &:active {
-      color: lighten($ui-highlight-color, 4%);
+      color: lighten($highlight-text-color, 4%);
     }
   }
 
@@ -3759,7 +3701,7 @@ a.status-card {
 
   p {
     font-size: 16px;
-    color: lighten($ui-base-color, 8%);
+    color: $lighter-text-color;
     margin-top: 10px;
     margin-bottom: 10px;
 
@@ -3770,7 +3712,7 @@ a.status-card {
     strong {
       font-weight: 500;
       background: $ui-base-color;
-      color: $ui-secondary-color;
+      color: $primary-text-color;
       border-radius: 4px;
       font-size: 14px;
       padding: 3px 6px;
@@ -3822,7 +3764,7 @@ a.status-card {
 
   &__label {
     font-weight: 500;
-    color: $ui-base-color;
+    color: $inverted-text-color;
     margin-bottom: 5px;
     text-transform: uppercase;
     font-size: 12px;
@@ -3830,7 +3772,7 @@ a.status-card {
 
   &__case {
     background: $ui-base-color;
-    color: $ui-secondary-color;
+    color: $primary-text-color;
     font-weight: 500;
     padding: 10px;
     border-radius: 4px;
@@ -3847,7 +3789,7 @@ a.status-card {
 
   .figure {
     background: darken($ui-base-color, 8%);
-    color: $ui-secondary-color;
+    color: $darker-text-color;
     margin-bottom: 20px;
     border-radius: 4px;
     padding: 10px;
@@ -3936,7 +3878,7 @@ a.status-card {
 .actions-modal,
 .mute-modal {
   background: lighten($ui-secondary-color, 8%);
-  color: $ui-base-color;
+  color: $inverted-text-color;
   border-radius: 8px;
   overflow: hidden;
   max-width: 90vw;
@@ -3994,7 +3936,7 @@ a.status-card {
   & > div {
     flex: 1 1 auto;
     text-align: right;
-    color: lighten($ui-base-color, 33%);
+    color: $lighter-text-color;
     padding-right: 10px;
   }
 
@@ -4081,7 +4023,7 @@ a.status-card {
     box-sizing: border-box;
     width: 100%;
     margin: 0;
-    color: $ui-base-color;
+    color: $inverted-text-color;
     background: $white;
     padding: 10px;
     font-family: inherit;
@@ -4103,7 +4045,7 @@ a.status-card {
     margin-bottom: 24px;
 
     &__label {
-      color: $ui-base-color;
+      color: $inverted-text-color;
       font-size: 14px;
     }
   }
@@ -4142,7 +4084,7 @@ a.status-card {
 
     li:not(:empty) {
       a {
-        color: $ui-base-color;
+        color: $inverted-text-color;
         display: flex;
         padding: 12px 16px;
         font-size: 15px;
@@ -4178,14 +4120,14 @@ a.status-card {
   .confirmation-modal__cancel-button,
   .mute-modal__cancel-button {
     background-color: transparent;
-    color: darken($ui-secondary-color, 34%);
+    color: $lighter-text-color;
     font-size: 14px;
     font-weight: 500;
 
     &:hover,
     &:focus,
     &:active {
-      color: darken($ui-secondary-color, 38%);
+      color: transparentize($lighter-text-color, 0.04);
     }
   }
 }
@@ -4218,7 +4160,7 @@ a.status-card {
 }
 
 .loading-bar {
-  background-color: $ui-highlight-color;
+  background-color: $highlight-text-color;
   height: 3px;
   position: absolute;
   top: 0;
@@ -4266,7 +4208,7 @@ a.status-card {
 
   &__icon {
     flex: 0 0 auto;
-    color: $ui-base-lighter-color;
+    color: $darker-text-color;
     padding: 8px 18px;
     cursor: default;
     border-right: 1px solid lighten($ui-base-color, 8%);
@@ -4296,7 +4238,7 @@ a.status-card {
 
     a {
       text-decoration: none;
-      color: $ui-base-lighter-color;
+      color: $darker-text-color;
       font-weight: 500;
 
       &:hover {
@@ -4315,7 +4257,7 @@ a.status-card {
     }
 
     .fa {
-      color: $ui-base-lighter-color;
+      color: $darker-text-color;
     }
   }
 }
@@ -4511,7 +4453,7 @@ a.status-card {
     z-index: 4;
     border: 0;
     background: $base-shadow-color;
-    color: $ui-primary-color;
+    color: $darker-text-color;
     transition: none;
     pointer-events: none;
 
@@ -4522,7 +4464,7 @@ a.status-card {
       &:hover,
       &:active,
       &:focus {
-        color: lighten($ui-primary-color, 8%);
+        color: transparentize($darker-text-color, 0.07);
       }
     }
 
@@ -4719,7 +4661,7 @@ a.status-card {
     background-size: cover;
     background-position: center;
     position: absolute;
-    color: $ui-primary-color;
+    color: $darker-text-color;
     text-decoration: none;
     border-radius: 4px;
 
@@ -4727,7 +4669,7 @@ a.status-card {
     &:active,
     &:focus {
       outline: 0;
-      color: $ui-secondary-color;
+      color: transparentize($darker-text-color, 0.07);
 
       &::before {
         content: "";
@@ -4758,7 +4700,7 @@ a.status-card {
   a {
     display: block;
     flex: 1 1 auto;
-    color: $ui-primary-color;
+    color: $darker-text-color;
     padding: 15px 0;
     font-size: 14px;
     font-weight: 500;
@@ -4767,7 +4709,7 @@ a.status-card {
     position: relative;
 
     &.active {
-      color: $ui-secondary-color;
+      color: transparentize($darker-text-color, 0.07);
 
       &::before,
       &::after {
@@ -4802,12 +4744,12 @@ a.status-card {
   padding: 10px 14px;
   padding-bottom: 14px;
   margin-top: 10px;
-  color: $ui-primary-color;
+  color: $lighter-text-color;
   box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
 
   h4 {
     text-transform: uppercase;
-    color: $ui-primary-color;
+    color: $lighter-text-color;
     font-size: 13px;
     font-weight: 500;
     margin-bottom: 10px;
@@ -4823,7 +4765,7 @@ a.status-card {
 
   em {
     font-weight: 500;
-    color: $ui-base-color;
+    color: $inverted-text-color;
   }
 }
 
@@ -4839,11 +4781,11 @@ noscript {
   div {
     font-size: 14px;
     margin: 30px auto;
-    color: $ui-secondary-color;
+    color: $primary-text-color;
     max-width: 400px;
 
     a {
-      color: $ui-highlight-color;
+      color: $highlight-text-color;
       text-decoration: underline;
 
       &:hover {
@@ -4941,7 +4883,6 @@ noscript {
     }
 
     .embed-modal__html {
-      color: $ui-secondary-color;
       outline: 0;
       box-sizing: border-box;
       display: block;
@@ -4950,7 +4891,7 @@ noscript {
       padding: 10px;
       font-family: 'mastodon-font-monospace', monospace;
       background: $ui-base-color;
-      color: $ui-primary-color;
+      color: $primary-text-color;
       font-size: 14px;
       margin: 0;
       margin-bottom: 15px;
@@ -4993,7 +4934,7 @@ noscript {
   &__message {
     position: relative;
     margin-left: 58px;
-    color: $ui-base-lighter-color;
+    color: $darker-text-color;
     padding: 8px 0;
     padding-top: 0;
     padding-bottom: 4px;
@@ -5176,3 +5117,40 @@ noscript {
     background: lighten($ui-highlight-color, 7%);
   }
 }
+
+.account__header .account__header__fields {
+  font-size: 14px;
+  line-height: 20px;
+  overflow: hidden;
+  border-collapse: collapse;
+  margin: 20px -10px -20px;
+  border-bottom: 0;
+
+  tr {
+    border-top: 1px solid lighten($ui-base-color, 8%);
+    text-align: center;
+  }
+
+  th,
+  td {
+    padding: 14px 20px;
+    vertical-align: middle;
+    max-height: 40px;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+  }
+
+  th {
+    color: $darker-text-color;
+    background: darken($ui-base-color, 4%);
+    max-width: 120px;
+    font-weight: 500;
+  }
+
+  td {
+    flex: auto;
+    color: $primary-text-color;
+    background: $ui-base-color;
+  }
+}
diff --git a/app/javascript/styles/mastodon/containers.scss b/app/javascript/styles/mastodon/containers.scss
index e761f58eb..8df2902d2 100644
--- a/app/javascript/styles/mastodon/containers.scss
+++ b/app/javascript/styles/mastodon/containers.scss
@@ -100,7 +100,7 @@
 
   .name {
     flex: 1 1 auto;
-    color: $ui-secondary-color;
+    color: $darker-text-color;
     width: calc(100% - 88px);
 
     .username {
diff --git a/app/javascript/styles/mastodon/emoji_picker.scss b/app/javascript/styles/mastodon/emoji_picker.scss
index 4161cc045..3620a6f54 100644
--- a/app/javascript/styles/mastodon/emoji_picker.scss
+++ b/app/javascript/styles/mastodon/emoji_picker.scss
@@ -7,7 +7,7 @@
 
   font-size: 13px;
   display: inline-block;
-  color: $ui-base-color;
+  color: $inverted-text-color;
 
   .emoji-mart-emoji {
     padding: 6px;
@@ -36,7 +36,7 @@
   display: flex;
   justify-content: space-between;
   padding: 0 6px;
-  color: $ui-primary-color;
+  color: $lighter-text-color;
   line-height: 0;
 }
 
@@ -50,15 +50,15 @@
   cursor: pointer;
 
   &:hover {
-    color: darken($ui-primary-color, 4%);
+    color: opacify($lighter-text-color, 0.04);
   }
 }
 
 .emoji-mart-anchor-selected {
-  color: darken($ui-highlight-color, 3%);
+  color: $highlight-text-color;
 
   &:hover {
-    color: darken($ui-highlight-color, 3%);
+    color: darken($highlight-text-color, 4%);
   }
 
   .emoji-mart-anchor-bar {
@@ -72,7 +72,7 @@
   left: 0;
   width: 100%;
   height: 3px;
-  background-color: darken($ui-highlight-color, 3%);
+  background-color: $highlight-text-color;
 }
 
 .emoji-mart-anchors {
@@ -115,7 +115,7 @@
     display: block;
     width: 100%;
     background: rgba($ui-secondary-color, 0.3);
-    color: $ui-primary-color;
+    color: $inverted-text-color;
     border: 1px solid $ui-secondary-color;
     border-radius: 4px;
 
@@ -184,7 +184,7 @@
   font-size: 14px;
   text-align: center;
   padding-top: 70px;
-  color: $ui-primary-color;
+  color: $lighter-text-color;
 
   .emoji-mart-category-label {
     display: none;
diff --git a/app/javascript/styles/mastodon/footer.scss b/app/javascript/styles/mastodon/footer.scss
index 2d953b34e..ba2a06954 100644
--- a/app/javascript/styles/mastodon/footer.scss
+++ b/app/javascript/styles/mastodon/footer.scss
@@ -2,7 +2,7 @@
   text-align: center;
   margin-top: 30px;
   font-size: 12px;
-  color: darken($ui-secondary-color, 25%);
+  color: $darker-text-color;
 
   .domain {
     font-weight: 500;
diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss
index d74c5a4fd..3a3b4c326 100644
--- a/app/javascript/styles/mastodon/forms.scss
+++ b/app/javascript/styles/mastodon/forms.scss
@@ -15,16 +15,28 @@ code {
     overflow: hidden;
   }
 
+  .row {
+    display: flex;
+    margin: 0 -5px;
+
+    .input {
+      box-sizing: border-box;
+      flex: 1 1 auto;
+      width: 50%;
+      padding: 0 5px;
+    }
+  }
+
   span.hint {
     display: block;
-    color: $ui-primary-color;
+    color: $darker-text-color;
     font-size: 12px;
     margin-top: 4px;
   }
 
   p.hint {
     margin-bottom: 15px;
-    color: $ui-primary-color;
+    color: $darker-text-color;
 
     &.subtle-hint {
       text-align: center;
@@ -32,10 +44,10 @@ code {
       line-height: 18px;
       margin-top: 15px;
       margin-bottom: 0;
-      color: $ui-primary-color;
+      color: $darker-text-color;
 
       a {
-        color: $ui-highlight-color;
+        color: $highlight-text-color;
       }
     }
   }
@@ -232,35 +244,35 @@ code {
     }
 
     &:focus:invalid {
-      border-bottom-color: $error-value-color;
+      border-bottom-color: lighten($error-red, 12%);
     }
 
     &:required:valid {
-      border-bottom-color: $valid-value-color;
+      border-bottom-color: lighten($error-red, 12%);
     }
 
     &:active,
     &:focus {
-      border-bottom-color: $ui-highlight-color;
+      border-bottom-color: $highlight-text-color;
       background: rgba($base-overlay-background, 0.1);
     }
   }
 
   .input.field_with_errors {
     label {
-      color: $error-value-color;
+      color: lighten($error-red, 12%);
     }
 
     input[type=text],
     input[type=email],
     input[type=password] {
-      border-bottom-color: $error-value-color;
+      border-bottom-color: lighten($error-red, 12%);
     }
 
     .error {
       display: block;
       font-weight: 500;
-      color: $error-value-color;
+      color: lighten($error-red, 12%);
       margin-top: 4px;
     }
   }
@@ -344,7 +356,7 @@ code {
       padding: 7px 4px;
       padding-bottom: 9px;
       font-size: 16px;
-      color: $ui-base-lighter-color;
+      color: $darker-text-color;
       font-family: inherit;
       pointer-events: none;
       cursor: default;
@@ -354,7 +366,7 @@ code {
 
 .flash-message {
   background: lighten($ui-base-color, 8%);
-  color: $ui-primary-color;
+  color: $darker-text-color;
   border-radius: 4px;
   padding: 15px 10px;
   margin-bottom: 30px;
@@ -366,7 +378,6 @@ code {
   }
 
   .oauth-code {
-    color: $ui-secondary-color;
     outline: 0;
     box-sizing: border-box;
     display: block;
@@ -375,7 +386,7 @@ code {
     padding: 10px;
     font-family: 'mastodon-font-monospace', monospace;
     background: $ui-base-color;
-    color: $ui-primary-color;
+    color: $primary-text-color;
     font-size: 14px;
     margin: 0;
 
@@ -414,7 +425,7 @@ code {
   text-align: center;
 
   a {
-    color: $ui-primary-color;
+    color: $darker-text-color;
     text-decoration: none;
 
     &:hover {
@@ -427,7 +438,7 @@ code {
 .follow-prompt {
   margin-bottom: 30px;
   text-align: center;
-  color: $ui-primary-color;
+  color: $darker-text-color;
 
   h2 {
     font-size: 16px;
@@ -435,7 +446,7 @@ code {
   }
 
   strong {
-    color: $ui-secondary-color;
+    color: $primary-text-color;
     font-weight: 500;
 
     @each $lang in $cjk-langs {
@@ -472,7 +483,7 @@ code {
 
 .qr-alternative {
   margin-bottom: 20px;
-  color: $ui-secondary-color;
+  color: $darker-text-color;
   flex: 150px;
 
   samp {
@@ -557,7 +568,7 @@ code {
 
 .post-follow-actions {
   text-align: center;
-  color: $ui-primary-color;
+  color: $darker-text-color;
 
   div {
     margin-bottom: 4px;
@@ -570,7 +581,7 @@ code {
 
   h4 {
     font-size: 16px;
-    color: $ui-base-lighter-color;
+    color: $primary-text-color;
     text-align: center;
     margin-bottom: 20px;
     border: 0;
diff --git a/app/javascript/styles/mastodon/landing_strip.scss b/app/javascript/styles/mastodon/landing_strip.scss
index ffa1e149d..651c06ced 100644
--- a/app/javascript/styles/mastodon/landing_strip.scss
+++ b/app/javascript/styles/mastodon/landing_strip.scss
@@ -1,7 +1,7 @@
 .landing-strip,
 .memoriam-strip {
   background: rgba(darken($ui-base-color, 7%), 0.8);
-  color: $ui-primary-color;
+  color: $darker-text-color;
   font-weight: 400;
   padding: 14px;
   border-radius: 4px;
@@ -45,7 +45,7 @@
   padding: 14px;
   border-radius: 4px;
   background: rgba(darken($ui-base-color, 7%), 0.8);
-  color: $ui-secondary-color;
+  color: $darker-text-color;
   font-weight: 400;
   margin-bottom: 20px;
 
@@ -88,7 +88,7 @@
 
     .fa {
       margin-right: 5px;
-      color: $ui-primary-color;
+      color: $darker-text-color;
     }
   }
 
@@ -103,7 +103,7 @@
       text-decoration: none;
 
       span {
-        color: $ui-highlight-color;
+        color: $highlight-text-color;
         font-weight: 400;
       }
     }
diff --git a/app/javascript/styles/mastodon/reset.scss b/app/javascript/styles/mastodon/reset.scss
index cc5ba9d7c..58d4de35f 100644
--- a/app/javascript/styles/mastodon/reset.scss
+++ b/app/javascript/styles/mastodon/reset.scss
@@ -53,11 +53,6 @@ table {
   border-spacing: 0;
 }
 
-::-webkit-scrollbar {
-  width: 8px;
-  height: 8px;
-}
-
 ::-webkit-scrollbar-thumb {
   background: lighten($ui-base-color, 4%);
   border: 0px none $base-border-color;
diff --git a/app/javascript/styles/mastodon/stream_entries.scss b/app/javascript/styles/mastodon/stream_entries.scss
index dfdc48d06..c39163ba8 100644
--- a/app/javascript/styles/mastodon/stream_entries.scss
+++ b/app/javascript/styles/mastodon/stream_entries.scss
@@ -84,7 +84,7 @@
         font-size: 14px;
 
         .status__relative-time {
-          color: $ui-primary-color;
+          color: $lighter-text-color;
         }
       }
     }
@@ -93,7 +93,7 @@
       display: block;
       max-width: 100%;
       padding-right: 25px;
-      color: $ui-base-color;
+      color: $lighter-text-color;
     }
 
     .status__avatar {
@@ -123,7 +123,7 @@
 
       strong {
         font-weight: 500;
-        color: $ui-base-color;
+        color: $inverted-text-color;
 
         @each $lang in $cjk-langs {
           &:lang(#{$lang}) {
@@ -134,15 +134,15 @@
 
       span {
         font-size: 14px;
-        color: $ui-primary-color;
+        color: $inverted-text-color;
       }
     }
 
     .status__content {
-      color: $ui-base-color;
+      color: $inverted-text-color;
 
       a {
-        color: $ui-highlight-color;
+        color: $highlight-text-color;
       }
 
       a.status__content__spoiler-link {
@@ -180,7 +180,7 @@
 
         strong {
           font-weight: 500;
-          color: $ui-base-color;
+          color: $inverted-text-color;
 
           @each $lang in $cjk-langs {
             &:lang(#{$lang}) {
@@ -191,7 +191,7 @@
 
         span {
           font-size: 14px;
-          color: $ui-primary-color;
+          color: $lighter-text-color;
         }
       }
     }
@@ -207,10 +207,10 @@
     }
 
     .status__content {
-      color: $ui-base-color;
+      color: $inverted-text-color;
 
       a {
-        color: $ui-highlight-color;
+        color: $highlight-text-color;
       }
 
       a.status__content__spoiler-link {
@@ -225,7 +225,7 @@
 
     .detailed-status__meta {
       margin-top: 15px;
-      color: $ui-primary-color;
+      color: $lighter-text-color;
       font-size: 14px;
       line-height: 18px;
 
@@ -243,7 +243,7 @@
 
     .status-card {
       border-color: lighten($ui-secondary-color, 4%);
-      color: darken($ui-primary-color, 4%);
+      color: $lighter-text-color;
 
       &:hover {
         background: lighten($ui-secondary-color, 4%);
@@ -252,7 +252,7 @@
 
     .status-card__title,
     .status-card__description {
-      color: $ui-base-color;
+      color: $inverted-text-color;
     }
 
     .status-card__image {
@@ -262,7 +262,7 @@
 
   .media-spoiler {
     background: $ui-base-color;
-    color: $ui-primary-color;
+    color: $darker-text-color;
   }
 
   .pre-header {
@@ -270,7 +270,7 @@
     padding-left: (48px + 14px * 2);
     padding-bottom: 0;
     margin-bottom: -4px;
-    color: $ui-primary-color;
+    color: $lighter-text-color;
     font-size: 14px;
     position: relative;
 
@@ -280,7 +280,7 @@
     }
 
     .status__display-name.muted strong {
-      color: $ui-primary-color;
+      color: $lighter-text-color;
     }
   }
 
diff --git a/app/javascript/styles/mastodon/tables.scss b/app/javascript/styles/mastodon/tables.scss
index 92870e679..c12d84f1c 100644
--- a/app/javascript/styles/mastodon/tables.scss
+++ b/app/javascript/styles/mastodon/tables.scss
@@ -30,7 +30,7 @@
   }
 
   a {
-    color: $ui-highlight-color;
+    color: $highlight-text-color;
     text-decoration: underline;
 
     &:hover {
@@ -68,7 +68,7 @@ a.table-action-link {
   display: inline-block;
   margin-right: 5px;
   padding: 0 10px;
-  color: rgba($primary-text-color, 0.7);
+  color: $darker-text-color;
   font-weight: 500;
 
   &:hover {
diff --git a/app/javascript/styles/mastodon/variables.scss b/app/javascript/styles/mastodon/variables.scss
index e456c27ee..dc4e72a2e 100644
--- a/app/javascript/styles/mastodon/variables.scss
+++ b/app/javascript/styles/mastodon/variables.scss
@@ -18,6 +18,11 @@ $base-overlay-background: $black !default;
 $base-border-color: $white !default;
 $simple-background-color: $white !default;
 $primary-text-color: $white !default;
+$darker-text-color: rgba($primary-text-color, 0.7) !default;
+$highlight-text-color: $classic-highlight-color !default;
+$inverted-text-color: $black !default;
+$lighter-text-color: rgba($inverted-text-color, 0.7) !default;
+$action-button-color: #8d9ac2;
 $valid-value-color: $success-green !default;
 $error-value-color: $error-red !default;
 
@@ -26,7 +31,7 @@ $ui-base-color: $classic-base-color !default;                  // Darkest
 $ui-base-lighter-color: lighten($ui-base-color, 26%) !default; // Lighter darkest
 $ui-primary-color: $classic-primary-color !default;            // Lighter
 $ui-secondary-color: $classic-secondary-color !default;        // Lightest
-$ui-highlight-color: $classic-highlight-color !default;        // Vibrant
+$ui-highlight-color: #2b5fd9;
 
 // Language codes that uses CJK fonts
 $cjk-langs: ja, ko, zh-CN, zh-HK, zh-TW;
diff --git a/app/lib/activitypub/adapter.rb b/app/lib/activitypub/adapter.rb
index f19b04ae6..e880499f1 100644
--- a/app/lib/activitypub/adapter.rb
+++ b/app/lib/activitypub/adapter.rb
@@ -19,6 +19,9 @@ class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base
         'Emoji'                     => 'toot:Emoji',
         'focalPoint'                => { '@container' => '@list', '@id' => 'toot:focalPoint' },
         'featured'                  => 'toot:featured',
+        'schema'                    => 'http://schema.org#',
+        'PropertyValue'             => 'schema:PropertyValue',
+        'value'                     => 'schema:value',
       },
     ],
   }.freeze
diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb
index f7e7a3c23..4124f1660 100644
--- a/app/lib/formatter.rb
+++ b/app/lib/formatter.rb
@@ -71,6 +71,11 @@ class Formatter
     html.html_safe # rubocop:disable Rails/OutputSafety
   end
 
+  def format_field(account, str)
+    return reformat(str).html_safe unless account.local? # rubocop:disable Rails/OutputSafety
+    encode_and_link_urls(str, me: true).html_safe # rubocop:disable Rails/OutputSafety
+  end
+
   def linkify(text)
     html = encode_and_link_urls(text)
     html = simple_format(html, {}, sanitize: false)
@@ -85,12 +90,17 @@ class Formatter
     HTMLEntities.new.encode(html)
   end
 
-  def encode_and_link_urls(html, accounts = nil)
+  def encode_and_link_urls(html, accounts = nil, options = {})
     entities = Extractor.extract_entities_with_indices(html, extract_url_without_protocol: false)
 
+    if accounts.is_a?(Hash)
+      options  = accounts
+      accounts = nil
+    end
+
     rewrite(html.dup, entities) do |entity|
       if entity[:url]
-        link_to_url(entity)
+        link_to_url(entity, options)
       elsif entity[:hashtag]
         link_to_hashtag(entity)
       elsif entity[:screen_name]
@@ -177,10 +187,12 @@ class Formatter
     result.flatten.join
   end
 
-  def link_to_url(entity)
+  def link_to_url(entity, options = {})
     url        = Addressable::URI.parse(entity[:url])
     html_attrs = { target: '_blank', rel: 'nofollow noopener' }
 
+    html_attrs[:rel] = "me #{html_attrs[:rel]}" if options[:me]
+
     Twitter::Autolink.send(:link_to_text, entity, link_html(entity[:url]), url, html_attrs)
   rescue Addressable::URI::InvalidURIError, IDN::Idna::IdnaError
     encode(entity[:url])
diff --git a/app/models/account.rb b/app/models/account.rb
index dd8bad585..db2171102 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -44,6 +44,7 @@
 #  memorial                :boolean          default(FALSE), not null
 #  moved_to_account_id     :integer
 #  featured_collection_url :string
+#  fields                  :jsonb
 #
 
 class Account < ApplicationRecord
@@ -116,7 +117,7 @@ class Account < ApplicationRecord
   scope :without_followers, -> { where(followers_count: 0) }
   scope :with_followers, -> { where('followers_count > 0') }
   scope :expiring, ->(time) { remote.where.not(subscription_expires_at: nil).where('subscription_expires_at < ?', time) }
-  scope :partitioned, -> { order('row_number() over (partition by domain)') }
+  scope :partitioned, -> { order(Arel.sql('row_number() over (partition by domain)')) }
   scope :silenced, -> { where(silenced: true) }
   scope :suspended, -> { where(suspended: true) }
   scope :recent, -> { reorder(id: :desc) }
@@ -192,6 +193,30 @@ class Account < ApplicationRecord
     @keypair ||= OpenSSL::PKey::RSA.new(private_key || public_key)
   end
 
+  def fields
+    (self[:fields] || []).map { |f| Field.new(self, f) }
+  end
+
+  def fields_attributes=(attributes)
+    fields = []
+
+    attributes.each_value do |attr|
+      next if attr[:name].blank?
+      fields << attr
+    end
+
+    self[:fields] = fields
+  end
+
+  def build_fields
+    return if fields.size >= 4
+
+    raw_fields = self[:fields] || []
+    add_fields = 4 - raw_fields.size
+    add_fields.times { raw_fields << { name: '', value: '' } }
+    self.fields = raw_fields
+  end
+
   def magic_key
     modulus, exponent = [keypair.public_key.n, keypair.public_key.e].map do |component|
       result = []
@@ -241,6 +266,17 @@ class Account < ApplicationRecord
     shared_inbox_url.presence || inbox_url
   end
 
+  class Field < ActiveModelSerializers::Model
+    attributes :name, :value, :account, :errors
+
+    def initialize(account, attr)
+      @account = account
+      @name    = attr['name']
+      @value   = attr['value']
+      @errors  = {}
+    end
+  end
+
   class << self
     def readonly_attributes
       super - %w(statuses_count following_count followers_count)
diff --git a/app/models/status.rb b/app/models/status.rb
index 5d309546f..952661169 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -24,6 +24,7 @@
 #  application_id         :integer
 #  in_reply_to_account_id :integer
 #  local_only             :boolean
+#  full_status_text       :text             default(""), not null
 #
 
 class Status < ApplicationRecord
diff --git a/app/policies/status_policy.rb b/app/policies/status_policy.rb
index 369ede2b0..307876856 100644
--- a/app/policies/status_policy.rb
+++ b/app/policies/status_policy.rb
@@ -18,7 +18,7 @@ class StatusPolicy < ApplicationPolicy
   end
 
   def reblog?
-    !direct? && !private? && show?
+    !direct? && (!private? || owned?) && show?
   end
 
   def destroy?
diff --git a/app/serializers/activitypub/actor_serializer.rb b/app/serializers/activitypub/actor_serializer.rb
index df3090726..fcf3bdf17 100644
--- a/app/serializers/activitypub/actor_serializer.rb
+++ b/app/serializers/activitypub/actor_serializer.rb
@@ -11,6 +11,7 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer
   has_one :public_key, serializer: ActivityPub::PublicKeySerializer
 
   has_many :virtual_tags, key: :tag
+  has_many :virtual_attachments, key: :attachment
 
   attribute :moved_to, if: :moved?
 
@@ -107,10 +108,26 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer
     object.emojis
   end
 
+  def virtual_attachments
+    object.fields
+  end
+
   def moved_to
     ActivityPub::TagManager.instance.uri_for(object.moved_to_account)
   end
 
   class CustomEmojiSerializer < ActivityPub::EmojiSerializer
   end
+
+  class Account::FieldSerializer < ActiveModel::Serializer
+    attributes :type, :name, :value
+
+    def type
+      'PropertyValue'
+    end
+
+    def value
+      Formatter.instance.format_field(object.account, object.value)
+    end
+  end
 end
diff --git a/app/serializers/rest/account_serializer.rb b/app/serializers/rest/account_serializer.rb
index 6097acda5..863238eb7 100644
--- a/app/serializers/rest/account_serializer.rb
+++ b/app/serializers/rest/account_serializer.rb
@@ -9,6 +9,16 @@ class REST::AccountSerializer < ActiveModel::Serializer
 
   has_one :moved_to_account, key: :moved, serializer: REST::AccountSerializer, if: :moved_and_not_nested?
 
+  class FieldSerializer < ActiveModel::Serializer
+    attributes :name, :value
+
+    def value
+      Formatter.instance.format_field(object.account, object.value)
+    end
+  end
+
+  has_many :fields
+
   def id
     object.id.to_s
   end
diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb
index 21c2fc57a..da32f9615 100644
--- a/app/services/activitypub/process_account_service.rb
+++ b/app/services/activitypub/process_account_service.rb
@@ -22,13 +22,15 @@ class ActivityPub::ProcessAccountService < BaseService
 
         create_account if @account.nil?
         update_account
-        process_tags(@account)
+        process_tags
       end
     end
 
+    return if @account.nil?
+
     after_protocol_change! if protocol_changed?
     after_key_change! if key_changed?
-    check_featured_collection! if @account&.featured_collection_url&.present?
+    check_featured_collection! if @account.featured_collection_url.present?
 
     @account
   rescue Oj::ParseError
@@ -68,6 +70,7 @@ class ActivityPub::ProcessAccountService < BaseService
     @account.display_name            = @json['name'] || ''
     @account.note                    = @json['summary'] || ''
     @account.locked                  = @json['manuallyApprovesFollowers'] || false
+    @account.fields                  = property_values || {}
   end
 
   def set_fetchable_attributes!
@@ -124,6 +127,11 @@ class ActivityPub::ProcessAccountService < BaseService
     end
   end
 
+  def property_values
+    return unless @json['attachment'].is_a?(Array)
+    @json['attachment'].select { |attachment| attachment['type'] == 'PropertyValue' }.map { |attachment| attachment.slice('name', 'value') }
+  end
+
   def mismatching_origin?(url)
     needle   = Addressable::URI.parse(url).host
     haystack = Addressable::URI.parse(@uri).host
@@ -189,17 +197,18 @@ class ActivityPub::ProcessAccountService < BaseService
     { redis: Redis.current, key: "process_account:#{@uri}" }
   end
 
-  def process_tags(account)
+  def process_tags
     return if @json['tag'].blank?
+
     as_array(@json['tag']).each do |tag|
       case tag['type']
       when 'Emoji'
-        process_emoji tag, account
+        process_emoji tag
       end
     end
   end
 
-  def process_emoji(tag, _account)
+  def process_emoji(tag)
     return if skip_download?
     return if tag['name'].blank? || tag['icon'].blank? || tag['icon']['url'].blank?
 
diff --git a/app/services/process_mentions_service.rb b/app/services/process_mentions_service.rb
index 8e285e1f7..dc8df4a9a 100644
--- a/app/services/process_mentions_service.rb
+++ b/app/services/process_mentions_service.rb
@@ -17,13 +17,11 @@ class ProcessMentionsService < BaseService
       if mention_undeliverable?(status, mentioned_account)
         begin
           mentioned_account = resolve_account_service.call($1)
-        rescue Goldfinger::Error, HTTP::Error
+        rescue Goldfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::UnexpectedResponseError
           mentioned_account = nil
         end
       end
 
-      mentioned_account ||= Account.find_remote(username, domain)
-
       next match if mention_undeliverable?(status, mentioned_account)
 
       mentioned_account.mentions.where(status: status).first_or_create(status: status)
diff --git a/app/services/search_service.rb b/app/services/search_service.rb
index 00a8b3dd7..5bb395942 100644
--- a/app/services/search_service.rb
+++ b/app/services/search_service.rb
@@ -4,7 +4,7 @@ class SearchService < BaseService
   attr_accessor :query, :account, :limit, :resolve
 
   def call(query, limit, resolve = false, account = nil)
-    @query   = query
+    @query   = query.strip
     @account = account
     @limit   = limit
     @resolve = resolve
diff --git a/app/validators/status_pin_validator.rb b/app/validators/status_pin_validator.rb
index 64da04120..2c7bce674 100644
--- a/app/validators/status_pin_validator.rb
+++ b/app/validators/status_pin_validator.rb
@@ -5,6 +5,6 @@ class StatusPinValidator < ActiveModel::Validator
     pin.errors.add(:base, I18n.t('statuses.pin_errors.reblog')) if pin.status.reblog?
     pin.errors.add(:base, I18n.t('statuses.pin_errors.ownership')) if pin.account_id != pin.status.account_id
     pin.errors.add(:base, I18n.t('statuses.pin_errors.private')) unless %w(public unlisted).include?(pin.status.visibility)
-    pin.errors.add(:base, I18n.t('statuses.pin_errors.limit')) if pin.account.status_pins.count > 4
+    pin.errors.add(:base, I18n.t('statuses.pin_errors.limit')) if pin.account.status_pins.count > 4 && pin.account.local?
   end
 end
diff --git a/app/views/about/_administration.html.haml b/app/views/about/_administration.html.haml
new file mode 100644
index 000000000..ec5834f9c
--- /dev/null
+++ b/app/views/about/_administration.html.haml
@@ -0,0 +1,19 @@
+.account
+  .account__wrapper
+    - if @instance_presenter.contact_account
+      = link_to TagManager.instance.url_for(@instance_presenter.contact_account), class: 'account__display-name' do
+        .account__avatar-wrapper
+          .account__avatar{ style: "background-image: url(#{@instance_presenter.contact_account.avatar.url})" }
+        %span.display-name
+          %bdi
+            %strong.display-name__html.emojify= display_name(@instance_presenter.contact_account)
+          %span.display-name__account @#{@instance_presenter.contact_account.acct}
+    - else
+      .account__display-name
+        .account__avatar-wrapper
+          .account__avatar{ style: "background-image: url(#{full_asset_url('avatars/original/missing.png', skip_pipeline: true)})" }
+        %span.display-name
+          %strong= t 'about.contact_missing'
+          %span.display-name__account= t 'about.contact_unavailable'
+
+    = link_to t('about.learn_more'), about_more_path, class: 'button button-alternative'
diff --git a/app/views/about/show.html.haml b/app/views/about/show.html.haml
index 674771cbc..e264c8574 100644
--- a/app/views/about/show.html.haml
+++ b/app/views/about/show.html.haml
@@ -109,26 +109,7 @@
                 %p= t 'about.about_mastodon_html'
               %div.contact
                 %h3= t 'about.administered_by'
-
-                .account
-                  .account__wrapper
-                    - if @instance_presenter.contact_account
-                      = link_to TagManager.instance.url_for(@instance_presenter.contact_account), class: 'account__display-name' do
-                        .account__avatar-wrapper
-                          .account__avatar{ style: "background-image: url(#{@instance_presenter.contact_account.avatar.url})" }
-                        %span.display-name
-                          %bdi
-                            %strong.display-name__html.emojify= display_name(@instance_presenter.contact_account)
-                          %span.display-name__account @#{@instance_presenter.contact_account.acct}
-                    - else
-                      .account__display-name
-                        .account__avatar-wrapper
-                          .account__avatar{ style: "background-image: url(#{full_asset_url('avatars/original/missing.png', skip_pipeline: true)})" }
-                        %span.display-name
-                          %strong= t 'about.contact_missing'
-                          %span.display-name__account= t 'about.contact_unavailable'
-
-                    = link_to t('about.learn_more'), about_more_path, class: 'button button-alternative'
+                = render 'administration'
 
             = render 'features'
 
@@ -143,8 +124,13 @@
       - else
         .column-4.non-preview.landing-page__information
           .landing-page__features
-            %h3= t 'about.what_is_mastodon'
-            %p= t 'about.about_mastodon_html'
+            .features-list
+              %div
+                %h3= t 'about.what_is_mastodon'
+                %p= t 'about.about_mastodon_html'
+              %div.contact
+                %h3= t 'about.administered_by'
+                = render 'administration'
 
             = render 'features'
 
diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml
index 73af27a4c..76f29d591 100644
--- a/app/views/accounts/_header.html.haml
+++ b/app/views/accounts/_header.html.haml
@@ -7,8 +7,8 @@
   .card__bio
     %h1.name
       %span.p-name.emojify= display_name(account)
-      %small
-        %span @#{account.local_username_and_domain}
+      %small<
+        %span>< @#{account.local_username_and_domain}
         = fa_icon('lock') if account.locked?
     - if Setting.show_staff_badge
       - if account.user_admin?
@@ -28,6 +28,14 @@
               %th.emojify>!=i[0]
               %td.emojify>!=i[1]
 
+      - unless account.fields.empty?
+        %table.account__header__fields
+          %tbody
+            - account.fields.each do |field|
+              %tr
+                %th.emojify= field.name
+                %td.emojify= Formatter.instance.format_field(account, field.value)
+
     .details-counters
       .counter{ class: active_nav_class(short_account_url(account)) }
         = link_to short_account_url(account), class: 'u-url u-uid' do
diff --git a/app/views/admin/accounts/_card.html.haml b/app/views/admin/accounts/_card.html.haml
deleted file mode 100644
index 2f5955011..000000000
--- a/app/views/admin/accounts/_card.html.haml
+++ /dev/null
@@ -1,17 +0,0 @@
-.table-wrapper
-  %table.table
-    %tbody
-      %tr
-        %td= t('admin.accounts.show.created_reports')
-        %td= link_to pluralize(account.reports.count, t('admin.accounts.show.report')), admin_reports_path(account_id: account.id)
-      %tr
-        %td= t('admin.accounts.show.targeted_reports')
-        %td= link_to pluralize(account.targeted_reports.count, t('admin.accounts.show.report')), admin_reports_path(target_account_id: account.id)
-      - if account.silenced? || account.suspended?
-        %tr
-          %td= t('admin.accounts.moderation.title')
-          %td
-            - if account.silenced?
-              %p= t('admin.accounts.moderation.silenced')
-            - if account.suspended?
-              %p= t('admin.accounts.moderation.suspended')
diff --git a/app/views/admin/reports/_account_details.html.haml b/app/views/admin/reports/_account_details.html.haml
new file mode 100644
index 000000000..a8af39bef
--- /dev/null
+++ b/app/views/admin/reports/_account_details.html.haml
@@ -0,0 +1,20 @@
+.table-wrapper
+  %table.table
+    %tbody
+      %tr
+        %td= t('admin.reports.account.created_reports')
+        %td= link_to pluralize(account.reports.count, t('admin.reports.account.report')), admin_reports_path(account_id: account.id)
+      %tr
+        %td= t('admin.reports.account.targeted_reports')
+        %td= link_to pluralize(account.targeted_reports.count, t('admin.reports.account.report')), admin_reports_path(target_account_id: account.id)
+      %tr
+        %td= t('admin.reports.account.moderation_notes')
+        %td= link_to pluralize(account.targeted_moderation_notes.count, t('admin.reports.account.note')), admin_reports_path(target_account_id: account.id)
+      - if account.silenced? || account.suspended?
+        %tr
+          %td= t('admin.reports.account.moderation.title')
+          %td
+            - if account.silenced?
+              %p= t('admin.reports.account.moderation.silenced')
+            - if account.suspended?
+              %p= t('admin.reports.account.moderation.suspended')
diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml
index a0c1ca283..60a8cab8e 100644
--- a/app/views/admin/reports/show.html.haml
+++ b/app/views/admin/reports/show.html.haml
@@ -57,11 +57,11 @@
   .report-accounts__item
     %h3= t('admin.reports.reported_account')
     = render 'authorize_follows/card', account: @report.target_account, admin: true
-    = render 'admin/accounts/card', account: @report.target_account
+    = render 'admin/reports/account_details', account: @report.target_account
   .report-accounts__item
     %h3= t('admin.reports.reported_by')
     = render 'authorize_follows/card', account: @report.account, admin: true
-    = render 'admin/accounts/card', account: @report.account
+    = render 'admin/reports/account_details', account: @report.account
 
 %h3= t('admin.reports.comment.label')
 
diff --git a/app/views/invites/_invite.html.haml b/app/views/invites/_invite.html.haml
index 81d67eb7d..1c7ec311d 100644
--- a/app/views/invites/_invite.html.haml
+++ b/app/views/invites/_invite.html.haml
@@ -13,5 +13,5 @@
           = l invite.expires_at
   %td= table_link_to 'link', public_invite_url(invite_code: invite.code), public_invite_url(invite_code: invite.code)
   %td
-    - if invite.expired? && policy(invite).destroy?
+    - if !invite.expired? && policy(invite).destroy?
       = table_link_to 'times', t('invites.delete'), invite_path(invite), method: :delete
diff --git a/app/views/settings/profiles/show.html.haml b/app/views/settings/profiles/show.html.haml
index be7bd0ba0..5f63466d9 100644
--- a/app/views/settings/profiles/show.html.haml
+++ b/app/views/settings/profiles/show.html.haml
@@ -19,6 +19,16 @@
   .fields-group
     = f.input :locked, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.locked')
 
+  .fields-group
+    .input.with_block_label
+      %label= t('simple_form.labels.defaults.fields')
+      %span.hint= t('simple_form.hints.defaults.fields')
+
+      = f.simple_fields_for :fields do |fields_f|
+        .row
+          = fields_f.input :name, placeholder: t('simple_form.labels.account.fields.name')
+          = fields_f.input :value, placeholder: t('simple_form.labels.account.fields.value')
+
   .actions
     = f.button :button, t('generic.save_changes'), type: :submit
 
diff --git a/app/workers/activitypub/synchronize_featured_collection_worker.rb b/app/workers/activitypub/synchronize_featured_collection_worker.rb
index dd676a3ee..7b16d3426 100644
--- a/app/workers/activitypub/synchronize_featured_collection_worker.rb
+++ b/app/workers/activitypub/synchronize_featured_collection_worker.rb
@@ -3,7 +3,7 @@
 class ActivityPub::SynchronizeFeaturedCollectionWorker
   include Sidekiq::Worker
 
-  sidekiq_options queue: 'pull'
+  sidekiq_options queue: 'pull', unique: :until_executed
 
   def perform(account_id)
     ActivityPub::FetchFeaturedCollectionService.new.call(Account.find(account_id))
diff --git a/config/locales/ar.yml b/config/locales/ar.yml
index c316a2fa5..8b9a6688a 100644
--- a/config/locales/ar.yml
+++ b/config/locales/ar.yml
@@ -204,6 +204,7 @@ ar:
       severity: الشدة
       show:
         affected_accounts:
+          one: هناك حساب واحد متأثر في قاعدة البيانات
           other: هناك %{count} حسابات في قاعدة البيانات متأثرة بذلك
         retroactive:
           silence: إلغاء الكتم عن كافة الحسابات المتواجدة على هذا النطاق
@@ -262,6 +263,7 @@ ar:
     settings:
       activity_api_enabled:
         desc_html: عدد المنشورات المحلية و المستخدمين النشطين و التسجيلات الأسبوعية الجديدة
+        title: نشر مُجمل الإحصائيات عن نشاط المستخدمين
       bootstrap_timeline_accounts:
         title: الإشتراكات الإفتراضية للمستخدمين الجدد
       contact_information:
@@ -274,12 +276,14 @@ ar:
         title: نشر عدد مثيلات الخوادم التي تم مصادفتها
       registrations:
         closed_message:
+          desc_html: يتم عرضه على الصفحة الرئيسية عندما يتم غلق تسجيل الحسابات الجديدة. يمكنكم إستخدام علامات الأيتش تي أم أل HTML
           title: رسالة التسجيلات المقفلة
         deletion:
           desc_html: السماح لأي مستخدم إغلاق حسابه
           title: السماح بحذف الحسابات
         min_invite_role:
           disabled: لا أحد
+          title: المستخدِمون المصرح لهم لإرسال الدعوات
         open:
           desc_html: السماح للجميع بإنشاء حساب
           title: فتح التسجيل
diff --git a/config/locales/devise.fi.yml b/config/locales/devise.fi.yml
index 91ab9559c..e356abf9f 100644
--- a/config/locales/devise.fi.yml
+++ b/config/locales/devise.fi.yml
@@ -2,60 +2,81 @@
 fi:
   devise:
     confirmations:
-      confirmed: Sähköpostisi on onnistuneesti vahvistettu.
-      send_instructions: Saat kohta sähköpostiisi ohjeet kuinka voit aktivoida tilisi.
-      send_paranoid_instructions: Jos sähköpostisi on meidän tietokannassa, saat pian ohjeet sen varmentamiseen.
+      confirmed: Sähköpostiosoitteen vahvistus onnistui.
+      send_instructions: Saat kohta sähköpostitse ohjeet, kuinka vahvistat sähköpostiosoitteen. Jos et saa viestiä, tarkista roskapostikansio.
+      send_paranoid_instructions: Jos sähköpostiosoite on tietokannassamme, saat pian ohjeet, kuinka vahvistat osoitteen. Jos et saa viestiä, tarkista roskapostikansio.
     failure:
       already_authenticated: Olet jo kirjautunut sisään.
-      inactive: Tiliäsi ei ole viellä aktivoitu.
+      inactive: Tiliäsi ei ole vielä aktivoitu.
       invalid: Virheellinen %{authentication_keys} tai salasana.
-      last_attempt: Sinulla on yksi yritys jäljellä tai tili lukitaan.
+      last_attempt: Voit yrittää enää kerran, ennen kuin tili lukitaan.
       locked: Tili on lukittu.
       not_found_in_database: Virheellinen %{authentication_keys} tai salasana.
-      timeout: Sessiosi on umpeutunut. Kirjaudu sisään jatkaaksesi.
-      unauthenticated: Sinun tarvitsee kirjautua sisään tai rekisteröityä jatkaaksesi.
-      unconfirmed: Sinun tarvitsee varmentaa sähköpostisi jatkaaksesi.
+      timeout: Istunto on umpeutunut. Jatka kirjautumalla sisään.
+      unauthenticated: Kirjaudu sisään tai rekisteröidy, jos haluat jatkaa.
+      unconfirmed: Vahvista sähköpostiosoitteesi, ennen kuin jatkat.
     mailer:
       confirmation_instructions:
-        subject: 'Mastodon: Varmistus ohjeet'
+        action: Vahvista sähköpostiosoite
+        explanation: Olet luonut tilin palvelimelle %{host} käyttäen tätä sähköpostiosoitetta. Aktivoi tili yhdellä klikkauksella. Jos et luonut tiliä itse, voit jättää tämän viestin huomiotta.
+        extra_html: Katso myös <a href="%{terms_path}">instanssin säännöt</a> ja <a href="%{policy_path}">käyttöehdot</a>.
+        subject: 'Mastodon: Vahvistusohjeet - %{instance}'
+        title: Vahvista sähköpostiosoite
+      email_changed:
+        explanation: 'Tilin sähköpostiosoitteeksi vaihdetaan:'
+        extra: Jos et vaihtanut sähköpostiosoitettasi, joku muu on todennäköisesti päässyt käyttämään tiliäsi. Vaihda salasanasi viipymättä. Jos et pääse kirjautumaan tilillesi, ota yhteyttä instanssin ylläpitäjään.
+        subject: 'Mastodon: Sähköpostiosoite vaihdettu'
+        title: Uusi sähköpostiosoite
       password_change:
+        explanation: Tilin salasana on vaihdettu.
+        extra: Jos et vaihtanut salasanaasi, joku muu on todennäköisesti päässyt käyttämään tiliäsi. Vaihda salasanasi viipymättä. Jos et pääse kirjautumaan tilillesi, ota yhteyttä instanssin ylläpitäjään.
         subject: 'Mastodon: Salasana vaihdettu'
+        title: Salasana vaihdettu
+      reconfirmation_instructions:
+        explanation: Vahvista uusi sähköpostiosoite, niin muutos astuu voimaan.
+        extra: Jos et tehnyt muutosta itse, voit jättää tämän viestin huomiotta. Mastodon-tilin sähköpostiosoitetta ei vaihdeta, ennen kuin klikkaat yllä olevaa linkkiä.
+        subject: 'Mastodon: Vahvista sähköpostiosoite - %{instance}'
+        title: Vahvista sähköpostiosoite
       reset_password_instructions:
-        subject: 'Mastodon: Salasanan vaihto ohjeet'
+        action: Vaihda salasana
+        explanation: Pyysit tilillesi uuden salasanan.
+        extra: Jos et tehnyt pyyntöä itse, voit jättää tämän viestin huomiotta. Salasanaasi ei vaihdeta, ennen kuin klikkaat yllä olevaa linkkiä ja luot uuden salasanan.
+        subject: 'Mastodon: Ohjeet salasanan vaihtoon'
+        title: Salasanan vaihto
       unlock_instructions:
-        subject: 'Mastodon: Avauksen ohjeet'
+        subject: 'Mastodon: Ohjeet lukituksen poistoon'
     omniauth_callbacks:
-      failure: Varmennus %{kind} epäonnistui koska "%{reason}".
-      success: Onnistuneesti varmennettu %{kind} tilillä.
+      failure: Tunnistautuminen lähteestä %{kind} ei onnistunut, koska "%{reason}".
+      success: Tunnistautuminen tililtä %{kind} onnistui.
     passwords:
-      no_token: Et pääse tälle sivulle ilman salasanan vaihto sähköpostia. Jos tulet tämmöisestä postista, varmista että sinulla on täydellinen URL.
-      send_instructions: Jos sähköpostisi on meidän tietokannassa, saat pian ohjeet salasanan palautukseen.
-      send_paranoid_instructions: Jos sähköpostisi on meidän tietokannassa, saat pian ohjeet salasanan palautukseen.
-      updated: Salasanasi vaihdettu onnistuneesti. Olet nyt kirjautunut sisään.
-      updated_not_active: Salasanasi vaihdettu onnistuneesti.
+      no_token: Tälle sivulle pääsee vain salasananvaihtoviestin kautta. Jos tiedät tulevasi salasananvaihtoviestin kautta, varmista, että käytät koko viestissä mainittua URL-osoitetta.
+      send_instructions: Jos sähköpostiosoite on tietokannassamme, siihen lähetetään pian linkki salasanan vaihtoon. Jos et saa viestiä, tarkista roskapostikansio.
+      send_paranoid_instructions: Jos sähköpostiosoite on tietokannassamme, siihen lähetetään pian linkki salasanan vaihtoon. Jos et saa viestiä, tarkista roskapostikansio.
+      updated: Salasanan vaihto onnistui. Olet nyt kirjautunut sisään.
+      updated_not_active: Salasanan vaihto onnistui.
     registrations:
-      destroyed: Näkemiin! Tilisi on onnistuneesti peruttu. Toivottavasti näemme joskus uudestaan.
-      signed_up: Tervetuloa! Rekisteröitymisesi onnistu.
-      signed_up_but_inactive: Olet onnistuneesti rekisteröitynyt, mutta emme voi kirjata sinua sisään koska tiliäsi ei ole viellä aktivoitu.
-      signed_up_but_locked: Olet onnistuneesti rekisteröitynyt, mutta emme voi kirjata sinua sisään koska tilisi on lukittu.
-      signed_up_but_unconfirmed: Varmistuslinkki on lähetty sähköpostiisi. Seuraa sitä jotta tilisi voidaan aktivoida.
-      update_needs_confirmation: Tilisi on onnistuneesti päivitetty, mutta meidän tarvitsee vahvistaa sinun uusi sähköpostisi. Tarkista sähköpostisi ja seuraa viestissä tullutta linkkiä varmistaaksesi uuden osoitteen..
-      updated: Tilisi on onnistuneesti päivitetty.
+      destroyed: Tilisi on poistettu. Näkemiin ja tervetuloa uudelleen!
+      signed_up: Tervetuloa! Rekisteröityminen onnistui.
+      signed_up_but_inactive: Rekisteröityminen onnistui. Emme kuitenkaan voi kirjata sinua sisään, sillä tiliäsi ei ole vielä aktivoitu.
+      signed_up_but_locked: Rekisteröityminen onnistui. Emme kuitenkaan voi kirjata sinua sisään, sillä tilisi on lukittu.
+      signed_up_but_unconfirmed: Sähköpostiosoitteeseesi on lähetetty vahvistuslinkki. Aktivoi tili seuraamalla linkkiä. Jos et saanut viestiä, tarkista roskapostikansio.
+      update_needs_confirmation: Tilin päivitys onnistui, mutta uusi sähköpostiosoite on vahvistettava. Tarkista sähköpostisi ja vahvista uusi sähköpostiosoite seuraamalla vahvistuslinkkiä. Jos et saanut viestiä, tarkista roskapostikansio.
+      updated: Tilin päivitys onnistui.
     sessions:
-      already_signed_out: Ulos kirjautuminen onnistui.
+      already_signed_out: Uloskirjautuminen onnistui.
       signed_in: Sisäänkirjautuminen onnistui.
-      signed_out: Ulos kirjautuminen onnistui.
+      signed_out: Uloskirjautuminen onnistui.
     unlocks:
-      send_instructions: Saat sähköpostiisi pian ohjeet, jolla voit avata tilisi uudestaan.
-      send_paranoid_instructions: Jos tilisi on olemassa, saat sähköpostiisi pian ohjeet tilisi avaamiseen.
-      unlocked: Tilisi on avattu onnistuneesti. Kirjaudu normaalisti sisään.
+      send_instructions: Saat pian sähköpostitse ohjeet tilin lukituksen poistoon. Jos et saanut viestiä, tarkista roskapostikansio.
+      send_paranoid_instructions: Jos tili on olemassa, saat pian sähköpostitse ohjeet tilin lukituksen poistoon. Jos et saanut viestiä, tarkista roskapostikansio.
+      unlocked: Tilin lukituksen poisto onnistui. Jatka kirjautumalla sisään.
   errors:
     messages:
-      already_confirmed: on jo varmistettu. Yritä kirjautua sisään
-      confirmation_period_expired: pitää varmistaa %{period} sisällä, ole hyvä ja pyydä uusi
-      expired: on erääntynyt, ole hyvä ja pyydä uusi
+      already_confirmed: on jo vahvistettu. Yritä kirjautua sisään
+      confirmation_period_expired: on vahvistettava %{period} sisällä. Pyydä uusi
+      expired: on vanhentunut. Pyydä uusi
       not_found: ei löydy
       not_locked: ei ollut lukittu
       not_saved:
-        one: '1 virhe esti %{resource} tallennuksen:'
-        other: "%{count} virhettä esti %{resource} tallennuksen:"
+        one: '1 virhe esti kohteen %{resource} tallennuksen:'
+        other: "%{count} virhettä esti kohteen %{resource} tallennuksen:"
diff --git a/config/locales/devise.sk.yml b/config/locales/devise.sk.yml
index 2ce328d22..e9c5dd455 100644
--- a/config/locales/devise.sk.yml
+++ b/config/locales/devise.sk.yml
@@ -13,13 +13,13 @@ sk:
       locked: Váš účet je zamknutý.
       not_found_in_database: Nesprávny %{authentication_keys} alebo heslo.
       timeout: Vaša aktívna sezóna vypršala. Pre pokračovanie sa prosím znovu prihláste.
-      unauthenticated: Pred pokračovaním sa musíte zaregistrovať alebo prihlásiť.
-      unconfirmed: Pred pokračovaním musíte potvrdiť svoj email.
+      unauthenticated: K pokračovaniu sa musíš zaregistrovať alebo prihlásiť.
+      unconfirmed: Pred pokračovaním musíš potvrdiť svoj email.
     mailer:
       confirmation_instructions:
-        action: Potvrite emailovú adresu
-        explanation: S touto email adresou ste si vytvoril/a účet na %{host}. Si iba jeden klik od jeho aktivácie. Pokiaľ ste to ale nebol/a vy, prosím ignoruj tento email.
-        extra_html: Prosím pozri sa aj na <a href="%{terms_path}"> pravidla tohto servera,</a> a <a href="%{policy_path}"> naše užívaťeľské podiemky</a>.
+        action: Potvŕď emailovú adresu
+        explanation: S touto emailovou adresou si si vytvoril/a účet na %{host}. Si iba jeden klik od jeho aktivácie. Pokiaľ si to ale nebol/a ty, prosím ignoruj tento email.
+        extra_html: Prosím pozri sa aj na <a href="%{terms_path}"> pravidlá tohto servera,</a> a <a href="%{policy_path}"> naše užívaťeľské podiemky</a>.
         subject: 'Mastodon: Potvrdzovacie inštrukcie pre %{instance}'
         title: Potvrď emailovú adresu
       email_changed:
diff --git a/config/locales/devise.zh-HK.yml b/config/locales/devise.zh-HK.yml
index 12d6e3c2c..ca16e06aa 100644
--- a/config/locales/devise.zh-HK.yml
+++ b/config/locales/devise.zh-HK.yml
@@ -78,5 +78,5 @@ zh-HK:
       not_found: 找不到
       not_locked: 並未被鎖定
       not_saved:
-        one: 1 個錯誤令 %{resource} 被法被儲存︰
-        other: "%{count} 個錯誤令 %{resource} 被法被儲存︰"
+        one: 1 個錯誤令 %{resource} 無法被儲存︰
+        other: "%{count} 個錯誤令 %{resource} 無法被儲存︰"
diff --git a/config/locales/doorkeeper.fi.yml b/config/locales/doorkeeper.fi.yml
index 8c1baf909..a3b878b65 100644
--- a/config/locales/doorkeeper.fi.yml
+++ b/config/locales/doorkeeper.fi.yml
@@ -3,17 +3,19 @@ fi:
   activerecord:
     attributes:
       doorkeeper/application:
-        name: Nimi
-        redirect_uri: Uudelleenohjaus URI
+        name: Sovelluksen nimi
+        redirect_uri: Uudelleenohjauksen URI
+        scopes: Oikeudet
+        website: Sovelluksen verkkosivu
     errors:
       models:
         doorkeeper/application:
           attributes:
             redirect_uri:
               fragment_present: ei voi sisältää osia.
-              invalid_uri: pitää olla validi URI.
-              relative_uri: pitää olla täydellinen URI.
-              secured_uri: pitää olla HTTPS/SSL URI.
+              invalid_uri: on oltava kelvollinen URI.
+              relative_uri: on oltava täydellinen URI.
+              secured_uri: on oltava HTTPS/SSL-URI.
   doorkeeper:
     applications:
       buttons:
@@ -25,89 +27,93 @@ fi:
       confirmations:
         destroy: Oletko varma?
       edit:
-        title: Muokkaa applikaatiota
+        title: Muokkaa sovellusta
       form:
-        error: Whoops! Tarkista lomakkeesi mahdollisten virheiden varalta
+        error: Hups! Tarkista, että lomakkeessa ei ole virheitä
       help:
         native_redirect_uri: Käytä %{native_redirect_uri} paikallisiin testeihin
-        redirect_uri: Käytä yhtä riviä per URI
-        scopes: Erota scopet välilyönnein. Jätä tyhjäksi käyteksi oletus scopeja.
+        redirect_uri: Lisää jokainen URI omalle rivilleen
+        scopes: Erota oikeudet välilyönnein. Jos kenttä jätetään tyhjäksi, käytetään oletusoikeuksia.
       index:
-        callback_url: Callback URL
+        application: Sovellus
+        callback_url: Takaisinkutsu-URL
+        delete: Poista
         name: Nimi
-        new: Uusi applikaatio
-        title: Sinun applikaatiosi
+        new: Uusi sovellus
+        scopes: Oikeudet
+        show: Näytä
+        title: Omat sovellukset
       new:
-        title: Uusi applikaatio
+        title: Uusi sovellus
       show:
         actions: Toiminnot
-        application_id: Applikaation Id
-        callback_urls: Callback urls
-        scopes: Scopet
-        secret: Salainen avain
-        title: 'Applikaatio: %{name}'
+        application_id: Asiakasohjelman tunnus
+        callback_urls: Takaisinkutsu-URL:t
+        scopes: Oikeudet
+        secret: Asiakasohjelman salainen avain
+        title: 'Sovellus: %{name}'
     authorizations:
       buttons:
         authorize: Valtuuta
         deny: Evää
       error:
-        title: Virhe on tapahtunut
+        title: Tapahtui virhe
       new:
         able_to: Se voi
-        prompt: Applikaatio %{client_name} pyytää lupaa tilillesi
+        prompt: Sovellus %{client_name} pyytää lupaa käyttää tiliäsi
         title: Valtuutus vaaditaan
       show:
-        title: Kopioi tämä valtuutuskoodi ja liitä se applikaatioon.
+        title: Kopioi tämä valtuutuskoodi ja liitä se sovellukseen.
     authorized_applications:
       buttons:
-        revoke: Evää
+        revoke: Peru
       confirmations:
         revoke: Oletko varma?
       index:
-        application: Applikaatio
+        application: Sovellus
         created_at: Valtuutettu
         date_format: "%Y-%m-%d %H:%M:%S"
-        scopes: Scopet
-        title: Valtuuttamasi applikaatiot
+        scopes: Oikeudet
+        title: Valtuutetut sovellukset
     errors:
       messages:
-        access_denied: Resurssin omistaja tai valtuutus palvelin hylkäsi pyynnönr.
-        credential_flow_not_configured: Resurssin omistajan salasana epäonnistui koska Doorkeeper.configure.resource_owner_from_credentials ei ole konfiguroitu.
-        invalid_client: Asiakkaan valtuutus epäonnistui koska tuntematon asiakas, asiakas ei sisältänyt valtuutusta, tai tukematon valtuutus tapa.
-        invalid_grant: Antamasi valtuutus lupa on joko väärä, erääntynyt, peruttu, ei vastaa uudelleenohjaus URI jota käytetään valtuutus pyynnössä, tai se myönnettin toiselle asiakkaalle.
-        invalid_redirect_uri: Uudelleenohjaus uri ei ole oikein.
-        invalid_request: Pyynnöstä puutti parametri, sisältää tukemattoman parametri arvonn, tai on korruptoitunut.
-        invalid_resource_owner: Annetut resurssin omistajan tunnnukset ovat väärät, tai resurssin omistajaa ei löydy
-        invalid_scope: Pyydetty scope on väärä, tuntemat, tai korruptoitunut.
+        access_denied: Resurssin omistaja tai valtuutuspalvelin hylkäsi pyynnön.
+        credential_flow_not_configured: Resurssin omistajan salasana epäonnistui, koska asetusta Doorkeeper.configure.resource_owner_from_credentials ei ole konfiguroitu.
+        invalid_client: Asiakasohjelman valtuutus epäonnistui, koska asiakas on tuntematon, asiakkaan valtuutus ei ollut mukana tai valtuutustapaa ei tueta.
+        invalid_grant: Valtuutuslupa on virheellinen, umpeutunut, peruttu, valtuutuspyynnössä käytettyä uudelleenohjaus-URI:tä vastaamaton tai myönnetty toiselle asiakkaalle.
+        invalid_redirect_uri: Uudelleenohjaus-URI on virheellinen.
+        invalid_request: Pyynnöstä puuttuu vaadittu parametri, se sisältää tukemattoman parametriarvon tai on muulla tavoin väärin muotoiltu.
+        invalid_resource_owner: Annetut resurssin omistajan tunnnukset ovat virheelliset, tai resurssin omistajaa ei löydy
+        invalid_scope: Pyydetyt oikeudet ovat virheellisiä, tuntemattomia tai väärin muotoiltuja.
         invalid_token:
-          expired: Access token vanhentunut
-          revoked: Access token evätty
-          unknown: Access token väärä
-        resource_owner_authenticator_not_configured: Resurssin omistajan etsiminen epäonnistui koska Doorkeeper.configure.resource_owner_authenticator ei ole konfiguroitu.
-        server_error: Valtuutus palvelin kohtasi odottamattoman virheen joka esti sitä täyttämästä pyyntöä.
-        temporarily_unavailable: Valtuutus palvelin ei voi tällä hetkellä käsitellä pyyntöäsi joko väliaikaisen ruuhkan tai huollon takia.
-        unauthorized_client: Asiakas ei ole valtuutettu tekemään tätä pyyntöä käyttäen tätä metodia.
-        unsupported_grant_type: Valtuutus grant type ei ole tuettu valtuutus palvelimella.
-        unsupported_response_type: Valtuutus palvelin ei tue tätä vastaus tyyppiä.
+          expired: Käyttöoikeustunnus on vanhentunut
+          revoked: Käyttöoikeustunnus on peruttu
+          unknown: Käyttöoikeustunnus on virheellinen
+        resource_owner_authenticator_not_configured: Resurssin omistajaa ei löytynyt, koska asetusta Doorkeeper.configure.resource_owner_authenticator ei ole konfiguroitu.
+        server_error: Valtuutuspalvelin kohtasi odottamattoman virheen, joka esti pyynnön täyttämisen.
+        temporarily_unavailable: Valtuutuspalvelin ei voi tällä hetkellä käsitellä pyyntöä joko väliaikaisen ruuhkan tai huollon takia.
+        unauthorized_client: Asiakkaalla ei ole valtuuksia tehdä tätä pyyntöä tällä metodilla.
+        unsupported_grant_type: Valtuutuspalvelin ei tue tätä valtuutusluvan tyyppiä.
+        unsupported_response_type: Valtuutuspalvelin ei tue tätä vastauksen tyyppiä.
     flash:
       applications:
         create:
-          notice: Applikaatio luotu.
+          notice: Sovellus luotu.
         destroy:
-          notice: Applikaatio poistettu.
+          notice: Sovellus poistettu.
         update:
-          notice: Applikaatio päivitetty.
+          notice: Sovellus päivitetty.
       authorized_applications:
         destroy:
-          notice: Applikaatio tuhottu.
+          notice: Sovellus peruttu.
     layouts:
       admin:
         nav:
-          applications: Applikaatiot
-          oauth2_provider: OAuth2 Provider
+          applications: Sovellukset
+          oauth2_provider: OAuth2-palveluntarjoaja
       application:
-        title: OAuth valtuutus tarvitaan
+        title: OAuth-valtuutus tarvitaan
     scopes:
-      follow: seuraa, estä, peru esto ja lopeta tilien seuraaminen
-      read: lukea tilin dataa
+      follow: seurata, estää, perua eston ja lopettaa tilien seuraaminen
+      read: lukea tilin tietoja
       write: julkaista puolestasi
diff --git a/config/locales/doorkeeper.sk.yml b/config/locales/doorkeeper.sk.yml
index 7a285eb4f..bda26429e 100644
--- a/config/locales/doorkeeper.sk.yml
+++ b/config/locales/doorkeeper.sk.yml
@@ -63,7 +63,7 @@ sk:
         prompt: Aplikácia %{client_name} žiada prístup k vašemu účtu
         title: Je potrebná autorizácia
       show:
-        title: Skopírujte tento autorizačný kód a vložte ho do aplikácie.
+        title: Skopíruj tento autorizačný kód a vlož ho do aplikácie.
     authorized_applications:
       buttons:
         revoke: Zrušiť oprávnenie
diff --git a/config/locales/doorkeeper.zh-HK.yml b/config/locales/doorkeeper.zh-HK.yml
index 747108762..4f46a416a 100644
--- a/config/locales/doorkeeper.zh-HK.yml
+++ b/config/locales/doorkeeper.zh-HK.yml
@@ -14,7 +14,7 @@ zh-HK:
             redirect_uri:
               fragment_present: URI 不可包含 "#fragment" 部份
               invalid_uri: 必需有正確的 URI.
-              relative_uri: 必需為絕對 URI.
+              relative_uri: 必需為完整 URI.
               secured_uri: 必需使用有 HTTPS/SSL 加密的 URI.
   doorkeeper:
     applications:
@@ -63,7 +63,7 @@ zh-HK:
         prompt: 應用程式 %{client_name} 要求得到你用戶的部份權限
         title: 需要用戶授權
       show:
-        title: Copy this authorization code and paste it to the application.
+        title: 請把這個授權碼複製到應用程式中。
     authorized_applications:
       buttons:
         revoke: 取消授權
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 65ae9182f..645999d66 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -259,6 +259,16 @@ en:
       created_msg: Report note successfully created!
       destroyed_msg: Report note successfully deleted!
     reports:
+      account:
+        created_reports: Reports created by this account
+        moderation:
+          silenced: Silenced
+          suspended: Suspended
+          title: Moderation
+        moderation_notes: Moderation Notes
+        note: note
+        report: report
+        targeted_reports: Reports made about this account
       action_taken_by: Action taken by
       are_you_sure: Are you sure?
       assign_to_self: Assign to me
@@ -283,6 +293,7 @@ en:
       nsfw:
         'false': Unhide media attachments
         'true': Hide media attachments
+      reopen: Reopen Report
       report: 'Report #%{id}'
       report_contents: Contents
       reported_account: Reported account
@@ -624,6 +635,10 @@ en:
     missing_resource: Could not find the required redirect URL for your account
     proceed: Proceed to follow
     prompt: 'You are going to follow:'
+  remote_unfollow:
+    error: Error
+    title: Title
+    unfollowed: Unfollowed
   sessions:
     activity: Last activity
     browser: Browser
diff --git a/config/locales/eo.yml b/config/locales/eo.yml
index a896592b0..27c62f899 100644
--- a/config/locales/eo.yml
+++ b/config/locales/eo.yml
@@ -24,12 +24,12 @@ eo:
       within_reach_body: Pluraj aplikaĵoj por iOS, Android, kaj aliaj platformoj danke al API-medio bonveniga por programistoj permesas resti en kontakto kun viaj amikoj ĉie.
       within_reach_title: Ĉiam kontaktebla
     generic_description: "%{domain} estas unu servilo en la reto"
-    hosted_on: Mastodon gastigita en %{domain}
+    hosted_on: "%{domain} estas nodo de Mastodon"
     learn_more: Lerni pli
     other_instances: Listo de nodoj
     source_code: Fontkodo
     status_count_after: mesaĝoj
-    status_count_before: Kiu publikigis
+    status_count_before: Kie skribiĝis
     user_count_after: uzantoj
     user_count_before: Hejmo de
     what_is_mastodon: Kio estas Mastodon?
@@ -358,7 +358,7 @@ eo:
     warning: Estu tre atenta kun ĉi tiu datumo. Neniam diskonigu ĝin al iu ajn!
     your_token: Via alira ĵetono
   auth:
-    agreement_html: Per registriĝo, vi konsentas kun <a href="%{rules_path}">la reguloj de la nodo</a> kaj <a href="%{terms_path}">niaj uzkondiĉoj</a>.
+    agreement_html: Per registriĝo, vi konsentas kun <a href="%{rules_path}">la reguloj de nia nodo</a> kaj <a href="%{terms_path}">niaj uzkondiĉoj</a>.
     change_password: Pasvorto
     confirm_email: Konfirmi retadreson
     delete_account: Forigi konton
diff --git a/config/locales/es.yml b/config/locales/es.yml
index b5e540e28..74045074e 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -546,7 +546,7 @@ es:
           quadrillion: Q
           thousand: K
           trillion: T
-          unit: ''
+          unit: " "
   pagination:
     newer: Más nuevo
     next: Próximo
@@ -634,6 +634,15 @@ es:
     two_factor_authentication: Autenticación de dos factores
     your_apps: Tus aplicaciones
   statuses:
+    attached:
+      description: 'Adjunto: %{attached}'
+      image:
+        one: "%{count} imagen"
+        other: "%{count} imágenes"
+      video:
+        one: "%{count} vídeo"
+        other: "%{count} vídeos"
+    content_warning: 'Alerta de contenido: %{warning}'
     open_in_web: Abrir en web
     over_character_limit: Límite de caracteres de %{max} superado
     pin_errors:
@@ -680,7 +689,7 @@ es:
     backup_ready:
       explanation: Has solicitado una copia completa de tu cuenta de Mastodon. ¡Ya está preparada para descargar!
       subject: Tu archivo está preparado para descargar
-      title: Recogida del archivo
+      title: Descargar archivo
     welcome:
       edit_profile_action: Configurar el perfil
       edit_profile_step: Puedes personalizar tu perfil subiendo un avatar, cabecera, cambiando tu nombre para mostrar y más. Si te gustaría revisar seguidores antes de autorizarlos a que te sigan, puedes bloquear tu cuenta.
diff --git a/config/locales/fi.yml b/config/locales/fi.yml
index 939ebd10a..62f6560bf 100644
--- a/config/locales/fi.yml
+++ b/config/locales/fi.yml
@@ -1,31 +1,35 @@
 ---
 fi:
   about:
-    about_mastodon_html: Mastodon on <em>vapaa, avoimeen lähdekoodiin perustuva</em> sosiaalinen verkosto. <em>Hajautettu</em> vaihtoehto kaupallisille alustoille, se välttää eiskit yhden yrityksen monopolisoinnin sinun viestinnässäsi. Valitse palvelin mihin luotat &mdash; minkä tahansa valitset, voit vuorovaikuttaa muiden kanssa. Kuka tahansa voi luoda Mastodon palvelimen ja ottaa osaa <em>sosiaaliseen verkkoon</em> saumattomasti.
+    about_hashtag_html: Nämä ovat hashtagilla <strong>#%{hashtag}</strong> merkittyjä julkisia tuuttauksia. Voit vastata niihin, jos sinulla on tili jossain päin fediversumia.
+    about_mastodon_html: Mastodon on sosiaalinen verkosto. Se on toteutettu avoimilla verkkoprotokollilla ja vapailla, avoimen lähdekoodin ohjelmistoilla, ja se toimii hajautetusti samaan tapaan kuin sähköposti.
     about_this: Tietoja tästä palvelimesta
-    closed_registrations: Rekisteröityminen tässä instanssissa on juuri nyt suljettu. Mutta! Voit yhdistää täysin samaan, yhteiseen verkostoon rekisteröitymällä jossain toisessa instanssissa.
+    closed_registrations: Tähän instanssiin ei voi tällä hetkellä rekisteröityä. Voit kuitenkin luoda tilin johonkin toiseen instanssiin ja käyttää samaa verkostoa sitä kautta.
     contact: Ota yhteyttä
     contact_missing: Ei asetettu
     contact_unavailable: Ei saatavilla
     description_headline: Mikä on %{domain}?
-    domain_count_after: muuhun palvelimeen
+    domain_count_after: muuhun instanssiin
     domain_count_before: Yhdistyneenä
     extended_description_html: |
       <h3>Hyvä paikka säännöille</h3>
-      <p>Pidennettyä kuvausta ei ole vielä asetettu.</p>
+      <p>Pidempää kuvausta ei ole vielä laadittu.</p>
     features:
-      humane_approach_body: Muiden verkostojen virheistä oppien, Mastodon pyrkii tekemään eettisiä valintoja suunnittelussa taistellakseen sosiaalisen median väärinkäyttöä vastaan.
-      humane_approach_title: Humaanimpi lähestymistapa
-      not_a_product_body: Mastodon ei ole kaupallinen verkosto. Ei mainoksia, ei tiedonlouhintaa, ei suljettuja sisäpiirejä. Mastodonissa ei ole keskitettyä auktoriteettiä.
+      humane_approach_body: Mastodonissa otetaan oppia muiden verkostojen virheistä, ja sen suunnittelussa pyritään toimimaan eettisesti ja ehkäisemään sosiaalisen median väärinkäyttöä.
+      humane_approach_title: Ihmisläheisempi ote
+      not_a_product_body: Mastodon ei ole kaupallinen verkosto. Ei mainoksia, ei tiedonlouhintaa, ei suljettuja protokollia. Mastodonissa ei ole keskusjohtoa.
       not_a_product_title: Olet henkilö, et tuote
-      real_conversation_title: Rakennettu oikealle keskustelulle
-      within_reach_body: Kehittäjäystävällisen rajapintaekosysteemin ansiosta useita appeja Androidille, iOS:lle ja muille alustoille, jotka mahdollistavat yhteydenpidon ystäviesi kanssa missä vain.
+      real_conversation_body: 'Voit ilmaista itseäsi niin kuin itse haluat: tilaa on 500 merkkiä, ja sisältövaroituksia voi tehdä monin tavoin.'
+      real_conversation_title: Tehty oikeaa keskustelua varten
+      within_reach_body: Rajapintoja on tarjolla moniin eri kehitysympäristöihin, minkä ansiosta iOS:lle, Androidille ja muille alustoille on saatavana useita eri sovelluksia. Näin voit pitää yhteyttä ystäviisi missä vain.
       within_reach_title: Aina lähellä
+    generic_description: "%{domain} on yksi verkostoon kuuluvista palvelimista"
+    hosted_on: Mastodon palvelimella %{domain}
     learn_more: Lisätietoja
     other_instances: Muut palvelimet
     source_code: Lähdekoodi
     status_count_after: statusta
-    status_count_before: Ovat luoneet
+    status_count_before: He ovat luoneet
     user_count_after: käyttäjälle
     user_count_before: Koti
     what_is_mastodon: Mikä on Mastodon?
@@ -33,161 +37,681 @@ fi:
     follow: Seuraa
     followers: Seuraajat
     following: Seuratut
+    media: Media
+    moved_html: "%{name} on muuttanut osoitteeseen %{new_profile_link}:"
     nothing_here: Täällä ei ole mitään!
-    people_followed_by: Henkilöitä joita %{name} seuraa
-    people_who_follow: Henkilöt jotka seuraa %{name}
-    posts: Postaukset
+    people_followed_by: Henkilöt, joita %{name} seuraa
+    people_who_follow: Käyttäjän %{name} seuraajat
+    posts: Tuuttaukset
+    posts_with_replies: Tuuttaukset ja vastaukset
     remote_follow: Etäseuranta
     reserved_username: Käyttäjänimi on varattu
     roles:
       admin: Ylläpitäjä
+      moderator: Moderaattori
     unfollow: Lopeta seuraaminen
   admin:
     account_moderation_notes:
       account: Moderaattori
       create: Luo
       created_at: Päiväys
-      created_msg: Moderointimerkintä luotu onnistuneesti!
+      created_msg: Moderointimerkinnän luonti onnistui!
       delete: Poista
-      destroyed_msg: Moderointimerkintä tuhottu onnistuneesti!
+      destroyed_msg: Moderointimerkinnän poisto onnistui!
     accounts:
       are_you_sure: Oletko varma?
-      confirm: Hyväksy
-      confirmed: Hyväksytty
+      by_domain: Verkko-osoite
+      confirm: Vahvista
+      confirmed: Vahvistettu
+      demote: Alenna
       disable: Poista käytöstä
       disable_two_factor_authentication: Poista 2FA käytöstä
       disabled: Poistettu käytöstä
+      display_name: Näyttönimi
+      domain: Verkko-osoite
       edit: Muokkaa
       email: Sähköposti
+      enable: Ota käyttöön
+      enabled: Käytössä
+      feed_url: Syötteen URL
       followers: Seuraajat
-      followers_url: Seuraajat URL
+      followers_url: Seuraajien URL
+      follows: Seuraa
+      inbox_url: Saapuvan postilaatikon URL
+      ip: IP
+      location:
+        all: Kaikki
+        local: Paikalliset
+        remote: Etätilit
+        title: Sijainti
+      login_status: Sisäänkirjautumisen tila
+      media_attachments: Medialiitteet
+      memorialize: Muuta muistosivuksi
+      moderation:
+        all: Kaikki
+        silenced: Hiljennetty
+        suspended: Jäähyllä
+        title: Moderointi
+      moderation_notes: Moderointimerkinnät
+      most_recent_activity: Viimeisin toiminta
+      most_recent_ip: Viimeisin IP
+      not_subscribed: Ei tilaaja
+      order:
+        alphabetic: Aakkosjärjestys
+        most_recent: Uusin
+        title: Järjestys
+      outbox_url: Lähtevän postilaatikon URL
+      perform_full_suspension: Siirrä kokonaan jäähylle
+      profile_url: Profiilin URL
+      promote: Ylennä
+      protocol: Protokolla
+      public: Julkinen
+      push_subscription_expires: PuSH-tilaus vanhenee
+      redownload: Päivitä profiilikuva
+      reset: Palauta
+      reset_password: Palauta salasana
+      resubscribe: Tilaa uudelleen
+      role: Oikeudet
+      roles:
+        admin: Ylläpitäjä
+        moderator: Moderaattori
+        staff: Henkilöstö
+        user: Käyttäjä
+      salmon_url: Salmon-URL
+      search: Haku
+      shared_inbox_url: Jaetun saapuvan postilaatikon URL
+      show:
+        created_reports: Tilin luomat raportit
+        report: raportti
+        targeted_reports: Tästä tilistä tehdyt raportit
+      silence: Hiljennä
+      statuses: Tilat
+      subscribe: Tilaa
+      title: Tilit
+      undo_silenced: Peru hiljennys
+      undo_suspension: Peru jäähy
+      unsubscribe: Lopeta tilaus
+      username: Käyttäjänimi
+      web: Web
+    action_logs:
+      actions:
+        confirm_user: "%{name} vahvisti käyttäjän %{target} sähköpostiosoitteen"
+        create_custom_emoji: "%{name} lähetti uuden emojin %{target}"
+        create_domain_block: "%{name} esti verkkotunnuksen %{target}"
+        create_email_domain_block: "%{name} lisäsi sähköpostiverkkotunnuksen %{target} estolistalle"
+        demote_user: "%{name} alensi käyttäjän %{target}"
+        destroy_domain_block: "%{name} poisti verkkotunnuksen %{target} eston"
+        destroy_email_domain_block: "%{name} lisäsi sähköpostiverkkotunnuksen %{target} sallittujen listalle"
+        destroy_status: "%{name} poisti käyttäjän %{target} tilan"
+        disable_2fa_user: "%{name} poisti käyttäjältä %{target} kaksivaiheisen todentamisen vaatimuksen"
+        disable_custom_emoji: "%{name} poisti emojin %{target} käytöstä"
+        disable_user: "%{name} poisti sisäänkirjautumisen käytöstä käyttäjältä %{target}"
+        enable_custom_emoji: "%{name} salli emojin %{target} käyttöön"
+        enable_user: "%{name} salli sisäänkirjautumisen käyttäjälle %{target}"
+        memorialize_account: "%{name} muutti käyttäjän %{target} tilin muistosivuksi"
+        promote_user: "%{name} ylensi käyttäjän %{target}"
+        reset_password_user: "%{name} palautti käyttäjän %{target} salasanan"
+        resolve_report: "%{name} hylkäsi raportin %{target}"
+        silence_account: "%{name} hiljensi käyttäjän %{target}"
+        suspend_account: "%{name} siirsi käyttäjän %{target} jäähylle"
+        unsilence_account: "%{name} poisti käyttäjän %{target} hiljennyksen"
+        unsuspend_account: "%{name} perui käyttäjän %{target} jäähyn"
+        update_custom_emoji: "%{name} päivitti emojin %{target}"
+        update_status: "%{name} päivitti käyttäjän %{target} tilan"
+      title: Auditointiloki
+    custom_emojis:
+      by_domain: Verkkotunnus
+      copied_msg: Emojin paikallisen kopion luonti onnistui
+      copy: Kopioi
+      copy_failed_msg: Emojista ei voitu tehdä paikallista kopiota
+      created_msg: Emojin luonti onnistui!
+      delete: Poista
+      destroyed_msg: Emojon poisto onnistui!
+      disable: Poista käytöstä
+      disabled_msg: Emojin käytöstäpoisto onnistui
+      emoji: Emoji
+      enable: Ota käyttöön
+      enabled_msg: Emojin käyttöönotto onnistui
+      image_hint: PNG enintään 50 kt
+      listed: Listassa
+      new:
+        title: Lisää uusi mukautettu emoji
+      overwrite: Kirjoita yli
+      shortcode: Lyhennekoodi
+      shortcode_hint: Vähintään kaksi merkkiä, vain kirjaimia, numeroita ja alaviivoja
+      title: Mukautetut emojit
+      unlisted: Ei listassa
+      update_failed_msg: Emojin päivitys epäonnistui
+      updated_msg: Emojin päivitys onnistui!
+      upload: Lähetä
+    domain_blocks:
+      add_new: Lisää uusi
+      created_msg: Verkkotunnuksen estoa käsitellään
+      destroyed_msg: Verkkotunnuksen esto on peruttu
+      domain: Verkkotunnus
+      new:
+        create: Luo esto
+        hint: Verkkotunnuksen esto ei estä tilien luomista ja lisäämistä tietokantaan, mutta se soveltaa näihin tileihin automaattisesti määrättyjä moderointitoimia tilin luomisen jälkeen.
+        severity:
+          desc_html: "<strong>Hiljennys</strong> estää tilin julkaisuja näkymästä muille kuin tilin seuraajille. <strong>Jäähy</strong> poistaa tilin kaiken sisällön, median ja profiilitiedot. Jos haluat vain hylätä mediatiedostot, valitse <strong>Ei mitään</strong>."
+          noop: Ei mitään
+          silence: Hiljennys
+          suspend: Jäähy
+        title: Uusi verkkotunnuksen esto
+      reject_media: Hylkää mediatiedostot
+      reject_media_hint: Poistaa paikallisesti tallennetut mediatiedostot eikä lataa niitä enää jatkossa. Ei merkitystä jäähyn kohdalla
+      severities:
+        noop: Ei mitään
+        silence: Hiljennys
+        suspend: Jäähy
+      severity: Vakavuus
+      show:
+        affected_accounts:
+          one: Vaikuttaa yhteen tiliin tietokannassa
+          other: Vaikuttaa %{count} tiliin tietokannassa
+        retroactive:
+          silence: Peru kaikkien tässä verkkotunnuksessa jo olemassa olevien tilien hiljennys
+          suspend: Peru kaikkien tässä verkkotunnuksessa jo olemassa olevien tilien jäähy
+        title: Peru verkkotunnuksen %{domain} esto
+        undo: Peru
+      title: Verkkotunnusten estot
+      undo: Peru
+    email_domain_blocks:
+      add_new: Lisää uusi
+      created_msg: Sähköpostiverkkotunnuksen lisäys estolistalle onnistui
+      delete: Poista
+      destroyed_msg: Sähköpostiverkkotunnuksen poisto estolistalta onnistui
+      domain: Verkkotunnus
+      new:
+        create: Lisää verkkotunnus
+        title: Uusi sähköpostiestolistan merkintä
+      title: Sähköpostiestolista
+    instances:
+      account_count: Tiedossa olevat tilit
+      domain_name: Verkkotunnus
+      reset: Palauta
+      search: Hae
+      title: Tiedossa olevat instanssit
+    invites:
+      filter:
+        all: Kaikki
+        available: Saatavilla
+        expired: Vanhentunut
+        title: Suodata
+      title: Kutsut
+    reports:
+      action_taken_by: Toimenpiteen tekijä
+      are_you_sure: Oletko varma?
+      comment:
+        label: Kommentti
+        none: Ei mitään
+      delete: Poista
+      id: Tunniste
+      mark_as_resolved: Merkitse ratkaistuksi
+      nsfw:
+        'false': Peru medialiitteiden piilotus
+        'true': Piilota medialiitteet
+      report: Raportti nro %{id}
+      report_contents: Sisältö
+      reported_account: Raportoitu tili
+      reported_by: Raportoija
+      resolved: Ratkaistut
+      silence_account: Hiljennä tili
+      status: Tila
+      suspend_account: Siirrä tili jäähylle
+      target: Kohde
+      title: Raportit
+      unresolved: Ratkaisemattomat
+      view: Näytä
+    settings:
+      activity_api_enabled:
+        desc_html: Paikallisesti julkaistujen tilojen, aktiivisten käyttäjien ja uusien rekisteröintien määrät viikoittain
+        title: Julkaise koostetilastoja käyttäjien aktiivisuudesta
+      bootstrap_timeline_accounts:
+        desc_html: Erota käyttäjänimet pilkulla. Vain paikalliset ja lukitsemattomat tilit toimivat. Jos kenttä jätetään tyhjäksi, oletusarvona ovat kaikki paikalliset ylläpitäjät.
+        title: Uudet käyttäjät seuraavat oletuksena seuraavia tilejä
+      contact_information:
+        email: Työsähköposti
+        username: Yhteyshenkilön käyttäjänimi
+      hero:
+        desc_html: Näytetään etusivulla. Suosituskoko vähintään 600x100 pikseliä. Jos kuvaa ei aseteta, käytetään instanssin pikkukuvaa
+        title: Sankarin kuva
+      peers_api_enabled:
+        desc_html: Verkkotunnukset, jotka tämä instanssi on kohdannut fediversumissa
+        title: Julkaise löydettyjen instanssien luettelo
+      registrations:
+        closed_message:
+          desc_html: Näytetään etusivulla, kun rekisteröinti on suljettu. HTML-tagit käytössä
+          title: Viesti, kun rekisteröinti on suljettu
+        deletion:
+          desc_html: Salli jokaisen poistaa oma tilinsä
+          title: Avoin tilin poisto
+        min_invite_role:
+          disabled: Ei kukaan
+          title: Salli kutsut käyttäjältä
+        open:
+          desc_html: Salli kenen tahansa luoda tili
+          title: Avoin rekisteröinti
+      show_known_fediverse_at_about_page:
+        desc_html: Kun tämä on valittu, esikatselussa näytetään tuuttaukset kaikkialta tunnetusta fediversumista. Muutoin näytetään vain paikalliset tuuttaukset.
+        title: Näytä aikajanan esikatselussa koko tunnettu fediversumi
+      show_staff_badge:
+        desc_html: Näytä käyttäjäsivulla henkilöstömerkki
+        title: Näytä henkilöstömerkki
+      site_description:
+        desc_html: Esittelykappale etusivulla ja metatunnisteissa. HTML-tagit käytössä, tärkeimmät ovat <code>&lt;a&gt;</code> ja <code>&lt;em&gt;</code>.
+        title: Instanssin kuvaus
+      site_description_extended:
+        desc_html: Hyvä paikka käytösohjeille, säännöille, ohjeistuksille ja muille instanssin muista erottaville asioille. HTML-tagit käytössä
+        title: Omavalintaiset laajat tiedot
+      site_terms:
+        desc_html: Tähän voi kirjoittaa instanssin tietosuojakäytännöstä, käyttöehdoista ja sen sellaisista asioista. HTML-tagit käytössä
+        title: Omavalintaiset käyttöehdot
+      site_title: Instanssin nimi
+      thumbnail:
+        desc_html: Käytetään esikatseluissa OpenGraphin ja API:n kautta. Suosituskoko 1200x630 pikseliä
+        title: Instanssin pikkukuva
+      timeline_preview:
+        desc_html: Näytä julkinen aikajana aloitussivulla
+        title: Aikajanan esikatselu
+      title: Sivuston asetukset
+    statuses:
+      back_to_account: Takaisin tilin sivulle
+      batch:
+        delete: Poista
+        nsfw_off: NSFW POIS
+        nsfw_on: NSFW PÄÄLLÄ
+      execute: Suorita
+      failed_to_execute: Suoritus epäonnistui
+      media:
+        hide: Piilota media
+        show: Näytä media
+        title: Media
+      no_media: Ei mediaa
+      title: Tilin tilat
+      with_media: Sisältää mediaa
+    subscriptions:
+      callback_url: Paluu-URL
+      confirmed: Vahvistettu
+      expires_in: Vanhenee
+      last_delivery: Viimeisin toimitus
+      title: WebSub
+      topic: Aihe
+    title: Ylläpito
+  admin_mailer:
+    new_report:
+      body: "%{reporter} on raportoinut kohteen %{target}"
+      subject: Uusi raportti instanssista %{instance} (nro %{id})
   application_mailer:
-    settings: 'Muokkaa sähköpostiasetuksia: %{link}'
-    view: 'Katso:'
+    notification_preferences: Muuta sähköpostiasetuksia
+    salutation: "%{name},"
+    settings: 'Muuta sähköpostiasetuksia: %{link}'
+    view: 'Näytä:'
+    view_profile: Näytä profiili
+    view_status: Näytä tila
   applications:
-    invalid_url: Annettu URL on väärä
+    created: Sovelluksen luonti onnistui
+    destroyed: Sovelluksen poisto onnistui
+    invalid_url: Annettu URL on virheellinen
+    regenerate_token: Luo pääsytunnus uudelleen
+    token_regenerated: Pääsytunnuksen uudelleenluonti onnistui
+    warning: Säilytä tietoa hyvin. Älä milloinkaan jaa sitä muille!
+    your_token: Pääsytunnus
   auth:
-    didnt_get_confirmation: Etkö saanut varmennusohjeita?
+    agreement_html: Rekisteröityessäsi sitoudut noudattamaan <a href="%{rules_path}">instanssin sääntöjä</a> ja <a href="%{terms_path}">käyttöehtoja</a>.
+    change_password: Salasana
+    confirm_email: Vahvista sähköpostiosoite
+    delete_account: Poista tili
+    delete_account_html: Jos haluat poistaa tilisi, <a href="%{path}">paina tästä</a>. Poisto on vahvistettava.
+    didnt_get_confirmation: Etkö saanut vahvistusohjeita?
     forgot_password: Unohditko salasanasi?
+    invalid_reset_password_token: Salasananpalautustunnus on virheellinen tai vanhentunut. Pyydä uusi.
     login: Kirjaudu sisään
     logout: Kirjaudu ulos
+    migrate_account: Muuta toiseen tiliin
+    migrate_account_html: Jos haluat ohjata tämän tilin toiseen tiliin, voit <a href="%{path}">asettaa toisen tilin tästä</a>.
+    or: tai
+    or_log_in_with: Tai käytä kirjautumiseen
+    providers:
+      cas: CAS
+      saml: SAML
     register: Rekisteröidy
-    resend_confirmation: Lähetä varmennusohjeet uudestaan
+    register_elsewhere: Rekisteröidy toiselle palvelimelle
+    resend_confirmation: Lähetä vahvistusohjeet uudestaan
     reset_password: Palauta salasana
     security: Tunnukset
     set_new_password: Aseta uusi salasana
   authorize_follow:
-    error: Valitettavasti tapahtui virhe etätilin haussa.
+    error: Valitettavasti etätilin haussa tapahtui virhe
     follow: Seuraa
-    title: Seuraa %{acct}
+    follow_request: 'Olet lähettänyt seuraamispyynnön käyttäjälle:'
+    following: 'Onnistui! Seuraat käyttäjää:'
+    post_follow:
+      close: Tai voit sulkea tämän ikkunan.
+      return: Palaa käyttäjän profiiliin
+      web: Siirry verkkosivulle
+    title: Seuraa käyttäjää %{acct}
   datetime:
     distance_in_words:
-      about_x_hours: "%{count}t"
-      about_x_months: "%{count}kk"
-      about_x_years: "%{count}v"
-      almost_x_years: "%{count}v"
-      half_a_minute: Juuri nyt
-      less_than_x_minutes: "%{count}m"
-      less_than_x_seconds: Juuri nyt
-      over_x_years: "%{count}v"
-      x_days: "%{count}pv"
-      x_minutes: "%{count}m"
-      x_months: "%{count}kk"
-      x_seconds: "%{count}s"
+      about_x_hours: "%{count} h"
+      about_x_months: "%{count} kk"
+      about_x_years: "%{count} v"
+      almost_x_years: "%{count} v"
+      half_a_minute: Nyt
+      less_than_x_minutes: "%{count} m"
+      less_than_x_seconds: Nyt
+      over_x_years: "%{count} v"
+      x_days: "%{count} pv"
+      x_minutes: "%{count} m"
+      x_months: "%{count} kk"
+      x_seconds: "%{count} s"
+  deletes:
+    bad_password_msg: Hyvä yritys, hakkerit! Väärä salasana
+    confirm_password: Tunnistaudu syöttämällä nykyinen salasanasi
+    description_html: Tämä poistaa <strong>pysyvästi ja peruuttamattomasti</strong> kaiken tilisi sisällön ja poistaa tilin käytöstä. Käyttäjänimesi pysyy varattuna, jotta identiteettiäsi ei myöhemmin varasteta.
+    proceed: Poista tili
+    success_msg: Tilin poisto onnistui
+    warning_html: Sisällön poistaminen taataan vain tämän instanssin osalta. Jos sisältöä on jaettu paljon, siitä todennäköisesti jää jälkiä. Palvelimet, joihin ei saada yhteyttä tai jotka ovat lopettaneet päivitystesi tilaamisen, eivät päivitä tietokantojaan.
+    warning_title: Sisällön saatavuustieto levitetty
+  errors:
+    '403': Sinulla ei ole lupaa nähdä tätä sivua.
+    '404': Etsimääsi sivua ei ole olemassa.
+    '410': Etsimääsi sivua ei ole enää olemassa.
+    '422':
+      content: Turvallisuusvahvistus epäonnistui. Oletko estänyt evästeet?
+      title: Turvallisuusvahvistus epäonnistui
+    '429': Rajoitettu
+    '500':
+      content: Valitettavasti jokin meni pieleen meidän päässämme.
+      title: Sivu ei ole oikein
+    noscript_html: Mastodon-selainsovelluksen käyttöön vaaditaan JavaScript. Voit vaihtoehtoisesti kokeilla jotakin omalle käyttöjärjestelmällesi tehtyä Mastodon<a href="https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md">sovellusta</a>.
   exports:
-    blocks: Estosi
+    archive_takeout:
+      date: Päiväys
+      download: Lataa arkisto
+      hint_html: Voit pyytää arkistoa omista <strong>tuuttauksistasi ja mediastasi</strong>. Vientitiedot ovat ActivityPub-muodossa, ja ne voi lukea millä tahansa yhteensopivalla ohjelmalla.
+      in_progress: Arkistoa kootaan...
+      request: Pyydä arkisto
+      size: Koko
+    blocks: Estot
     csv: CSV
     follows: Seurattavat
-    storage: Mediasi
+    mutes: Mykistetyt
+    storage: Media-arkisto
+  followers:
+    domain: Verkkotunnus
+    explanation_html: Jos haluat olla varma tilapäivitystesi yksityisyydestä, sinun täytyy tietää, ketkä seuraavat sinua. <strong>Yksityiset tilapäivityksesi lähetetään kaikkiin niihin instansseihin, joissa sinulla on seuraajia</strong>. Jos et luota siihen, että näiden instanssien ylläpitäjät tai ohjelmisto kunnioittavat yksityisyyttäsi, käy läpi seuraajaluettelosi ja poista tarvittaessa käyttäjiä.
+    followers_count: Seuraajien määrä
+    lock_link: Lukitse tili
+    purge: Poista seuraajista
+    success:
+      one: Estetään kevyesti seuraajia yhdestä verkkotunnuksesta...
+      other: Estetään kevyesti seuraajia %{count} verkkotunnuksesta...
+    true_privacy_html: Muista, että <strong>kunnollinen yksityisyys voidaan varmistaa vain päästä päähän -salauksella</strong>.
+    unlocked_warning_html: Kuka tahansa voi seurata sinua ja nähdä saman tien yksityiset tilapäivityksesi. %{lock_link}, niin voit tarkastaa ja torjua seuraajia.
+    unlocked_warning_title: Tiliäsi ei ole lukittu
   generic:
-    changes_saved_msg: Muutokset onnistuneesti tallennettu!
-    powered_by: powered by %{link}
+    changes_saved_msg: Muutosten tallennus onnistui!
+    powered_by: voimanlähteenä %{link}
     save_changes: Tallenna muutokset
     validation_errors:
-      one: Jokin ei ole viellä oikein! Katso virhe alapuolelta.
-      other: Jokin ei ole viellä oikein! Katso %{count} virhettä alapuolelta.
+      one: Kaikki ei ole aivan oikein! Tarkasta alla oleva virhe
+      other: Kaikki ei ole aivan oikein! Tarkasta alla olevat %{count} virhettä
   imports:
-    preface: Voit tuoda tiettyä dataa kaikista ihmisistä joita seuraat tai estät tilillesi tälle palvelimelle tiedostoista, jotka on luotu toisella palvelimella.
-    success: Datasi on onnistuneesti ladattu ja käsitellään pian
+    preface: Voit tuoda toisesta instanssista viemiäsi tietoja, kuten esimerkiksi seuraamiesi tai estämiesi henkilöiden listan.
+    success: Tietojen lähettäminen onnistui, ja ne käsitellään kohtapuoliin
     types:
-      blocking: Estetyt lista
-      following: Seuratut lista
+      blocking: Estettyjen lista
+      following: Seurattujen lista
+      muting: Mykistettyjen lista
     upload: Lähetä
-  landing_strip_html: "<strong>%{name}</strong> on käyttäjä domainilla %{link_to_root_path}. Voit seurata tai vuorovaikuttaa heidän kanssaan jos sinulla on tili yleisessä verkossa."
-  landing_strip_signup_html: Jos sinulla ei ole tiliä, voit <a href="%{sign_up_path}">rekisteröityä täällä</a>.
+  in_memoriam_html: Muistoissamme.
+  invites:
+    delete: Poista käytöstä
+    expired: Vanhentunut
+    expires_in:
+      '1800': 30 minuuttia
+      '21600': 6 tuntia
+      '3600': 1 tunti
+      '43200': 12 tuntia
+      '86400': 1 vuorokausi
+    expires_in_prompt: Ei koskaan
+    generate: Luo
+    max_uses:
+      one: kertakäyttöinen
+      other: "%{count} käyttökertaa"
+    max_uses_prompt: Ei rajoitusta
+    prompt: Luo linkkejä ja jaa niiden avulla muille pääsyoikeus tähän instanssiin
+    table:
+      expires_at: Vanhenee
+      uses: Käytetty
+    title: Kutsu ihmisiä
+  landing_strip_html: "<strong>%{name}</strong> on käyttäjänä palvelimella %{link_to_root_path}. Voit seurata heitä tai pitää heihin yhteyttä, jos sinulla on tili missä tahansa fediversumin kolkassa."
+  landing_strip_signup_html: Jos sinulla ei ole tiliä, voit <a href="%{sign_up_path}">rekisteröityä tätä kautta</a>.
+  lists:
+    errors:
+      limit: Sinulla on jo suurin sallittu määrä listoja
+  media_attachments:
+    validations:
+      images_and_video: Videota ei voi liittää tilapäivitykseen, jossa on jo kuvia
+      too_many: Tiedostoja voi liittää enintään 4
+  migrations:
+    acct: uuden tilin käyttäjätunnus@verkkotunnus
+    currently_redirecting: 'Profiiliisi on asetettu uudelleenohjaus:'
+    proceed: Tallenna
+    updated_msg: Tilinsiirtoasetusten päivitys onnistui!
+  moderation:
+    title: Moderointi
   notification_mailer:
     digest:
-      body: 'Tässä on pieni yhteenveto palvelimelta %{instance} viimeksi kun olit paikalla %{since}:'
+      action: Näytä kaikki ilmoitukset
+      body: Tässä lyhyt yhteenveto viime käyntisi (%{since}) jälkeen tulleista viesteistä
       mention: "%{name} mainitsi sinut:"
       new_followers_summary:
-        one: Olet myös saanut yhden uuden seuraajan poissaollessasi! Jee!
-        other: Olet saanut %{count} uutta seuraajaa poissaollessasi! Loistavaa!
+        one: Olet myös saanut yhden uuden seuraajan! Juhuu!
+        other: Olet myös saanut %{count} uutta seuraajaa! Aivan mahtavaa!
       subject:
-        one: "1 uusi ilmoitus viimeisen käyntisi jälkeen \U0001F418"
-        other: "%{count} uutta ilmoitusta viimeisen käyntisi jälkeen \U0001F418"
+        one: "1 uusi ilmoitus viime käyntisi jälkeen \U0001F418"
+        other: "%{count} uutta ilmoitusta viime käyntisi jälkeen \U0001F418"
+      title: Poissaollessasi…
     favourite:
-      body: 'Statuksestasi tykkäsi %{name}:'
-      subject: "%{name} tykkäsi sinun statuksestasi"
+      body: "%{name} tykkäsi tilastasi:"
+      subject: "%{name} tykkäsi tilastasi"
+      title: Uusi tykkäys
     follow:
       body: "%{name} seuraa nyt sinua!"
       subject: "%{name} seuraa nyt sinua"
+      title: Uusi seuraaja
     follow_request:
-      body: "%{name} on pyytänyt seurata sinua"
-      subject: 'Odottava seuraus pyyntö: %{name}'
+      action: Hallinnoi seuraamispyyntöjä
+      body: "%{name} haluaa seurata sinua"
+      subject: 'Odottava seuraamispyyntö: %{name}'
+      title: Uusi seuraamispyyntö
     mention:
-      body: 'Sinut mainitsi %{name} postauksessa:'
-      subject: Sinut mainitsi %{name}
+      action: Vastaa
+      body: "%{name} mainitsi sinut:"
+      subject: "%{name} mainitsi sinut"
+      title: Uusi maininta
     reblog:
-      body: 'Sinun statustasi boostasi %{name}:'
-      subject: "%{name} boostasi statustasi"
+      body: "%{name} buustasi tilaasi:"
+      subject: "%{name} boostasi tilaasi"
+      title: Uusi buustaus
   number:
     human:
       decimal_units:
-        format: "%n%u"
+        format: "%n %u"
         units:
-          billion: B
+          billion: Mrd
           million: M
-          quadrillion: Q
-          thousand: K
-          trillion: T
+          quadrillion: Brd
+          thousand: k
+          trillion: B
           unit: ''
   pagination:
+    newer: Uudemmat
     next: Seuraava
+    older: Vanhemmat
     prev: Edellinen
+    truncate: "&hellip;"
+  preferences:
+    languages: Kielet
+    other: Muut
+    publishing: Julkaiseminen
+    web: Web
+  push_notifications:
+    favourite:
+      title: "%{name} tykkäsi tilastasi"
+    follow:
+      title: "%{name} seuraa nyt sinua"
+    group:
+      title: "%{count} ilmoitusta"
+    mention:
+      action_boost: Buustaa
+      action_expand: Näytä lisää
+      action_favourite: Tykkää
+      title: "%{nimi} mainitsi sinut"
+    reblog:
+      title: "%{name} buustasi tilaasi"
   remote_follow:
-    acct: Syötä sinun käyttäjänimesi@domain jos haluat seurata palvelimelta
-    missing_resource: Ei löydetty tarvittavaa uudelleenohjaavaa URL-linkkiä tilillesi
-    proceed: Siirry seuraamiseen
-    prompt: 'Sinä aiot seurata:'
+    acct: Syötä se käyttäjätunnus@verkkotunnus, josta haluat seurata
+    missing_resource: Vaadittavaa uudelleenohjaus-URL:ää tiliisi ei löytynyt
+    proceed: Siirry seuraamaan
+    prompt: 'Olet aikeissa seurata:'
+  sessions:
+    activity: Viimeisin toiminta
+    browser: Selain
+    browsers:
+      alipay: Alipay
+      blackberry: Blackberry
+      chrome: Chrome
+      edge: Microsoft Edge
+      electron: Electron
+      firefox: Firefox
+      generic: Tuntematon selain
+      ie: Internet Explorer
+      micro_messenger: MicroMessenger
+      nokia: Nokia S40 Ovi -selain
+      opera: Opera
+      otter: Otter
+      phantom_js: PhantomJS
+      qq: QQ Browser
+      safari: Safari
+      uc_browser: UCBrowser
+      weibo: Weibo
+    current_session: Nykyinen istunto
+    description: "%{selain}, %{platform}"
+    explanation: Nämä verkkoselaimet ovat tällä hetkellä kirjautuneet Mastodon-tilillesi.
+    ip: IP
+    platforms:
+      adobe_air: Adobe Air
+      android: Android
+      blackberry: Blackberry
+      chrome_os: ChromeOS
+      firefox_os: Firefox OS
+      ios: iOS
+      linux: Linux
+      mac: Mac
+      other: tuntematon järjestelmä
+      windows: Windows
+      windows_mobile: Windows Mobile
+      windows_phone: Windows Phone
+    revoke: Hylkää
+    revoke_success: Istunnon hylkäys onnistui
+    title: Istunnot
   settings:
-    authorized_apps: Valtuutetut ohjelmat
+    authorized_apps: Valtuutetut sovellukset
     back: Takaisin Mastodoniin
+    delete: Tilin poisto
+    development: Kehittäminen
     edit_profile: Muokkaa profiilia
-    export: Vie dataa
-    import: Tuo dataa
+    export: Vie tietoja
+    followers: Valtuutetut seuraajat
+    import: Tuo
+    migrate: Tilin muutto muualle
+    notifications: Ilmoitukset
     preferences: Ominaisuudet
     settings: Asetukset
-    two_factor_authentication: Kaksivaiheinen tunnistus
+    two_factor_authentication: Kaksivaiheinen todentaminen
+    your_apps: Omat sovellukset
   statuses:
-    open_in_web: Avaa webissä
-    over_character_limit: sallittu kirjanmäärä %{max} ylitetty
+    attached:
+      description: 'Liitetty: %{attached}'
+      image:
+        one: "%{count} kuva"
+        other: "%{count} kuvaa"
+      video:
+        one: "%{count} video"
+        other: "%{count} videota"
+    content_warning: 'Sisältövaroitus: %{warning}'
+    open_in_web: Avaa selaimessa
+    over_character_limit: merkkimäärän rajoitus %{max} ylitetty
+    pin_errors:
+      limit: Olet jo kiinnittänyt suurimman mahdollisen määrän tuuttauksia
+      ownership: Muiden tuuttauksia ei voi kiinnittää
+      private: Piilotettua tuuttausta ei voi kiinnittää
+      reblog: Buustausta ei voi kiinnittää
     show_more: Näytä lisää
+    title: "%{name}: ”%{quote}”"
     visibilities:
-      private: Näytä vain seuraajille
+      private: Vain seuraajille
+      private_long: Näytä vain seuraajille
       public: Julkinen
-      unlisted: Julkinen, mutta älä näytä julkisella aikajanalla
+      public_long: Kaikki voivat nähdä
+      unlisted: Listaamaton julkinen
+      unlisted_long: Kaikki voivat nähdä, mutta ei näytetä julkisilla aikajanoilla
   stream_entries:
-    click_to_show: Klikkaa näyttääksesi
-    reblogged: boosted
-    sensitive_content: Herkkä materiaali
+    click_to_show: Katso napsauttamalla
+    pinned: Kiinnitetty tuuttaus
+    reblogged: buustasi
+    sensitive_content: Arkaluontoista sisältöä
+  terms:
+    title: "%{instance}, käyttöehdot ja tietosuojakäytäntö"
+  themes:
+    default: Mastodon
   time:
     formats:
-      default: "%b %d, %Y, %H:%M"
+      default: "%d.%m.%Y klo %H.%M"
   two_factor_authentication:
-    description_html: Jos otat käyttöön <strong>kaksivaiheisen tunnistuksen</strong>, kirjautumiseen vaaditaan puhelin, joka voi luoda tokeneita kirjautumista varten.
+    code_hint: Vahvista syöttämällä todentamissovelluksen generoima koodi
+    description_html: Jos otat käyttöön <strong>kaksivaiheisen todentamisen</strong>, kirjautumiseen vaaditaan puhelin, jolla voidaan luoda kirjautumistunnuksia.
     disable: Poista käytöstä
     enable: Ota käyttöön
-    instructions_html: "<strong>Skannaa tämä QR-koodi Google Authenticator- tai vastaavaan sovellukseen puhelimellasi</strong>. Tästä hetkestä lähtien ohjelma luo koodin, mikä sinun tarvitsee syöttää sisäänkirjautuessa."
+    enabled: Kaksivaiheinen todentaminen käytössä
+    enabled_success: Kaksivaiheisen todentamisen käyttöönotto onnistui
+    generate_recovery_codes: Luo palautuskoodit
+    instructions_html: "<strong>Lue tämä QR-koodi puhelimen Google Authenticator- tai vastaavalla TOTP-sovelluksella</strong>. Sen jälkeen sovellus luo tunnuksia, joita tarvitset sisäänkirjautuessasi."
+    lost_recovery_codes: Palautuskoodien avulla voit käyttää tiliä, jos menetät puhelimesi. Jos olet hukannut palautuskoodit, voit luoda uudet tästä. Vanhat palautuskoodit poistetaan käytöstä.
+    manual_instructions: 'Jos et voi lukea QR-koodia ja haluat syöttää sen käsin, tässä on salainen koodi tekstinä:'
+    recovery_codes: Varapalautuskoodit
+    recovery_codes_regenerated: Uusien palautuskoodien luonti onnistui
+    recovery_instructions_html: Jos menetät puhelimesi, voit kirjautua tilillesi jollakin alla olevista palautuskoodeista. <strong>Pidä palautuskoodit hyvässä tallessa</strong>. Voit esimerkiksi tulostaa ne ja säilyttää muiden tärkeiden papereiden joukossa.
+    setup: Ota käyttöön
+    wrong_code: Annettu koodi oli virheellinen! Ovatko palvelimen aika ja laitteen aika oikein?
+  user_mailer:
+    backup_ready:
+      explanation: Pyysit täydellistä varmuuskopiota Mastodon-tilistäsi. Voit nyt ladata sen!
+      subject: Arkisto on valmiina ladattavaksi
+      title: Arkiston tallennus
+    welcome:
+      edit_profile_action: Aseta profiili
+      edit_profile_step: Voit mukauttaa profiiliasi lataamalla profiilikuvan ja otsakekuvan, muuttamalla näyttönimeäsi ym. Jos haluat hyväksyä uudet seuraajat ennen kuin he voivat seurata sinua, voit lukita tilisi.
+      explanation: Näillä vinkeillä pääset alkuun
+      final_action: Ala julkaista
+      final_step: 'Ala julkaista! Vaikkei sinulla olisi seuraajia, monet voivat nähdä julkiset viestisi esimerkiksi paikallisella aikajanalla ja hashtagien avulla. Kannattaa esittäytyä! Käytä hashtagia #introductions. (Jos haluat esittäytyä myös suomeksi, se kannattaa tehdä erillisessä tuuttauksessa ja käyttää hashtagia #esittely.)'
+      full_handle: Koko käyttäjätunnuksesi
+      full_handle_hint: Kerro tämä ystävillesi, niin he voivat lähettää sinulle viestejä tai löytää sinut toisen instanssin kautta.
+      review_preferences_action: Muuta asetuksia
+      review_preferences_step: Käy tarkistamassa, että asetukset ovat haluamallasi tavalla. Voit valita, missä tilanteissa haluat saada sähköpostia, mikä on julkaisujesi oletusnäkyvyys jne. Jos et saa helposti pahoinvointia, voit valita, että GIF-animaatiot toistetaan automaattisesti.
+      subject: Tervetuloa Mastodoniin
+      tip_bridge_html: Jos tulet Twitteristä, voit etsiä ystäviäsi Mastodonista <a href="%{bridge_url}">siltasovelluksen</a> avulla. Se kuitenkin löytää heidät vain, jos hekin käyttävät sitä!
+      tip_federated_timeline: Yleinen aikajana näyttää sisältöä koko Mastodon-verkostosta. Siinä näkyvät kuitenkin vain ne henkilöt, joita oman instanssisi käyttäjät seuraavat. Siinä ei siis näytetä aivan kaikkea.
+      tip_following: Oletusarvoisesti seuraat oman palvelimesi ylläpitäjiä. Etsi lisää kiinnostavia ihmisiä paikalliselta ja yleiseltä aikajanalta.
+      tip_local_timeline: Paikallinen aikajana näyttää instanssin %{instance} käyttäjien julkaisut. He ovat naapureitasi!
+      tip_mobile_webapp: Jos voit lisätä Mastodonin mobiiliselaimen kautta aloitusnäytöllesi, voit vastaanottaa push-ilmoituksia. Toiminta vastaa monin tavoin tavanomaista sovellusta!
+      tips: Vinkkejä
+      title: Tervetuloa mukaan, %name}!
   users:
-    invalid_email: Virheellinen sähköposti
-    invalid_otp_token: Virheellinen kaksivaihetunnistuskoodi
+    invalid_email: Virheellinen sähköpostiosoite
+    invalid_otp_token: Virheellinen kaksivaiheisen todentamisen koodi
+    seamless_external_login: Olet kirjautunut ulkoisen palvelun kautta, joten salasana- ja sähköpostiasetukset eivät ole käytettävissä.
+    signed_in_as: 'Kirjautunut henkilönä:'
diff --git a/config/locales/ja.yml b/config/locales/ja.yml
index 5f5cb9b01..01fb9657f 100644
--- a/config/locales/ja.yml
+++ b/config/locales/ja.yml
@@ -4,6 +4,7 @@ ja:
     about_hashtag_html: ハッシュタグ <strong>#%{hashtag}</strong> の付いた公開トゥートです。どこでもいいので、連合に参加しているSNS上にアカウントを作れば会話に参加することができます。
     about_mastodon_html: Mastodon は、オープンなウェブプロトコルを採用した、自由でオープンソースなソーシャルネットワークです。電子メールのような分散型の仕組みを採っています。
     about_this: 詳細情報
+    administered_by: '管理者:'
     closed_registrations: 現在このインスタンスでの新規登録は受け付けていません。しかし、他のインスタンスにアカウントを作成しても全く同じネットワークに参加することができます。
     contact: 連絡先
     contact_missing: 未設定
@@ -17,7 +18,7 @@ ja:
     features:
       humane_approach_body: 他の SNS の失敗から学び、Mastodon はソーシャルメディアが誤った使い方をされることの無いように倫理的な設計を目指しています。
       humane_approach_title: より思いやりのある設計
-      not_a_product_body: Mastodon は営利的な SNS ではありません。広告や、データの収集・解析は無く、またユーザーの囲い込みもありません。
+      not_a_product_body: Mastodon は営利的な SNS ではありません。広告や、データの収集・解析は無く、またユーザーの囲い込みもありません。ここには中央権力はありません。
       not_a_product_title: あなたは人間であり、商品ではありません
       real_conversation_body: 好きなように書ける500文字までの投稿や、文章やメディアの内容に警告をつけられる機能で、思い通りに自分自身を表現することができます。
       real_conversation_title: 本当のコミュニケーションのために
@@ -62,6 +63,13 @@ ja:
       are_you_sure: 本当に実行しますか?
       avatar: アイコン
       by_domain: ドメイン
+      change_email:
+        changed_msg: メールアドレスの変更に成功しました!
+        current_email: 現在のメールアドレス
+        label: メールアドレスを変更
+        new_email: 新しいメールアドレス
+        submit: Change Email
+        title: "%{username} さんのメールアドレスを変更"
       confirm: 確認
       confirmed: 確認済み
       demote: 降格
@@ -71,7 +79,7 @@ ja:
       display_name: 表示名
       domain: ドメイン
       edit: 編集
-      email: E-mail
+      email: メールアドレス
       enable: 有効化
       enabled: 有効
       feed_url: フィードURL
@@ -130,6 +138,7 @@ ja:
       statuses: トゥート数
       subscribe: 購読する
       title: アカウント
+      unconfirmed_email: 確認待ちのメールアドレス
       undo_silenced: サイレンスから戻す
       undo_suspension: 停止から戻す
       unsubscribe: 購読の解除
@@ -138,6 +147,7 @@ ja:
     action_logs:
       actions:
         assigned_to_self_report: "%{name} さんがレポート %{target} を自身の担当に割り当てました"
+        change_email_user: "%{name} さんが %{target} さんのメールアドレスを変更しました"
         confirm_user: "%{name} さんが %{target} さんのメールアドレスを確認済みにしました"
         create_custom_emoji: "%{name} さんがカスタム絵文字 %{target} を追加しました"
         create_domain_block: "%{name} さんがドメイン %{target} をブロックしました"
@@ -246,8 +256,8 @@ ja:
         title: フィルター
       title: 招待
     report_notes:
-      created_msg: モデレーションメモを書き込みました!
-      destroyed_msg: モデレーションメモを削除しました!
+      created_msg: レポートメモを書き込みました!
+      destroyed_msg: レポートメモを削除しました!
     reports:
       action_taken_by: レポート処理者
       are_you_sure: 本当に実行しますか?
@@ -256,15 +266,20 @@ ja:
       comment:
         label: コメント
         none: なし
+      created_at: レポート日時
       delete: 削除
+      history: モデレーション履歴
       id: ID
       mark_as_resolved: 解決済みとしてマーク
       mark_as_unresolved: 未解決として再び開く
       notes:
         create: 書き込む
         create_and_resolve: 書き込み、解決済みにする
+        create_and_unresolve: 書き込み、未解決として開く
         delete: 削除
-        label: メモ
+        label: モデレーターメモ
+        new_label: モデレーターメモの追加
+        placeholder: このレポートに取られた措置やその他更新を記述してください
       nsfw:
         'false': NSFW オフ
         'true': NSFW オン
@@ -610,6 +625,10 @@ ja:
     missing_resource: リダイレクト先が見つかりませんでした
     proceed: フォローする
     prompt: 'フォローしようとしています:'
+  remote_unfollow:
+    error: エラー
+    title: タイトル
+    unfollowed: フォロー解除しました
   sessions:
     activity: 最後のアクティビティ
     browser: ブラウザ
@@ -700,6 +719,83 @@ ja:
     reblogged: さんがブースト
     sensitive_content: 閲覧注意
   terms:
+    body_html: |
+      <h2>プライバシーポリシー</h2>
+      <h3 id="collect">どのような情報を収集しますか?</h3>
+
+      <ul>
+        <li><em>基本的なアカウント情報</em>: 当サイトに登録すると、ユーザー名・メールアドレス・パスワードの入力を求められることがあります。また表示名や自己紹介・プロフィール画像・ヘッダー画像といった追加のプロフィールを登録できます。ユーザー名・表示名・自己紹介・プロフィール画像・ヘッダー画像は常に公開されます。</li>
+        <li><em>投稿・フォロー・その他公開情報</em>: フォローしているユーザーの一覧は一般公開されます。フォロワーも同様です。メッセージを投稿する際、日時だけでなく投稿に使用したアプリケーション名も記録されます。メッセージには写真や動画といった添付メディアを含むことがあります。「公開」や「未収載」の投稿は一般公開されます。プロフィールに投稿を載せるとそれもまた公開情報となります。投稿はフォロワーに配信されます。場合によっては他のサーバーに配信され、そこにコピーが保存されることを意味します。投稿を削除した場合も同様にフォロワーに配信されます。他の投稿をリブログやお気に入り登録する行動は常に公開されます。</li>
+        <li><em>「ダイレクト」と「非公開」投稿</em>: すべての投稿はサーバーに保存され、処理されます。「非公開」投稿はフォロワーと投稿に書かれたユーザーに配信されます。「ダイレクト」投稿は投稿に書かれたユーザーにのみ配信されます。場合によっては他のサーバーに配信され、そこにコピーが保存されることを意味します。私たちはこれらの閲覧を一部の許可された者に限定するよう誠意を持って努めます。しかし他のサーバーにおいても同様に扱われるとは限りません。したがって、相手の所属するサーバーを吟味することが重要です。設定で新しいフォロワーの承認または拒否を手動で行うよう切り替えることもできます。<em>サーバー管理者は「ダイレクト」や「非公開」投稿も閲覧する可能性があることを忘れないでください。</em>また受信者がスクリーンショットやコピー、もしくは共有する可能性があることを忘れないでください。<em>いかなる危険な情報もMastodon上で共有しないでください。</em></li>
+        <li><em>IPアドレスやその他メタデータ</em>: ログインする際IPアドレスだけでなくブラウザーアプリケーション名を記録します。ログインしたセッションはすべてユーザー設定で見直し、取り消すことができます。使用されている最新のIPアドレスは最大12ヵ月間保存されます。またサーバーへのIPアドレスを含むすべてのリクエストのログを保持することがあります。</li>
+      </ul>
+
+      <hr class="spacer" />
+
+      <h3 id="use">情報を何に使用しますか?</h3>
+
+      <p>収集した情報は次の用途に使用されることがあります:</p>
+
+      <ul>
+        <li>Mastodonのコア機能の提供: ログインしている間にかぎり他の人たちと投稿を通じて交流することができます。例えば自分専用のホームタイムラインで投稿をまとめて読むために他の人たちをフォローできます。</li>
+        <li>コミュニティ維持の補助: 例えばIPアドレスを既知のものと比較し、BAN回避目的の複数登録者やその他違反者を判別します。</li>
+        <li>提供されたメールアドレスはお知らせの送信・投稿に対するリアクションやメッセージ送信の通知・お問い合わせやその他要求や質問への返信に使用されることがあります。</li>
+      </ul>
+
+      <hr class="spacer" />
+
+      <h3 id="protect">情報をどのように保護しますか?</h3>
+
+      <p>私たちはあなたが入力・送信する際や自身の情報にアクセスする際に個人情報を安全に保つため、さまざまなセキュリティ上の対策を実施します。特にブラウザーセッションだけでなくアプリケーションとAPI間の通信もSSLによって保護されます。またパスワードは強力な不可逆アルゴリズムでハッシュ化されます。二段階認証を有効にし、アカウントへのアクセスをさらに安全にすることができます。</p>
+
+      <hr class="spacer" />
+
+      <h3 id="data-retention">データ保持方針はどうなっていますか?</h3>
+
+      <p>私たちは次のように誠意を持って努めます:</p>
+
+      <ul>
+        <li>当サイトへのIPアドレスを含むすべての要求に対するサーバーログを90日以内のできるかぎりの間保持します。</li>
+        <li>登録されたユーザーに関連付けられたIPアドレスを12ヵ月以内の間保持します。</li>
+      </ul>
+
+      <p>あなたは投稿・添付メディア・プロフィール画像・ヘッダー画像を含む自身のデータのアーカイブを要求し、ダウンロードすることができます。</p>
+
+      <p>あなたはいつでもアカウントの削除を要求できます。削除は取り消すことができません。</p>
+
+      <hr class="spacer"/>
+
+      <h3 id="cookies">クッキーを使用していますか?</h3>
+
+      <p>はい。クッキーは (あなたが許可した場合に) WebサイトやサービスがWebブラウザーを介してコンピューターに保存する小さなファイルです。使用することで Web サイトがブラウザーを識別し、登録済みのアカウントがある場合関連付けます。</p>
+
+      <p>私たちはクッキーを将来の訪問のために設定を保存し呼び出す用途に使用します。</p>
+
+      <hr class="spacer" />
+
+      <h3 id="disclose">なんらかの情報を外部に提供していますか?</h3>
+
+      <p>私たちは個人を特定できる情報を外部へ販売・取引・その他方法で渡すことはありません。これには当サイトの運営・業務遂行・サービス提供を行ううえで補助する信頼できる第三者をこの機密情報の保護に同意するかぎり含みません。法令の遵守やサイトポリシーの施行、権利・財産・安全の保護に適切と判断した場合、あなたの情報を公開することがあります。</p>
+
+      <p>あなたの公開情報はネットワーク上の他のサーバーにダウンロードされることがあります。相手が異なるサーバーに所属する場合、「公開」と「非公開」投稿はフォロワーの所属するサーバーに配信され、「ダイレクト」投稿は受信者の所属するサーバーに配信されます。</p>
+
+      <p>あなたがアカウントの使用をアプリケーションに許可すると、承認した権限の範囲内で公開プロフィール情報・フォローリスト・フォロワー・リスト・すべての投稿・お気に入り登録にアクセスできます。アプリケーションはメールアドレスやパスワードに決してアクセスできません。</p>
+
+      <hr class="spacer" />
+
+      <h3 id="coppa">児童オンラインプライバシー保護法の遵守</h3>
+
+      <p>当サイト・製品・サービスは13歳以上の人を対象としています。サーバーが米国にあり、あなたが13歳未満の場合、COPPA (<a href="https://en.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act">Children's Online Privacy Protection Act</a> - 児童オンラインプライバシー保護法) により当サイトを使用できません。</p>
+
+      <hr class="spacer" />
+
+      <h3 id="changes">プライバシーポリシーの変更</h3>
+
+      <p>プライバシーポリシーの変更を決定した場合、このページに変更点を掲載します。</p>
+
+      <p>この文章のライセンスはCC-BY-SAです。最終更新日は2018年3月7日です。</p>
+
+      <p>オリジナルの出典: <a href="https://github.com/discourse/discourse">Discourse privacy policy</a></p>
     title: "%{instance} 利用規約・プライバシーポリシー"
   themes:
     default: Mastodon
diff --git a/config/locales/pl.yml b/config/locales/pl.yml
index a71f48758..4fba2c0c1 100644
--- a/config/locales/pl.yml
+++ b/config/locales/pl.yml
@@ -63,6 +63,13 @@ pl:
       are_you_sure: Jesteś tego pewien?
       avatar: Awatar
       by_domain: Domena
+      change_email:
+        changed_msg: Pomyślnie zmieniono adres e-mail konta!
+        current_email: Obecny adres e-mail
+        label: Zmień adres e-mail
+        new_email: Nowy adres e-mail
+        submit: Zmień adres e-mail
+        title: Zmień adres e-mail dla %{username}
       confirm: Potwierdź
       confirmed: Potwierdzono
       demote: Degraduj
@@ -131,6 +138,7 @@ pl:
       statuses: Wpisy
       subscribe: Subskrybuj
       title: Konta
+      unconfirmed_email: Niepotwierdzony adres e-mail
       undo_silenced: Cofnij wyciszenie
       undo_suspension: Cofnij zawieszenie
       unsubscribe: Przestań subskrybować
@@ -139,6 +147,7 @@ pl:
     action_logs:
       actions:
         assigned_to_self_report: "%{name} przypisał sobie zgłoszenie %{target}"
+        change_email_user: "%{name} zmienił adres-email użytkownika %{target}"
         confirm_user: "%{name} potwierdził adres e-mail użytkownika %{target}"
         create_custom_emoji: "%{name} dodał nowe emoji %{target}"
         create_domain_block: "%{name} zablokował domenę %{target}"
@@ -251,6 +260,16 @@ pl:
       created_msg: Pomyslnie utworzono notatkę moderacyjną.
       destroyed_msg: Pomyślnie usunięto notatkę moderacyjną.
     reports:
+      account:
+        created_reports: Zgłoszenia utworzone z tego konta
+        moderation:
+          silenced: Wyciszone
+          suspended: Zawieszone
+          title: Moderacja
+        moderation_notes: Notatki moderacyjne
+        note: notatka
+        report: zgłoszenie
+        targeted_reports: Zgłoszenia dotycząće tego konta
       action_taken_by: Działanie podjęte przez
       are_you_sure: Czy na pewno?
       assign_to_self: Przypisz do siebie
@@ -258,18 +277,24 @@ pl:
       comment:
         label: Komentarz do zgłoszenia
         none: Brak
+      created_at: Zgłoszono
       delete: Usuń
+      history: Historia moderacji
       id: ID
       mark_as_resolved: Oznacz jako rozwiązane
       mark_as_unresolved: Oznacz jako nierozwiązane
       notes:
         create: Utwórz notatkę
         create_and_resolve: Rozwiąż i pozostaw notatkę
+        create_and_unresolve: Cofnij rozwiązanie i pozostaw notatkę
         delete: Usuń
         label: Notatki
+        new_label: Dodaj notatkę moderacyjną
+        placeholder: Opisz wykonane akcje i inne szczegóły dotyczące tego zgłoszenia…
       nsfw:
         'false': Nie oznaczaj jako NSFW
         'true': Oznaczaj jako NSFW
+      reopen: Otwórz ponownie
       report: 'Zgłoszenie #%{id}'
       report_contents: Zawartość
       reported_account: Zgłoszone konto
@@ -617,6 +642,10 @@ pl:
     missing_resource: Nie udało się znaleźć adresu przekierowania z Twojej domeny
     proceed: Śledź
     prompt: 'Zamierzasz śledzić:'
+  remote_unfollow:
+    error: Błąd
+    title: Tytuł
+    unfollowed: Przestałeś śledzić
   sessions:
     activity: Ostatnia aktywność
     browser: Przeglądarka
diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml
index 7ffa59f46..f635bf441 100644
--- a/config/locales/simple_form.en.yml
+++ b/config/locales/simple_form.en.yml
@@ -8,6 +8,7 @@ en:
         display_name:
           one: <span class="name-counter">1</span> character left
           other: <span class="name-counter">%{count}</span> characters left
+        fields: You can have up to 4 items displayed as a table on your profile
         header: PNG, GIF or JPG. At most 2MB. Will be downscaled to 700x335px
         locked: Requires you to manually approve followers
         note:
@@ -22,6 +23,10 @@ en:
       user:
         filtered_languages: Checked languages will be filtered from public timelines for you
     labels:
+      account:
+        fields:
+          name: Label
+          value: Content
       defaults:
         avatar: Avatar
         confirm_new_password: Confirm new password
@@ -31,6 +36,7 @@ en:
         display_name: Display name
         email: E-mail address
         expires_in: Expire after
+        fields: Profile metadata
         filtered_languages: Filtered languages
         header: Header
         locale: Language
diff --git a/config/locales/simple_form.eo.yml b/config/locales/simple_form.eo.yml
index 17862f916..41a0c26aa 100644
--- a/config/locales/simple_form.eo.yml
+++ b/config/locales/simple_form.eo.yml
@@ -14,7 +14,7 @@ eo:
           one: <span class="note-counter">1</span> signo restas
           other: <span class="note-counter">%{count}</span> signoj restas
         setting_noindex: Influas vian publikan profilon kaj mesaĝajn paĝojn
-        setting_theme: Influas kiel Mastodon aspektas kiam vi ensalutis en ajna aparato.
+        setting_theme: Influas kiel Mastodon aspektas post ensaluto de ajna aparato.
       imports:
         data: CSV-dosiero el alia nodo de Mastodon
       sessions:
@@ -45,7 +45,7 @@ eo:
         setting_default_privacy: Mesaĝa videbleco
         setting_default_sensitive: Ĉiam marki aŭdovidaĵojn tiklaj
         setting_delete_modal: Montri fenestron por konfirmi antaŭ ol forigi mesaĝon
-        setting_display_sensitive_media: Ĉiam montri aŭdovidaĵon markitajn tiklaj
+        setting_display_sensitive_media: Ĉiam montri aŭdovidaĵojn markitajn tiklaj
         setting_noindex: Ellistiĝi de retserĉila indeksado
         setting_reduce_motion: Malrapidigi animaciojn
         setting_system_font_ui: Uzi la dekomencan tiparon de la sistemo
diff --git a/config/locales/simple_form.fi.yml b/config/locales/simple_form.fi.yml
index 34605c4f6..f48e9ab23 100644
--- a/config/locales/simple_form.fi.yml
+++ b/config/locales/simple_form.fi.yml
@@ -3,64 +3,68 @@ fi:
   simple_form:
     hints:
       defaults:
-        avatar: PNG, GIF tai JPG. Korkeintaan 2MB. Skaalataan kokoon 400x400px
-        digest: Lähetetään vain pitkän poissaolon jälkeen, ja vain jos olet vastaanottanut yksityisviestejä poissaolosi aikana
-        display_name: Korkeintaan 30 merkkiä
-        header: PNG, GIF tai JPG. Korkeintaan 2MB. Skaalataan kokoon 700x335px
-        locked: Vaatii sinua manuaalisesti hyväksymään seuraajat
-        note: Korkeintaan 160 merkkiä
-        setting_noindex: Vaikuttaa julkiseen profiiliisi ja statuspäivityksiisi
-        setting_theme: Vaikuttaa siihen, miltä Mastodon näyttää kun olet kirjautuneena milllä tahansa laitteella.
+        avatar: PNG, GIF tai JPG. Enintään 2 Mt. Skaalataan kokoon 400 x 400 px
+        digest: Lähetetään vain pitkän poissaolon jälkeen ja vain, jos olet saanut suoria viestejä poissaolosi aikana
+        display_name:
+          one: <span class="name-counter">1</span> merkki jäljellä
+          other: <span class="name-counter">%{count}</span> merkkiä jäljellä
+        header: PNG, GIF tai JPG. Enintään 2 Mt. Skaalataan kokoon 700 x 335 px
+        locked: Sinun täytyy hyväksyä seuraajat manuaalisesti
+        note:
+          one: <span class="note-counter">1</span> merkki jäljellä
+          other: <span class="note-counter">%{count}</span> merkkiä jäljellä
+        setting_noindex: Vaikuttaa julkiseen profiiliisi ja tilasivuihisi
+        setting_theme: Vaikuttaa Mastodonin ulkoasuun millä tahansa laitteella kirjauduttaessa.
       imports:
-        data: CSV tiedosto, joka on tuotu toiselta Mastodon-palvelimelta
+        data: Toisesta Mastodon-instanssista tuotu CSV-tiedosto
       sessions:
-        otp: Syötä kaksivaiheisen tunnistuksen koodi puhelimestasi tai käytä yhtä palautuskoodeistasi.
+        otp: Syötä puhelimeen saamasi kaksivaiheisen tunnistautumisen koodi tai käytä palautuskoodia.
       user:
-        filtered_languages: Valitut kielet suodatetaan julkisilta aikajanoilta
+        filtered_languages: Valitut kielet suodatetaan pois julkisilta aikajanoilta
     labels:
       defaults:
         avatar: Profiilikuva
-        confirm_new_password: Varmista uusi salasana
-        confirm_password: Varmista salasana
+        confirm_new_password: Vahvista uusi salasana
+        confirm_password: Vahvista salasana
         current_password: Nykyinen salasana
-        data: Data
+        data: Tiedot
         display_name: Nimimerkki
         email: Sähköpostiosoite
-        expires_in: Vanhentuu
+        expires_in: Vanhenee
         filtered_languages: Suodatetut kielet
         header: Otsakekuva
         locale: Kieli
-        locked: Tee tilistä yksityinen
-        max_uses: Max käyttökerrat
+        locked: Lukitse tili
+        max_uses: Käyttökertoja enintään
         new_password: Uusi salasana
         note: Kuvaus
-        otp_attempt: Kaksivaiheinen koodi
+        otp_attempt: Kaksivaiheisen tunnistautumisen koodi
         password: Salasana
-        setting_auto_play_gif: Animoitujen GIFfien automaattitoisto
-        setting_boost_modal: Näytä vahvistusikkuna ennen boostausta
-        setting_default_privacy: Julkaisun yksityisyys
+        setting_auto_play_gif: Toista GIF-animaatiot automaattisesti
+        setting_boost_modal: Kysy vahvistusta ennen buustausta
+        setting_default_privacy: Julkaisun näkyvyys
         setting_default_sensitive: Merkitse media aina arkaluontoiseksi
-        setting_delete_modal: Näytä vahvistusikkuna ennen töötin poistamista
+        setting_delete_modal: Kysy vahvistusta ennen tuuttauksen poistamista
         setting_display_sensitive_media: Näytä aina arkaluontoiseksi merkitty media
         setting_noindex: Jättäydy pois hakukoneindeksoinnista
-        setting_reduce_motion: Vähennä liikettä animaatioissa
-        setting_system_font_ui: Käytä käyttöjärjestelmän oletusfonttia
+        setting_reduce_motion: Vähennä animaatioiden liikettä
+        setting_system_font_ui: Käytä järjestelmän oletusfonttia
         setting_theme: Sivuston teema
-        setting_unfollow_modal: Näytä vahvistusikkuna ennen seuraamisen lopettamista
-        severity: Vakavuusaste
-        type: Tuontityyppi
+        setting_unfollow_modal: Kysy vahvistusta, ennen kuin lopetat seuraamisen
+        severity: Vakavuus
+        type: Tietojen laji
         username: Käyttäjänimi
         username_or_email: Käyttäjänimi tai sähköposti
       interactions:
         must_be_follower: Estä ilmoitukset käyttäjiltä, jotka eivät seuraa sinua
         must_be_following: Estä ilmoitukset käyttäjiltä, joita et seuraa
-        must_be_following_dm: Estä suorat viestit ihmisiltä, joita et seuraa
+        must_be_following_dm: Estä suorat viestit käyttäjiltä, joita et seuraa
       notification_emails:
-        digest: Lähetä koosteviestejä sähköpostilla
-        favourite: Lähetä sähköposti, kun joku tykkää statuksestasi
+        digest: Lähetä koosteviestejä sähköpostitse
+        favourite: Lähetä sähköposti, kun joku tykkää tilastasi
         follow: Lähetä sähköposti, kun joku seuraa sinua
         follow_request: Lähetä sähköposti, kun joku pyytää seurata sinua
-        mention: Lähetä sähköposti, kun joku mainitsee sinut
+        mention: Lähetä sähköposti, kun sinut mainitaan
         reblog: Lähetä sähköposti, kun joku buustaa julkaisusi
     'no': Ei
     required:
diff --git a/config/locales/simple_form.pl.yml b/config/locales/simple_form.pl.yml
index 4f9594e6f..325cb2691 100644
--- a/config/locales/simple_form.pl.yml
+++ b/config/locales/simple_form.pl.yml
@@ -10,6 +10,7 @@ pl:
           many: Pozostało <span class="name-counter">%{count}</span> znaków
           one: Pozostał <span class="name-counter">1</span> znak
           other: Pozostało <span class="name-counter">%{count}</span> znaków
+        fields: Możesz ustawić maksymalnie 4 niestandardowe pola wyświetlane jako tabela na Twoim profilu
         header: PNG, GIF lub JPG. Maksymalnie 2MB. Zostanie zmniejszony do 700x335px
         locked: Musisz akceptować prośby o śledzenie
         note:
@@ -26,6 +27,10 @@ pl:
       user:
         filtered_languages: Wpisy w wybranych językach nie będą wyświetlać się na publicznych osiach czasu
     labels:
+      account:
+        fields:
+          name: Nazwa
+          value: Zawartość
       defaults:
         avatar: Awatar
         confirm_new_password: Potwierdź nowe hasło
@@ -35,6 +40,7 @@ pl:
         display_name: Widoczna nazwa
         email: Adres e-mail
         expires_in: Wygaśnie po
+        fields: Metadane profilu
         filtered_languages: Filtrowane języki
         header: Nagłówek
         locale: Język
diff --git a/config/locales/simple_form.sk.yml b/config/locales/simple_form.sk.yml
index 7d4241bac..e504c9774 100644
--- a/config/locales/simple_form.sk.yml
+++ b/config/locales/simple_form.sk.yml
@@ -6,21 +6,21 @@ sk:
         avatar: PNG, GIF alebo JPG. Maximálne 2MB. Bude zmenšený na 400x400px
         digest: Odoslané iba v prípade dlhodobej neprítomnosti, a len ak ste obdŕžali nejaké osobné správy kým ste boli preč
         display_name:
-          one: Ostáva vám <span class="name-counter">1</span> znak
-          other: Ostáva vám <span class="name-counter">%{count}</span> znakov
+          one: Ostáva ti <span class="name-counter">1</span> znak
+          other: Ostáva ti <span class="name-counter">%{count}</span> znakov
         header: PNG, GIF alebo JPG. Maximálne 2MB. Bude zmenšený na 700x335px
         locked: Musíte manuálne schváliť sledujúcich
         note:
           one: Ostáva vám <span class="note-counter">1</span> znak
-          other: Ostáva vám <span class="note-counter">%{count}</span>znakov
-        setting_noindex: Ovplyvňuje profil  a správy tak, že ich nebude možné nájsť vyhľadávaním
-        setting_theme: Ovplyvní ako bude Mastodon vyzerať pri prihlásení z hociktorého zariadenia.
+          other: Ostáva ti <span class="note-counter">%{count}</span> znakov
+        setting_noindex: Ovplyvňuje profil a správy tak, že ich nebude možné nájsť vyhľadávaním
+        setting_theme: Toto ovplyvní ako bude Mastodon vyzerať pri prihlásení z hociktorého zariadenia.
       imports:
         data: CSV súbor vyexportovaný z inej Mastodon inštancie
       sessions:
-        otp: Vložte 2FA kód z telefónu alebo použite jeden z vašich obnovovacích kódov.
+        otp: Napíš sem dvoj-faktorový kód z telefónu, alebo použite jeden z vašich obnovovacích kódov.
       user:
-        filtered_languages: Zaškrtnuté jazyky vám nebudú zobrazené vo verejnej časovej osi
+        filtered_languages: Zaškrtnuté jazyky budú pre teba vynechané nebudú z verejnej časovej osi
     labels:
       defaults:
         avatar: Avatar
@@ -53,7 +53,7 @@ sk:
         setting_unfollow_modal: Zobrazovať potvrdzovacie okno pred skončením sledovania iného používateľa
         severity: Závažnosť
         type: Typ importu
-        username: Používateľské meno
+        username: Užívateľské meno
         username_or_email: Prezívka, alebo Email
       interactions:
         must_be_follower: Blokovať notifikácie pod používateľov, ktorí ťa nesledujú
diff --git a/config/locales/simple_form.zh-HK.yml b/config/locales/simple_form.zh-HK.yml
index da0292a90..6b890b036 100644
--- a/config/locales/simple_form.zh-HK.yml
+++ b/config/locales/simple_form.zh-HK.yml
@@ -6,6 +6,7 @@ zh-HK:
         avatar: 支援 PNG, GIF 或 JPG 圖片,檔案最大為 2MB,會縮裁成 400x400px
         digest: 僅在你長時間未登錄,且收到了私信時發送
         display_name: 最多 30 個字元
+        fields: 個人資料頁可顯示多至 4 個項目
         header: 支援 PNG, GIF 或 JPG 圖片,檔案最大為 2MB,會縮裁成 700x335px
         locked: 你必須人手核准每個用戶對你的關注請求,而你的文章私隱會被預設為「只有關注你的人能看」
         note: 最多 160 個字元
@@ -18,6 +19,10 @@ zh-HK:
       user:
         filtered_languages: 下面被選擇的語言的文章將不會出現在你的公共時間軸上。
     labels:
+      account:
+        fields:
+          name: 標籤
+          value: 內容
       defaults:
         avatar: 個人頭像
         confirm_new_password: 確認新密碼
@@ -27,6 +32,7 @@ zh-HK:
         display_name: 顯示名稱
         email: 電郵地址
         expires_in: 失效時間
+        fields: 資料
         filtered_languages: 封鎖下面語言的文章
         header: 個人頁面頂部
         locale: 語言
diff --git a/config/locales/sk.yml b/config/locales/sk.yml
index 2ee25b372..25e672604 100644
--- a/config/locales/sk.yml
+++ b/config/locales/sk.yml
@@ -149,15 +149,15 @@ sk:
         enable_custom_emoji: "%{name} povolil emoji %{target}"
         enable_user: "%{name} povolil prihlásenie pre používateľa %{target}"
         memorialize_account: '%{name} zmenil účet %{target} na stránku "Navždy budeme spomínať"'
-        promote_user: "%{name} povýšil používateľa %{target}"
-        reset_password_user: "%{name} resetoval heslo pre používateľa %{target}"
-        resolve_report: "%{name} zamietol nahlásenie %{target}"
-        silence_account: "%{name} stíšil účet %{target}"
-        suspend_account: "%{name} suspendoval účet používateľa %{target}"
-        unsilence_account: "%{name} zrušil stíšenie účtu používateľa %{target}"
-        unsuspend_account: "%{name} zrušil suspendáciu účtu používateľa %{target}"
-        update_custom_emoji: "%{name} aktualizoval emoji %{target}"
-        update_status: "%{name} aktualizoval status %{target}"
+        promote_user: "%{name} povýšil/a používateľa %{target}"
+        reset_password_user: "%{name} resetoval/a heslo pre používateľa %{target}"
+        resolve_report: "%{name} zamietli nahlásenie %{target}"
+        silence_account: "%{name} utíšil/a účet %{target}"
+        suspend_account: "%{name} zablokoval/a účet používateľa %{target}"
+        unsilence_account: "%{name} zrušil/a utíšenie účtu používateľa %{target}"
+        unsuspend_account: "%{name} zrušil/a blokovanie účtu používateľa %{target}"
+        update_custom_emoji: "%{name} aktualizoval/a emoji %{target}"
+        update_status: "%{name} aktualizoval/a status pre %{target}"
       title: Kontrólny záznam
     custom_emojis:
       by_domain: Doména
@@ -358,7 +358,7 @@ sk:
     warning: Na tieto údaje dávajte ohromný pozor. Nikdy ich s nikým nezďieľajte!
     your_token: Váš prístupový token
   auth:
-    agreement_html: V rámci registrácie súhlasíte, že sa budete riadiť  <a href="%{rules_path}"> 1 pravidlami tejto instancie</a> 2 a taktiež <a href="%{terms_path}"> 3 našími servisnými podmienkami </a> 4.
+    agreement_html: V rámci registrácie súhlasíš, že sa budeš riadiť <a href="%{rules_path}"> pravidlami tejto instancie</a>, a taktiež <a href="%{terms_path}"> našími servisnými podmienkami </a>.
     change_password: Heslo
     confirm_email: Potvrdiť email
     delete_account: Vymazať účet
@@ -366,8 +366,8 @@ sk:
     didnt_get_confirmation: Neobdŕžali ste kroky pre potvrdenie?
     forgot_password: Zabudli ste heslo?
     invalid_reset_password_token: Token na obnovu hesla vypršal. Prosím vypítajte si nový.
-    login: Prihlásenie
-    logout: Odhlásiť sa
+    login: Prihlás sa
+    logout: Odhlás sa
     migrate_account: Presunúť sa na iný účet
     migrate_account_html: Pokiaľ si želáte presmerovať tento účet na nejaký iný, môžete <a href="%{path}"> tak urobiť tu</a>.
     or: alebo
@@ -375,7 +375,7 @@ sk:
     providers:
       cas: CAS
       saml: SAML
-    register: Zaregistrovať sa
+    register: Zaregistruj sa
     register_elsewhere: Zaregistruj sa na inom serveri
     resend_confirmation: Poslať potvrdzujúce pokyny znovu
     reset_password: Resetovať heslo
@@ -677,6 +677,7 @@ sk:
       full_handle: Adresa tvojho profilu v celom formáte
       full_handle_hint: Toto je čo musíš dať vedieť svojím priateľom aby ti mohli posielať správy, alebo ťa následovať z inej instancie.
       review_preferences_action: Zmeniť nastavenia
+      review_preferences_step: Daj si záležať na svojích nastaveniach, napríklad že aké emailové notifikácie chceš dostávať, alebo pod aký level súkromia sa tvoje príspevky majú sami automaticky zaradiť. Pokiaľ nemáš malátnosť z pohybu, môžeš si zvoliť aj automatické spúšťanie GIF animácií.
       subject: Vitaj na Mastodone
       tip_bridge_html: Ak prichádzaš z Twitteru, môžeš svojích priateľov nájsť na Mastodone pomocou tzv. <a href="%{bridge_url}">mostíkovej aplikácie</a>. Ale tá funguje iba ak ju aj oni niekedy použili!
       tip_federated_timeline: Federovaná os zobrazuje sieť Mastodonu až po jej hranice. Ale zahŕňa iba ľúdí ktorých ostatní okolo teba sledujú, takže predsa nieje úplne celistvá.
diff --git a/config/locales/zh-HK.yml b/config/locales/zh-HK.yml
index 5c1feabfc..964ff5811 100644
--- a/config/locales/zh-HK.yml
+++ b/config/locales/zh-HK.yml
@@ -4,6 +4,7 @@ zh-HK:
     about_hashtag_html: 這些是包含「<strong>#%{hashtag}</strong>」標籤的公開文章。只要你有任何 Mastodon 服務站、或者聯盟網站的用戶,便可以與他們互動。
     about_mastodon_html: Mastodon(萬象)是<em>自由、開源</em>的社交網絡。服務站<em>各自獨立而互連</em>,避免單一商業機構壟斷。找你所信任的服務站,建立帳號,你即可與任何服務站上的用戶溝通,享受無縫的<em>網絡交流</em>。
     about_this: 關於本服務站
+    administered_by: 管理者:
     closed_registrations: 本服務站暫時停止接受登記。
     contact: 聯絡
     contact_missing: 未設定
@@ -60,7 +61,15 @@ zh-HK:
       destroyed_msg: 管理記錄已被刪除
     accounts:
       are_you_sure: 你確定嗎?
+      avatar: 頭像
       by_domain: 域名
+      change_email:
+        changed_msg: 帳號電郵更新成功!
+        current_email: 現時電郵
+        label: 改變電郵
+        new_email: 新的電郵
+        submit: 改變電郵
+        title: 改變 %{username} 的電郵
       confirm: 確定
       confirmed: 已確定
       demote: 降任
@@ -108,6 +117,7 @@ zh-HK:
       public: 公共
       push_subscription_expires: PuSH 訂閱過期
       redownload: 更新頭像
+      remove_avatar: 取消頭像
       reset: 重設
       reset_password: 重設密碼
       resubscribe: 重新訂閱
@@ -128,6 +138,7 @@ zh-HK:
       statuses: 文章
       subscribe: 訂閱
       title: 用戶
+      unconfirmed_email: 未確認的電郵
       undo_silenced: 解除靜音
       undo_suspension: 解除停權
       unsubscribe: 取消訂閱
@@ -135,6 +146,8 @@ zh-HK:
       web: 用戶頁面
     action_logs:
       actions:
+        assigned_to_self_report: "%{name} 指派了 %{target} 的舉報給自己"
+        change_email_user: "%{name} 改變了用戶 %{target} 的電郵地址"
         confirm_user: "%{name} 確認了用戶 %{target} 的電郵地址"
         create_custom_emoji: "%{name} 加入自訂表情符號 %{target}"
         create_domain_block: "%{name} 阻隔了網域 %{target}"
@@ -150,10 +163,13 @@ zh-HK:
         enable_user: "%{name} 把用戶 %{target} 設定為允許登入"
         memorialize_account: "%{name} 把 %{target} 設定為追悼帳戶"
         promote_user: "%{name} 對用戶 %{target} 进行了升任操作"
+        remove_avatar_user: "%{name} 取消了 %{target} 的頭像"
+        reopen_report: "%{name} 重開 %{target} 的舉報"
         reset_password_user: "%{name} 重設了用戶 %{target} 的密碼"
         resolve_report: "%{name} 處理了 %{target} 的舉報"
         silence_account: "%{name} 靜音了用戶 %{target}"
         suspend_account: "%{name} 停權了用戶 %{target}"
+        unassigned_report: "%{name} 取消指派 %{target} 的舉報"
         unsilence_account: "%{name} 取消了用戶 %{target} 的靜音狀態"
         unsuspend_account: "%{name} 取消了用戶 %{target} 的停權狀態"
         update_custom_emoji: "%{name} 更新了自訂表情符號 %{target}"
@@ -238,29 +254,60 @@ zh-HK:
         expired: 已失效
         title: 篩選
       title: 邀請用戶
+    report_notes:
+      created_msg: 舉報筆記已建立。
+      destroyed_msg: 舉報筆記已刪除。
     reports:
+      account:
+        created_reports: 由此帳號發出的舉報
+        moderation:
+          silenced: 被靜音的
+          suspended: 被停權的
+          title: 管理操作
+        moderation_notes: 管理筆記
+        note: 筆記
+        report: 舉報
+        targeted_reports: 關於此帳號的舉報
       action_taken_by: 操作執行者
       are_you_sure: 你確認嗎?
+      assign_to_self: 指派給自己
+      assigned: 指派負責人
       comment:
         label: 詳細解釋
         none: 沒有
+      created_at: 日期
       delete: 刪除
+      history: 執行紀錄
       id: ID
       mark_as_resolved: 標示為「已處理」
+      mark_as_unresolved: 標示為「未處理」
+      notes:
+        create: 建立筆記
+        create_and_resolve: 建立筆記並標示為「已處理」
+        create_and_unresolve: 建立筆記並標示為「未處理」
+        delete: 刪除
+        label: 管理筆記
+        new_label: 建立管理筆記
+        placeholder: 記錄已執行的動作,或其他更新
       nsfw:
         'false': 取消 NSFW 標記
         'true': 添加 NSFW 標記
+      reopen: 重開舉報
       report: '舉報 #%{id}'
       report_contents: 舉報內容
       reported_account: 舉報用戶
       reported_by: 舉報者
       resolved: 已處理
+      resolved_msg: 舉報已處理。
       silence_account: 將用戶靜音
       status: 狀態
+      statuses: 被舉報的文章
       suspend_account: 將用戶停權
       target: 對象
       title: 舉報
+      unassign: 取消指派
       unresolved: 未處理
+      updated_at: 更新
       view: 檢視
     settings:
       activity_api_enabled:
@@ -381,6 +428,7 @@ zh-HK:
     security: 登入資訊
     set_new_password: 設定新密碼
   authorize_follow:
+    already_following: 你已經關注了這個帳號
     error: 對不起,尋找這個跨站用戶的過程發生錯誤
     follow: 關注
     follow_request: 關注請求已發送给:
@@ -473,6 +521,7 @@ zh-HK:
       '21600': 6 小時後
       '3600': 1 小時後
       '43200': 12 小時後
+      '604800': 1 週後
       '86400': 1 天後
     expires_in_prompt: 永不過期
     generate: 生成邀請連結
@@ -574,6 +623,10 @@ zh-HK:
     missing_resource: 無法找到你用戶的轉接網址
     proceed: 下一步
     prompt: 你希望關注︰
+  remote_unfollow:
+    error: 錯誤
+    title: 標題
+    unfollowed: 取消關注
   sessions:
     activity: 最近活動
     browser: 瀏覽器
@@ -593,7 +646,7 @@ zh-HK:
       safari: Safari
       uc_browser: UC瀏覽器
       weibo: 新浪微博
-    current_session: 目前的 session
+    current_session: 目前的作業階段
     description: "%{platform} 上的 %{browser}"
     explanation: 這些是現在正登入於你的 Mastodon 帳號的瀏覽器。
     ip: IP 位址
@@ -611,8 +664,8 @@ zh-HK:
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
     revoke: 取消
-    revoke_success: Session 取消成功。
-    title: Session
+    revoke_success: 作業階段取消成功。
+    title: 作業階段
   settings:
     authorized_apps: 授權應用程式
     back: 回到 Mastodon
@@ -677,6 +730,10 @@ zh-HK:
     setup: 設定
     wrong_code: 你輸入的認證碼並不正確!可能伺服器時間和你手機不一致,請檢查你手機的時鐘,或與本站管理員聯絡。
   user_mailer:
+    backup_ready:
+      explanation: 你要求的 Mastodon 帳號完整備份檔案現已就緒,可供下載。
+      subject: 你的備份檔已可供下載
+      title: 檔案匯出
     welcome:
       edit_profile_action: 設定個人資料
       edit_profile_step: 你可以設定你的個人資料,包括上傳頭像、橫幅圖片、更改顯示名稱等等。如果你想在新的關注者關注你之前對他們進行審核,你也可以選擇為你的帳戶設為「私人」。
diff --git a/db/migrate/20180410204633_add_fields_to_accounts.rb b/db/migrate/20180410204633_add_fields_to_accounts.rb
new file mode 100644
index 000000000..5b8c17480
--- /dev/null
+++ b/db/migrate/20180410204633_add_fields_to_accounts.rb
@@ -0,0 +1,5 @@
+class AddFieldsToAccounts < ActiveRecord::Migration[5.1]
+  def change
+    add_column :accounts, :fields, :jsonb
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 7796600d7..6413db3f0 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,10 +10,9 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 20180410220657) do
+ActiveRecord::Schema.define(version: 2018_04_10_220657) do
 
   # These are extensions that must be enabled in order to support this database
-  enable_extension "pg_stat_statements"
   enable_extension "plpgsql"
 
   create_table "account_domain_blocks", force: :cascade do |t|
@@ -75,6 +74,7 @@ ActiveRecord::Schema.define(version: 20180410220657) do
     t.boolean "memorial", default: false, null: false
     t.bigint "moved_to_account_id"
     t.string "featured_collection_url"
+    t.jsonb "fields"
     t.index "(((setweight(to_tsvector('simple'::regconfig, (display_name)::text), 'A'::\"char\") || setweight(to_tsvector('simple'::regconfig, (username)::text), 'B'::\"char\")) || setweight(to_tsvector('simple'::regconfig, (COALESCE(domain, ''::character varying))::text), 'C'::\"char\")))", name: "search_index", using: :gin
     t.index "lower((username)::text), lower((domain)::text)", name: "index_accounts_on_username_and_domain_lower"
     t.index ["uri"], name: "index_accounts_on_uri"
@@ -463,6 +463,7 @@ ActiveRecord::Schema.define(version: 20180410220657) do
     t.bigint "application_id"
     t.bigint "in_reply_to_account_id"
     t.boolean "local_only"
+    t.text "full_status_text", default: "", null: false
     t.index ["account_id", "id", "visibility", "updated_at"], name: "index_statuses_20180106", order: { id: :desc }
     t.index ["conversation_id"], name: "index_statuses_on_conversation_id"
     t.index ["in_reply_to_id"], name: "index_statuses_on_in_reply_to_id"
diff --git a/docker-compose.yml b/docker-compose.yml
index 836cb00b8..8058326dc 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -40,7 +40,7 @@ services:
       - external_network
       - internal_network
     ports:
-      - "3000:3000"
+      - "127.0.0.1:3000:3000"
     depends_on:
       - db
       - redis
@@ -60,7 +60,7 @@ services:
       - external_network
       - internal_network
     ports:
-      - "4000:4000"
+      - "127.0.0.1:4000:4000"
     depends_on:
       - db
       - redis
diff --git a/package.json b/package.json
index 491a487d2..d861f1723 100644
--- a/package.json
+++ b/package.json
@@ -84,8 +84,8 @@
     "prop-types": "^15.5.10",
     "punycode": "^2.1.0",
     "rails-ujs": "^5.1.2",
-    "react": "^16.2.0",
-    "react-dom": "^16.2.0",
+    "react": "^16.3.0",
+    "react-dom": "^16.3.0",
     "react-hotkeys": "^0.10.0",
     "react-immutable-proptypes": "^2.1.0",
     "react-immutable-pure-component": "^1.1.1",
diff --git a/spec/controllers/stream_entries_controller_spec.rb b/spec/controllers/stream_entries_controller_spec.rb
index f81e2be7b..665c5b747 100644
--- a/spec/controllers/stream_entries_controller_spec.rb
+++ b/spec/controllers/stream_entries_controller_spec.rb
@@ -66,16 +66,12 @@ RSpec.describe StreamEntriesController, type: :controller do
   describe 'GET #show' do
     include_examples 'before_action', :show
 
-    it 'renders with HTML' do
-      ancestor = Fabricate(:status)
-      status = Fabricate(:status, in_reply_to_id: ancestor.id)
-      descendant = Fabricate(:status, in_reply_to_id: status.id)
+    it 'redirects to status page' do
+      status = Fabricate(:status)
 
       get :show, params: { account_username: status.account.username, id: status.stream_entry.id }
 
-      expect(assigns(:ancestors)).to eq [ancestor]
-      expect(assigns(:descendants)).to eq [descendant]
-      expect(response).to have_http_status(:success)
+      expect(response).to redirect_to(short_account_status_url(status.account, status))
     end
 
     it 'returns http success with Atom' do
diff --git a/spec/models/status_pin_spec.rb b/spec/models/status_pin_spec.rb
index 6f54f80f9..944baf639 100644
--- a/spec/models/status_pin_spec.rb
+++ b/spec/models/status_pin_spec.rb
@@ -37,5 +37,36 @@ RSpec.describe StatusPin, type: :model do
 
       expect(StatusPin.new(account: account, status: status).save).to be false
     end
+
+    max_pins = 5
+    it 'does not allow pins above the max' do
+      account = Fabricate(:account)
+      status = []
+
+      (max_pins + 1).times do |i|
+        status[i] = Fabricate(:status, account: account)
+      end
+
+      max_pins.times do |i|
+        expect(StatusPin.new(account: account, status: status[i]).save).to be true
+      end
+
+      expect(StatusPin.new(account: account, status: status[max_pins]).save).to be false
+    end
+
+    it 'allows pins above the max for remote accounts' do
+      account = Fabricate(:account, domain: 'remote', username: 'bob', url: 'https://remote/')
+      status = []
+
+      (max_pins + 1).times do |i|
+        status[i] = Fabricate(:status, account: account)
+      end
+
+      max_pins.times do |i|
+        expect(StatusPin.new(account: account, status: status[i]).save).to be true
+      end
+
+      expect(StatusPin.new(account: account, status: status[max_pins]).save).to be true
+    end
   end
 end
diff --git a/spec/services/activitypub/process_account_service_spec.rb b/spec/services/activitypub/process_account_service_spec.rb
index 84a74c231..15e1f4bb2 100644
--- a/spec/services/activitypub/process_account_service_spec.rb
+++ b/spec/services/activitypub/process_account_service_spec.rb
@@ -1,5 +1,31 @@
 require 'rails_helper'
 
 RSpec.describe ActivityPub::ProcessAccountService do
-  pending
+  subject { described_class.new }
+
+  context 'property values' do
+    let(:payload) do
+      {
+        id: 'https://foo',
+        type: 'Actor',
+        inbox: 'https://foo/inbox',
+        attachment: [
+          { type: 'PropertyValue', name: 'Pronouns', value: 'They/them' },
+          { type: 'PropertyValue', name: 'Occupation', value: 'Unit test' },
+        ],
+      }.with_indifferent_access
+    end
+
+    it 'parses out of attachment' do
+      account = subject.call('alice', 'example.com', payload)
+      expect(account.fields).to be_a Array
+      expect(account.fields.size).to eq 2
+      expect(account.fields[0]).to be_a Account::Field
+      expect(account.fields[0].name).to eq 'Pronouns'
+      expect(account.fields[0].value).to eq 'They/them'
+      expect(account.fields[1]).to be_a Account::Field
+      expect(account.fields[1].name).to eq 'Occupation'
+      expect(account.fields[1].value).to eq 'Unit test'
+    end
+  end
 end
diff --git a/streaming/index.js b/streaming/index.js
index d08b9cd87..48bab8078 100644
--- a/streaming/index.js
+++ b/streaming/index.js
@@ -329,52 +329,53 @@ const startWorker = (workerId) => {
 
       // Only messages that may require filtering are statuses, since notifications
       // are already personalized and deletes do not matter
-      if (needsFiltering && event === 'update') {
-        pgPool.connect((err, client, done) => {
-          if (err) {
-            log.error(err);
-            return;
-          }
+      if (!needsFiltering || event !== 'update') {
+        transmit();
+        return;
+      }
 
-          const unpackedPayload  = payload;
-          const targetAccountIds = [unpackedPayload.account.id].concat(unpackedPayload.mentions.map(item => item.id));
-          const accountDomain    = unpackedPayload.account.acct.split('@')[1];
+      const unpackedPayload  = payload;
+      const targetAccountIds = [unpackedPayload.account.id].concat(unpackedPayload.mentions.map(item => item.id));
+      const accountDomain    = unpackedPayload.account.acct.split('@')[1];
 
-          if (Array.isArray(req.filteredLanguages) && req.filteredLanguages.indexOf(unpackedPayload.language) !== -1) {
-            log.silly(req.requestId, `Message ${unpackedPayload.id} filtered by language (${unpackedPayload.language})`);
-            done();
+      if (Array.isArray(req.filteredLanguages) && req.filteredLanguages.indexOf(unpackedPayload.language) !== -1) {
+        log.silly(req.requestId, `Message ${unpackedPayload.id} filtered by language (${unpackedPayload.language})`);
+        return;
+      }
+
+      // When the account is not logged in, it is not necessary to confirm the block or mute
+      if (!req.accountId) {
+        transmit();
+        return;
+      }
+
+      pgPool.connect((err, client, done) => {
+        if (err) {
+          log.error(err);
+          return;
+        }
+
+        const queries = [
+          client.query(`SELECT 1 FROM blocks WHERE (account_id = $1 AND target_account_id IN (${placeholders(targetAccountIds, 2)})) OR (account_id = $2 AND target_account_id = $1) UNION SELECT 1 FROM mutes WHERE account_id = $1 AND target_account_id IN (${placeholders(targetAccountIds, 2)})`, [req.accountId, unpackedPayload.account.id].concat(targetAccountIds)),
+        ];
+
+        if (accountDomain) {
+          queries.push(client.query('SELECT 1 FROM account_domain_blocks WHERE account_id = $1 AND domain = $2', [req.accountId, accountDomain]));
+        }
+
+        Promise.all(queries).then(values => {
+          done();
+
+          if (values[0].rows.length > 0 || (values.length > 1 && values[1].rows.length > 0)) {
             return;
           }
 
-          if (req.accountId) {
-            const queries = [
-              client.query(`SELECT 1 FROM blocks WHERE (account_id = $1 AND target_account_id IN (${placeholders(targetAccountIds, 2)})) OR (account_id = $2 AND target_account_id = $1) UNION SELECT 1 FROM mutes WHERE account_id = $1 AND target_account_id IN (${placeholders(targetAccountIds, 2)})`, [req.accountId, unpackedPayload.account.id].concat(targetAccountIds)),
-            ];
-
-            if (accountDomain) {
-              queries.push(client.query('SELECT 1 FROM account_domain_blocks WHERE account_id = $1 AND domain = $2', [req.accountId, accountDomain]));
-            }
-
-            Promise.all(queries).then(values => {
-              done();
-
-              if (values[0].rows.length > 0 || (values.length > 1 && values[1].rows.length > 0)) {
-                return;
-              }
-
-              transmit();
-            }).catch(err => {
-              done();
-              log.error(err);
-            });
-          } else {
-            done();
-            transmit();
-          }
+          transmit();
+        }).catch(err => {
+          done();
+          log.error(err);
         });
-      } else {
-        transmit();
-      }
+      });
     };
 
     subscribe(`${redisPrefix}${id}`, listener);
diff --git a/yarn.lock b/yarn.lock
index 03915eafe..ea54790c3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5998,9 +5998,9 @@ rc@^1.1.7:
     minimist "^1.2.0"
     strip-json-comments "~2.0.1"
 
-react-dom@^16.2.0:
-  version "16.2.0"
-  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.2.0.tgz#69003178601c0ca19b709b33a83369fe6124c044"
+react-dom@^16.3.0:
+  version "16.3.2"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.3.2.tgz#cb90f107e09536d683d84ed5d4888e9640e0e4df"
   dependencies:
     fbjs "^0.8.16"
     loose-envify "^1.1.0"
@@ -6184,9 +6184,9 @@ react-transition-group@^2.2.0:
     prop-types "^15.5.8"
     warning "^3.0.0"
 
-react@^16.2.0:
-  version "16.2.0"
-  resolved "https://registry.yarnpkg.com/react/-/react-16.2.0.tgz#a31bd2dab89bff65d42134fa187f24d054c273ba"
+react@^16.3.0:
+  version "16.3.2"
+  resolved "https://registry.yarnpkg.com/react/-/react-16.3.2.tgz#fdc8420398533a1e58872f59091b272ce2f91ea9"
   dependencies:
     fbjs "^0.8.16"
     loose-envify "^1.1.0"