about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/controllers/activitypub/collections_controller.rb4
-rw-r--r--app/controllers/admin/accounts_controller.rb13
-rw-r--r--app/controllers/admin/dashboard_controller.rb1
-rw-r--r--app/controllers/admin/settings_controller.rb2
-rw-r--r--app/controllers/admin/tags_controller.rb2
-rw-r--r--app/controllers/concerns/remote_account_controller_concern.rb21
-rw-r--r--app/controllers/directories_controller.rb5
-rw-r--r--app/controllers/settings/applications_controller.rb1
-rw-r--r--app/controllers/settings/sessions_controller.rb5
-rw-r--r--app/helpers/admin/action_logs_helper.rb2
-rw-r--r--app/helpers/home_helper.rb2
-rw-r--r--app/javascript/flavours/glitch/actions/compose.js8
-rw-r--r--app/javascript/flavours/glitch/actions/timelines.js7
-rw-r--r--app/javascript/flavours/glitch/components/scrollable_list.js6
-rw-r--r--app/javascript/flavours/glitch/components/status_list.js2
-rw-r--r--app/javascript/flavours/glitch/features/account/components/header.js4
-rw-r--r--app/javascript/flavours/glitch/features/account_gallery/index.js19
-rw-r--r--app/javascript/flavours/glitch/features/composer/index.js16
-rw-r--r--app/javascript/flavours/glitch/reducers/notifications.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/timelines.js6
-rw-r--r--app/javascript/flavours/glitch/styles/forms.scss1
-rw-r--r--app/javascript/mastodon/actions/compose.js6
-rw-r--r--app/javascript/mastodon/actions/timelines.js7
-rw-r--r--app/javascript/mastodon/components/scrollable_list.js11
-rw-r--r--app/javascript/mastodon/components/status_list.js2
-rw-r--r--app/javascript/mastodon/features/account/components/header.js2
-rw-r--r--app/javascript/mastodon/features/account_gallery/index.js19
-rw-r--r--app/javascript/mastodon/features/followers/index.js1
-rw-r--r--app/javascript/mastodon/features/following/index.js1
-rw-r--r--app/javascript/mastodon/locales/ar.json19
-rw-r--r--app/javascript/mastodon/locales/ast.json3
-rw-r--r--app/javascript/mastodon/locales/bg.json1
-rw-r--r--app/javascript/mastodon/locales/ca.json21
-rw-r--r--app/javascript/mastodon/locales/co.json19
-rw-r--r--app/javascript/mastodon/locales/cs.json27
-rw-r--r--app/javascript/mastodon/locales/cy.json1
-rw-r--r--app/javascript/mastodon/locales/da.json3
-rw-r--r--app/javascript/mastodon/locales/de.json25
-rw-r--r--app/javascript/mastodon/locales/defaultMessages.json4
-rw-r--r--app/javascript/mastodon/locales/el.json25
-rw-r--r--app/javascript/mastodon/locales/eo.json77
-rw-r--r--app/javascript/mastodon/locales/es.json1
-rw-r--r--app/javascript/mastodon/locales/eu.json21
-rw-r--r--app/javascript/mastodon/locales/fa.json1
-rw-r--r--app/javascript/mastodon/locales/fi.json1
-rw-r--r--app/javascript/mastodon/locales/fr.json19
-rw-r--r--app/javascript/mastodon/locales/gl.json23
-rw-r--r--app/javascript/mastodon/locales/he.json1
-rw-r--r--app/javascript/mastodon/locales/hr.json1
-rw-r--r--app/javascript/mastodon/locales/hu.json1
-rw-r--r--app/javascript/mastodon/locales/hy.json1
-rw-r--r--app/javascript/mastodon/locales/id.json1
-rw-r--r--app/javascript/mastodon/locales/io.json1
-rw-r--r--app/javascript/mastodon/locales/it.json19
-rw-r--r--app/javascript/mastodon/locales/ja.json3
-rw-r--r--app/javascript/mastodon/locales/ka.json1
-rw-r--r--app/javascript/mastodon/locales/ko.json1
-rw-r--r--app/javascript/mastodon/locales/ms.json1
-rw-r--r--app/javascript/mastodon/locales/nl.json21
-rw-r--r--app/javascript/mastodon/locales/no.json1
-rw-r--r--app/javascript/mastodon/locales/oc.json28
-rw-r--r--app/javascript/mastodon/locales/pt-BR.json23
-rw-r--r--app/javascript/mastodon/locales/pt.json1
-rw-r--r--app/javascript/mastodon/locales/ro.json1
-rw-r--r--app/javascript/mastodon/locales/ru.json1
-rw-r--r--app/javascript/mastodon/locales/sk.json41
-rw-r--r--app/javascript/mastodon/locales/sl.json1
-rw-r--r--app/javascript/mastodon/locales/sr-Latn.json1
-rw-r--r--app/javascript/mastodon/locales/sr.json1
-rw-r--r--app/javascript/mastodon/locales/sv.json1
-rw-r--r--app/javascript/mastodon/locales/ta.json1
-rw-r--r--app/javascript/mastodon/locales/te.json1
-rw-r--r--app/javascript/mastodon/locales/th.json1
-rw-r--r--app/javascript/mastodon/locales/tr.json1
-rw-r--r--app/javascript/mastodon/locales/uk.json1
-rw-r--r--app/javascript/mastodon/locales/zh-CN.json1
-rw-r--r--app/javascript/mastodon/locales/zh-HK.json1
-rw-r--r--app/javascript/mastodon/locales/zh-TW.json1
-rw-r--r--app/javascript/mastodon/reducers/timelines.js6
-rw-r--r--app/javascript/styles/mastodon/forms.scss1
-rw-r--r--app/models/custom_emoji.rb6
-rw-r--r--app/models/custom_emoji_filter.rb2
-rw-r--r--app/models/form/admin_settings.rb2
-rw-r--r--app/models/user.rb6
-rw-r--r--app/policies/account_policy.rb4
-rw-r--r--app/serializers/activitypub/actor_serializer.rb20
-rw-r--r--app/services/pubsubhubbub/subscribe_service.rb28
-rw-r--r--app/services/update_account_service.rb3
-rw-r--r--app/validators/email_mx_validator.rb19
-rw-r--r--app/views/accounts/_header.html.haml7
-rw-r--r--app/views/admin/accounts/_account.html.haml3
-rw-r--r--app/views/admin/accounts/index.html.haml1
-rw-r--r--app/views/admin/accounts/show.html.haml9
-rw-r--r--app/views/admin/dashboard/index.html.haml6
-rw-r--r--app/views/admin/settings/edit.html.haml3
-rw-r--r--app/views/layouts/public.html.haml3
-rw-r--r--app/views/settings/profiles/show.html.haml5
97 files changed, 442 insertions, 321 deletions
diff --git a/app/controllers/activitypub/collections_controller.rb b/app/controllers/activitypub/collections_controller.rb
index 96bf901a7..995da9c55 100644
--- a/app/controllers/activitypub/collections_controller.rb
+++ b/app/controllers/activitypub/collections_controller.rb
@@ -31,7 +31,7 @@ class ActivityPub::CollectionsController < Api::BaseController
     when 'featured'
       @account.pinned_statuses.count
     else
-      raise ActiveRecord::NotFound
+      raise ActiveRecord::RecordNotFound
     end
   end
 
@@ -42,7 +42,7 @@ class ActivityPub::CollectionsController < Api::BaseController
         scope.merge!(@account.pinned_statuses)
       end
     else
-      raise ActiveRecord::NotFound
+      raise ActiveRecord::RecordNotFound
     end
   end
 
diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb
index f155543ce..771302db8 100644
--- a/app/controllers/admin/accounts_controller.rb
+++ b/app/controllers/admin/accounts_controller.rb
@@ -2,7 +2,7 @@
 
 module Admin
   class AccountsController < BaseController
-    before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload, :remove_avatar, :enable, :disable, :memorialize]
+    before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload, :remove_avatar, :remove_header, :enable, :disable, :memorialize]
     before_action :require_remote_account!, only: [:subscribe, :unsubscribe, :redownload]
     before_action :require_local_account!, only: [:enable, :disable, :memorialize]
 
@@ -71,6 +71,17 @@ module Admin
       redirect_to admin_account_path(@account.id)
     end
 
+    def remove_header
+      authorize @account, :remove_header?
+
+      @account.header = nil
+      @account.save!
+
+      log_action :remove_header, @account.user
+
+      redirect_to admin_account_path(@account.id)
+    end
+
     private
 
     def set_account
diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb
index 7be753c9b..bb923c185 100644
--- a/app/controllers/admin/dashboard_controller.rb
+++ b/app/controllers/admin/dashboard_controller.rb
@@ -28,6 +28,7 @@ module Admin
       @pam_enabled           = ENV['PAM_ENABLED'] == 'true'
       @hidden_service        = ENV['ALLOW_ACCESS_TO_HIDDEN_SERVICE'] == 'true'
       @trending_hashtags     = TrendingTags.get(7)
+      @profile_directory     = Setting.profile_directory
     end
 
     private
diff --git a/app/controllers/admin/settings_controller.rb b/app/controllers/admin/settings_controller.rb
index fe2720c48..76b3c3a2b 100644
--- a/app/controllers/admin/settings_controller.rb
+++ b/app/controllers/admin/settings_controller.rb
@@ -28,6 +28,7 @@ module Admin
       show_known_fediverse_at_about_page
       preview_sensitive_media
       custom_css
+      profile_directory
     ).freeze
 
     BOOLEAN_SETTINGS = %w(
@@ -39,6 +40,7 @@ module Admin
       peers_api_enabled
       show_known_fediverse_at_about_page
       preview_sensitive_media
+      profile_directory
     ).freeze
 
     UPLOAD_SETTINGS = %w(
diff --git a/app/controllers/admin/tags_controller.rb b/app/controllers/admin/tags_controller.rb
index 3f2256566..e9f4f2cfa 100644
--- a/app/controllers/admin/tags_controller.rb
+++ b/app/controllers/admin/tags_controller.rb
@@ -18,7 +18,7 @@ module Admin
 
     def unhide
       authorize @tag, :unhide?
-      @tag.account_tag_stat.update!(hidden: true)
+      @tag.account_tag_stat.update!(hidden: false)
       redirect_to admin_tags_path(@filter_params)
     end
 
diff --git a/app/controllers/concerns/remote_account_controller_concern.rb b/app/controllers/concerns/remote_account_controller_concern.rb
deleted file mode 100644
index e17910642..000000000
--- a/app/controllers/concerns/remote_account_controller_concern.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-
-module RemoteAccountControllerConcern
-  extend ActiveSupport::Concern
-
-  included do
-    layout 'public'
-    before_action :set_account
-    before_action :check_account_suspension
-  end
-
-  private
-
-  def set_account
-    @account = Account.find_remote!(params[:acct])
-  end
-
-  def check_account_suspension
-    gone if @account.suspended?
-  end
-end
diff --git a/app/controllers/directories_controller.rb b/app/controllers/directories_controller.rb
index 9d65361a6..1c8ebdac9 100644
--- a/app/controllers/directories_controller.rb
+++ b/app/controllers/directories_controller.rb
@@ -3,6 +3,7 @@
 class DirectoriesController < ApplicationController
   layout 'public'
 
+  before_action :check_enabled
   before_action :set_instance_presenter
   before_action :set_tag, only: :show
   before_action :set_tags
@@ -23,6 +24,10 @@ class DirectoriesController < ApplicationController
     use_pack 'share'
   end
 
+  def check_enabled
+    return not_found unless Setting.profile_directory
+  end
+
   def set_tag
     @tag = Tag.discoverable.find_by!(name: params[:id].downcase)
   end
diff --git a/app/controllers/settings/applications_controller.rb b/app/controllers/settings/applications_controller.rb
index 03c890a50..d3ac268d8 100644
--- a/app/controllers/settings/applications_controller.rb
+++ b/app/controllers/settings/applications_controller.rb
@@ -1,7 +1,6 @@
 # frozen_string_literal: true
 
 class Settings::ApplicationsController < Settings::BaseController
-
   before_action :set_application, only: [:show, :update, :destroy, :regenerate]
   before_action :prepare_scopes, only: [:create, :update]
 
diff --git a/app/controllers/settings/sessions_controller.rb b/app/controllers/settings/sessions_controller.rb
index f235dd477..780ea64b4 100644
--- a/app/controllers/settings/sessions_controller.rb
+++ b/app/controllers/settings/sessions_controller.rb
@@ -3,7 +3,6 @@
 #  Intentionally does not inherit from BaseController
 class Settings::SessionsController < ApplicationController
   before_action :set_session, only: :destroy
-  before_action :set_body_classes
 
   def destroy
     @session.destroy!
@@ -16,8 +15,4 @@ class Settings::SessionsController < ApplicationController
   def set_session
     @session = current_user.session_activations.find(params[:id])
   end
-
-  def set_body_classes
-    @body_classes = 'admin'
-  end
 end
diff --git a/app/helpers/admin/action_logs_helper.rb b/app/helpers/admin/action_logs_helper.rb
index c28f0be6b..68cf8c75d 100644
--- a/app/helpers/admin/action_logs_helper.rb
+++ b/app/helpers/admin/action_logs_helper.rb
@@ -92,7 +92,7 @@ module Admin::ActionLogsHelper
       opposite_verbs?(log) ? 'negative' : 'positive'
     when :update, :reset_password, :disable_2fa, :memorialize, :change_email
       'neutral'
-    when :demote, :silence, :disable, :suspend, :remove_avatar, :reopen
+    when :demote, :silence, :disable, :suspend, :remove_avatar, :remove_header, :reopen
       'negative'
     when :destroy
       opposite_verbs?(log) ? 'positive' : 'negative'
diff --git a/app/helpers/home_helper.rb b/app/helpers/home_helper.rb
index ba7c443c2..9b3f1380b 100644
--- a/app/helpers/home_helper.rb
+++ b/app/helpers/home_helper.rb
@@ -23,7 +23,7 @@ module HomeHelper
                   else
                     link_to(path || TagManager.instance.url_for(account), class: 'account__display-name') do
                       content_tag(:div, class: 'account__avatar-wrapper') do
-                        content_tag(:div, '', class: 'account__avatar', style: "width: #{size}px; height: #{size}px; background-size: #{size}px #{size}px; background-image: url(#{account.avatar.url})")
+                        content_tag(:div, '', class: 'account__avatar', style: "width: #{size}px; height: #{size}px; background-size: #{size}px #{size}px; background-image: url(#{full_asset_url(current_account&.user&.setting_auto_play_gif ? account.avatar_original_url : account.avatar_static_url)})")
                       end +
                         content_tag(:span, class: 'display-name') do
                           content_tag(:bdi) do
diff --git a/app/javascript/flavours/glitch/actions/compose.js b/app/javascript/flavours/glitch/actions/compose.js
index 58f2b3786..d53d26924 100644
--- a/app/javascript/flavours/glitch/actions/compose.js
+++ b/app/javascript/flavours/glitch/actions/compose.js
@@ -116,7 +116,7 @@ export function directCompose(account, router) {
   };
 };
 
-export function submitCompose() {
+export function submitCompose(routerHistory) {
   return function (dispatch, getState) {
     let status = getState().getIn(['compose', 'text'], '');
     let media  = getState().getIn(['compose', 'media_attachments']);
@@ -158,6 +158,12 @@ export function submitCompose() {
         }
       };
 
+      if (routerHistory && routerHistory.location.pathname === '/statuses/new'
+          && window.history.state
+          && !getState().getIn(['compose', 'advanced_options', 'threaded_mode'])) {
+        routerHistory.goBack();
+      }
+
       insertIfOnline('home');
 
       if (response.data.in_reply_to_id === null && response.data.visibility === 'public') {
diff --git a/app/javascript/flavours/glitch/actions/timelines.js b/app/javascript/flavours/glitch/actions/timelines.js
index ffd259d5f..d13d66516 100644
--- a/app/javascript/flavours/glitch/actions/timelines.js
+++ b/app/javascript/flavours/glitch/actions/timelines.js
@@ -54,11 +54,13 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
       params.since_id = timeline.getIn(['items', 0]);
     }
 
+    const isLoadingRecent = !!params.since_id;
+
     dispatch(expandTimelineRequest(timelineId, isLoadingMore));
 
     api(getState).get(path, { params }).then(response => {
       const next = getLinks(response).refs.find(link => link.rel === 'next');
-      dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.code === 206, isLoadingMore));
+      dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.code === 206, isLoadingRecent, isLoadingMore));
       done();
     }).catch(error => {
       dispatch(expandTimelineFail(timelineId, error, isLoadingMore));
@@ -85,13 +87,14 @@ export function expandTimelineRequest(timeline, isLoadingMore) {
   };
 };
 
-export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadingMore) {
+export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadingRecent, isLoadingMore) {
   return {
     type: TIMELINE_EXPAND_SUCCESS,
     timeline,
     statuses,
     next,
     partial,
+    isLoadingRecent,
     skipLoading: !isLoadingMore,
   };
 };
diff --git a/app/javascript/flavours/glitch/components/scrollable_list.js b/app/javascript/flavours/glitch/components/scrollable_list.js
index a05d49829..bd922462e 100644
--- a/app/javascript/flavours/glitch/components/scrollable_list.js
+++ b/app/javascript/flavours/glitch/components/scrollable_list.js
@@ -164,7 +164,7 @@ export default class ScrollableList extends PureComponent {
     const { fullscreen } = this.state;
     const childrenCount = React.Children.count(children);
 
-    const loadMore     = (hasMore && childrenCount > 0 && onLoadMore) ? <LoadMore visible={!isLoading} onClick={this.handleLoadMore} /> : null;
+    const loadMore     = (hasMore && onLoadMore) ? <LoadMore visible={!isLoading} onClick={this.handleLoadMore} /> : null;
     let scrollableArea = null;
 
     if (showLoading) {
@@ -179,7 +179,7 @@ export default class ScrollableList extends PureComponent {
           </div>
         </div>
       );
-    } else if (isLoading || childrenCount > 0 || !emptyMessage) {
+    } else if (isLoading || childrenCount > 0 || hasMore || !emptyMessage) {
       scrollableArea = (
         <div className={classNames('scrollable', { fullscreen })} ref={this.setRef}>
           <div role='feed' className='item-list'>
@@ -204,7 +204,7 @@ export default class ScrollableList extends PureComponent {
       );
     } else {
       scrollableArea = (
-        <div className={classNames('scrollable', { fullscreen })} ref={this.setRef} style={{ flex: '1 1 auto', display: 'flex', flexDirection: 'column' }}>
+        <div className={classNames('scrollable scrollable--flex', { fullscreen })} ref={this.setRef}>
           {alwaysPrepend && prepend}
 
           <div className='empty-column-indicator'>
diff --git a/app/javascript/flavours/glitch/components/status_list.js b/app/javascript/flavours/glitch/components/status_list.js
index 68cd608b9..5249af76d 100644
--- a/app/javascript/flavours/glitch/components/status_list.js
+++ b/app/javascript/flavours/glitch/components/status_list.js
@@ -55,7 +55,7 @@ export default class StatusList extends ImmutablePureComponent {
   }
 
   handleLoadOlder = debounce(() => {
-    this.props.onLoadMore(this.props.statusIds.last());
+    this.props.onLoadMore(this.props.statusIds.size > 0 ? this.props.statusIds.last() : undefined);
   }, 300, { leading: true })
 
   _selectChild (index) {
diff --git a/app/javascript/flavours/glitch/features/account/components/header.js b/app/javascript/flavours/glitch/features/account/components/header.js
index f0d36947d..dc5b1447b 100644
--- a/app/javascript/flavours/glitch/features/account/components/header.js
+++ b/app/javascript/flavours/glitch/features/account/components/header.js
@@ -7,7 +7,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
 import Avatar from 'flavours/glitch/components/avatar';
 import IconButton from 'flavours/glitch/components/icon_button';
 
-import { me } from 'flavours/glitch/util/initial_state';
+import { autoPlayGif, me } from 'flavours/glitch/util/initial_state';
 import classNames from 'classnames';
 
 const messages = defineMessages({
@@ -108,7 +108,7 @@ export default class Header extends ImmutablePureComponent {
 
     return (
       <div className='account__header__wrapper'>
-        <div className={classNames('account__header', { inactive: !!account.get('moved') })} style={{ backgroundImage: `url(${account.get('header')})` }}>
+        <div className={classNames('account__header', { inactive: !!account.get('moved') })} style={{ backgroundImage: `url(${autoPlayGif ? account.get('header') : account.get('header_static')})` }}>
           <div>
             <a
               href={account.get('url')}
diff --git a/app/javascript/flavours/glitch/features/account_gallery/index.js b/app/javascript/flavours/glitch/features/account_gallery/index.js
index 53b906d16..3f61af0e8 100644
--- a/app/javascript/flavours/glitch/features/account_gallery/index.js
+++ b/app/javascript/flavours/glitch/features/account_gallery/index.js
@@ -35,7 +35,7 @@ class LoadMoreMedia extends ImmutablePureComponent {
     return (
       <LoadMore
         disabled={this.props.disabled}
-        onLoadMore={this.handleLoadMore}
+        onClick={this.handleLoadMore}
       />
     );
   }
@@ -67,7 +67,7 @@ export default class AccountGallery extends ImmutablePureComponent {
 
   handleScrollToBottom = () => {
     if (this.props.hasMore) {
-      this.handleLoadMore(this.props.medias.last().getIn(['status', 'id']));
+      this.handleLoadMore(this.props.medias.size > 0 ? this.props.medias.last().getIn(['status', 'id']) : undefined);
     }
   }
 
@@ -107,8 +107,8 @@ export default class AccountGallery extends ImmutablePureComponent {
       );
     }
 
-    if (!isLoading && medias.size > 0 && hasMore) {
-      loadOlder = <LoadMore onClick={this.handleLoadOlder} />;
+    if (hasMore) {
+      loadOlder = <LoadMore visible={!isLoading} onClick={this.handleLoadOlder} />;
     }
 
     return (
@@ -116,14 +116,15 @@ export default class AccountGallery extends ImmutablePureComponent {
         <ColumnBackButton />
 
         <ScrollContainer scrollKey='account_gallery' shouldUpdateScroll={this.shouldUpdateScroll}>
-          <div className='scrollable' onScroll={this.handleScroll}>
+          <div className='scrollable scrollable--flex' onScroll={this.handleScroll}>
             <HeaderContainer accountId={this.props.params.accountId} />
 
-            <div className='account-gallery__container'>
+            <div role='feed' className='account-gallery__container'>
               {medias.map((media, index) => media === null ? (
                 <LoadMoreMedia
                   key={'more:' + medias.getIn(index + 1, 'id')}
                   maxId={index > 0 ? medias.getIn(index - 1, 'id') : null}
+                  onLoadMore={this.handleLoadMore}
                 />
               ) : (
                 <MediaItem
@@ -133,6 +134,12 @@ export default class AccountGallery extends ImmutablePureComponent {
               ))}
               {loadOlder}
             </div>
+
+            {isLoading && medias.size === 0 && (
+              <div className='scrollable__append'>
+                <LoadingIndicator />
+              </div>
+            )}
           </div>
         </ScrollContainer>
       </Column>
diff --git a/app/javascript/flavours/glitch/features/composer/index.js b/app/javascript/flavours/glitch/features/composer/index.js
index 029b11a36..40eae1f53 100644
--- a/app/javascript/flavours/glitch/features/composer/index.js
+++ b/app/javascript/flavours/glitch/features/composer/index.js
@@ -159,15 +159,15 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
   onSelectSuggestion(position, token, suggestion) {
     dispatch(selectComposeSuggestion(position, token, suggestion));
   },
-  onMediaDescriptionConfirm() {
+  onMediaDescriptionConfirm(routerHistory) {
     dispatch(openModal('CONFIRM', {
       message: intl.formatMessage(messages.missingDescriptionMessage),
       confirm: intl.formatMessage(messages.missingDescriptionConfirm),
-      onConfirm: () => dispatch(submitCompose()),
+      onConfirm: () => dispatch(submitCompose(routerHistory)),
     }));
   },
-  onSubmit() {
-    dispatch(submitCompose());
+  onSubmit(routerHistory) {
+    dispatch(submitCompose(routerHistory));
   },
   onUndoUpload(id) {
     dispatch(undoUploadCompose(id));
@@ -256,9 +256,9 @@ const handlers = {
           inputs[firstWithoutDescription].focus();
         }
       }
-      onMediaDescriptionConfirm();
+      onMediaDescriptionConfirm(this.context.router ? this.context.router.history : null);
     } else if (onSubmit) {
-      onSubmit();
+      onSubmit(this.context.router ? this.context.router.history : null);
     }
   },
 
@@ -563,6 +563,10 @@ Composer.propTypes = {
   onMediaDescriptionConfirm: PropTypes.func,
 };
 
+Composer.contextTypes = {
+  router: PropTypes.object,
+};
+
 //  Connecting and export.
 export { Composer as WrappedComponent };
 export default wrap(Composer, mapStateToProps, mapDispatchToProps, true);
diff --git a/app/javascript/flavours/glitch/reducers/notifications.js b/app/javascript/flavours/glitch/reducers/notifications.js
index 0b816e85e..b65c51f32 100644
--- a/app/javascript/flavours/glitch/reducers/notifications.js
+++ b/app/javascript/flavours/glitch/reducers/notifications.js
@@ -96,7 +96,7 @@ const expandNormalizedNotifications = (state, notifications, next) => {
     }
 
     if (!next) {
-      mutable.set('hasMore', true);
+      mutable.set('hasMore', false);
     }
 
     mutable.set('isLoading', false);
diff --git a/app/javascript/flavours/glitch/reducers/timelines.js b/app/javascript/flavours/glitch/reducers/timelines.js
index 844a0580f..a9eaae26e 100644
--- a/app/javascript/flavours/glitch/reducers/timelines.js
+++ b/app/javascript/flavours/glitch/reducers/timelines.js
@@ -25,10 +25,10 @@ const initialTimeline = ImmutableMap({
   items: ImmutableList(),
 });
 
-const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial) => {
+const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial, isLoadingRecent) => {
   return state.update(timeline, initialTimeline, map => map.withMutations(mMap => {
     mMap.set('isLoading', false);
-    if (!next) mMap.set('hasMore', false);
+    if (!next && !isLoadingRecent) mMap.set('hasMore', false);
 
     if (!statuses.isEmpty()) {
       mMap.update('items', ImmutableList(), oldIds => {
@@ -116,7 +116,7 @@ export default function timelines(state = initialState, action) {
   case TIMELINE_EXPAND_FAIL:
     return state.update(action.timeline, initialTimeline, map => map.set('isLoading', false));
   case TIMELINE_EXPAND_SUCCESS:
-    return expandNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next, action.partial);
+    return expandNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next, action.partial, action.isLoadingRecent);
   case TIMELINE_UPDATE:
     return updateTimeline(state, action.timeline, fromJS(action.status));
   case TIMELINE_DELETE:
diff --git a/app/javascript/flavours/glitch/styles/forms.scss b/app/javascript/flavours/glitch/styles/forms.scss
index 4f96204f2..6132dd1ae 100644
--- a/app/javascript/flavours/glitch/styles/forms.scss
+++ b/app/javascript/flavours/glitch/styles/forms.scss
@@ -420,6 +420,7 @@ code {
     border: 1px solid darken($ui-base-color, 14%);
     border-radius: 4px;
     padding: 10px;
+    padding-right: 30px;
     height: 41px;
   }
 
diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js
index 86d83122f..1c7f14b5e 100644
--- a/app/javascript/mastodon/actions/compose.js
+++ b/app/javascript/mastodon/actions/compose.js
@@ -144,7 +144,11 @@ export function submitCompose(routerHistory) {
 
       if (response.data.visibility === 'direct' && getState().getIn(['conversations', 'mounted']) <= 0 && routerHistory) {
         routerHistory.push('/timelines/direct');
-      } else if (response.data.visibility !== 'direct') {
+      } else if (routerHistory && routerHistory.location.pathname === '/statuses/new' && window.history.state) {
+        routerHistory.goBack();
+      }
+
+      if (response.data.visibility !== 'direct') {
         insertIfOnline('home');
       }
 
diff --git a/app/javascript/mastodon/actions/timelines.js b/app/javascript/mastodon/actions/timelines.js
index 215adc4ea..6e7bd027c 100644
--- a/app/javascript/mastodon/actions/timelines.js
+++ b/app/javascript/mastodon/actions/timelines.js
@@ -74,12 +74,14 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
       params.since_id = timeline.getIn(['items', 0]);
     }
 
+    const isLoadingRecent = !!params.since_id;
+
     dispatch(expandTimelineRequest(timelineId, isLoadingMore));
 
     api(getState).get(path, { params }).then(response => {
       const next = getLinks(response).refs.find(link => link.rel === 'next');
       dispatch(importFetchedStatuses(response.data));
-      dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.code === 206, isLoadingMore));
+      dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.code === 206, isLoadingRecent, isLoadingMore));
       done();
     }).catch(error => {
       dispatch(expandTimelineFail(timelineId, error, isLoadingMore));
@@ -112,13 +114,14 @@ export function expandTimelineRequest(timeline, isLoadingMore) {
   };
 };
 
-export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadingMore) {
+export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadingRecent, isLoadingMore) {
   return {
     type: TIMELINE_EXPAND_SUCCESS,
     timeline,
     statuses,
     next,
     partial,
+    isLoadingRecent,
     skipLoading: !isLoadingMore,
   };
 };
diff --git a/app/javascript/mastodon/components/scrollable_list.js b/app/javascript/mastodon/components/scrollable_list.js
index ab4e7d59c..774c8835d 100644
--- a/app/javascript/mastodon/components/scrollable_list.js
+++ b/app/javascript/mastodon/components/scrollable_list.js
@@ -30,7 +30,6 @@ export default class ScrollableList extends PureComponent {
     hasMore: PropTypes.bool,
     prepend: PropTypes.node,
     alwaysPrepend: PropTypes.bool,
-    alwaysShowScrollbar: PropTypes.bool,
     emptyMessage: PropTypes.node,
     children: PropTypes.node,
   };
@@ -206,11 +205,11 @@ export default class ScrollableList extends PureComponent {
   }
 
   render () {
-    const { children, scrollKey, trackScroll, shouldUpdateScroll, showLoading, isLoading, hasMore, prepend, alwaysPrepend, alwaysShowScrollbar, emptyMessage, onLoadMore } = this.props;
+    const { children, scrollKey, trackScroll, shouldUpdateScroll, showLoading, isLoading, hasMore, prepend, alwaysPrepend, emptyMessage, onLoadMore } = this.props;
     const { fullscreen } = this.state;
     const childrenCount = React.Children.count(children);
 
-    const loadMore     = (hasMore && childrenCount > 0 && onLoadMore) ? <LoadMore visible={!isLoading} onClick={this.handleLoadMore} /> : null;
+    const loadMore     = (hasMore && onLoadMore) ? <LoadMore visible={!isLoading} onClick={this.handleLoadMore} /> : null;
     let scrollableArea = null;
 
     if (showLoading) {
@@ -225,7 +224,7 @@ export default class ScrollableList extends PureComponent {
           </div>
         </div>
       );
-    } else if (isLoading || childrenCount > 0 || !emptyMessage) {
+    } else if (isLoading || childrenCount > 0 || hasMore || !emptyMessage) {
       scrollableArea = (
         <div className={classNames('scrollable', { fullscreen })} ref={this.setRef} onMouseMove={this.handleMouseMove}>
           <div role='feed' className='item-list'>
@@ -249,10 +248,8 @@ export default class ScrollableList extends PureComponent {
         </div>
       );
     } else {
-      const scrollable = alwaysShowScrollbar;
-
       scrollableArea = (
-        <div className={classNames({ scrollable, fullscreen })} ref={this.setRef} style={{ flex: '1 1 auto', display: 'flex', flexDirection: 'column' }}>
+        <div className={classNames('scrollable scrollable--flex', { fullscreen })} ref={this.setRef}>
           {alwaysPrepend && prepend}
 
           <div className='empty-column-indicator'>
diff --git a/app/javascript/mastodon/components/status_list.js b/app/javascript/mastodon/components/status_list.js
index 01cc05661..e417f9a2b 100644
--- a/app/javascript/mastodon/components/status_list.js
+++ b/app/javascript/mastodon/components/status_list.js
@@ -55,7 +55,7 @@ export default class StatusList extends ImmutablePureComponent {
   }
 
   handleLoadOlder = debounce(() => {
-    this.props.onLoadMore(this.props.statusIds.last());
+    this.props.onLoadMore(this.props.statusIds.size > 0 ? this.props.statusIds.last() : undefined);
   }, 300, { leading: true })
 
   _selectChild (index) {
diff --git a/app/javascript/mastodon/features/account/components/header.js b/app/javascript/mastodon/features/account/components/header.js
index 8604e7167..2ab25cde4 100644
--- a/app/javascript/mastodon/features/account/components/header.js
+++ b/app/javascript/mastodon/features/account/components/header.js
@@ -158,7 +158,7 @@ class Header extends ImmutablePureComponent {
     const badge           = account.get('bot') ? (<div className='roles'><div className='account-role bot'><FormattedMessage id='account.badges.bot' defaultMessage='Bot' /></div></div>) : null;
 
     return (
-      <div className={classNames('account__header', { inactive: !!account.get('moved') })} style={{ backgroundImage: `url(${account.get('header')})` }}>
+      <div className={classNames('account__header', { inactive: !!account.get('moved') })} style={{ backgroundImage: `url(${autoPlayGif ? account.get('header') : account.get('header_static')})` }}>
         <div>
           <Avatar account={account} />
 
diff --git a/app/javascript/mastodon/features/account_gallery/index.js b/app/javascript/mastodon/features/account_gallery/index.js
index 32cb5ebdc..0d66868ed 100644
--- a/app/javascript/mastodon/features/account_gallery/index.js
+++ b/app/javascript/mastodon/features/account_gallery/index.js
@@ -36,7 +36,7 @@ class LoadMoreMedia extends ImmutablePureComponent {
     return (
       <LoadMore
         disabled={this.props.disabled}
-        onLoadMore={this.handleLoadMore}
+        onClick={this.handleLoadMore}
       />
     );
   }
@@ -68,7 +68,7 @@ class AccountGallery extends ImmutablePureComponent {
 
   handleScrollToBottom = () => {
     if (this.props.hasMore) {
-      this.handleLoadMore(this.props.medias.last().getIn(['status', 'id']));
+      this.handleLoadMore(this.props.medias.size > 0 ? this.props.medias.last().getIn(['status', 'id']) : undefined);
     }
   }
 
@@ -103,8 +103,8 @@ class AccountGallery extends ImmutablePureComponent {
       );
     }
 
-    if (!isLoading && medias.size > 0 && hasMore) {
-      loadOlder = <LoadMore onClick={this.handleLoadOlder} />;
+    if (hasMore) {
+      loadOlder = <LoadMore visible={!isLoading} onClick={this.handleLoadOlder} />;
     }
 
     return (
@@ -112,14 +112,15 @@ class AccountGallery extends ImmutablePureComponent {
         <ColumnBackButton />
 
         <ScrollContainer scrollKey='account_gallery' shouldUpdateScroll={shouldUpdateScroll}>
-          <div className='scrollable' onScroll={this.handleScroll}>
+          <div className='scrollable scrollable--flex' onScroll={this.handleScroll}>
             <HeaderContainer accountId={this.props.params.accountId} />
 
-            <div className='account-gallery__container'>
+            <div role='feed' className='account-gallery__container'>
               {medias.map((media, index) => media === null ? (
                 <LoadMoreMedia
                   key={'more:' + medias.getIn(index + 1, 'id')}
                   maxId={index > 0 ? medias.getIn(index - 1, 'id') : null}
+                  onLoadMore={this.handleLoadMore}
                 />
               ) : (
                 <MediaItem
@@ -129,6 +130,12 @@ class AccountGallery extends ImmutablePureComponent {
               ))}
               {loadOlder}
             </div>
+
+            {isLoading && medias.size === 0 && (
+              <div className='scrollable__append'>
+                <LoadingIndicator />
+              </div>
+            )}
           </div>
         </ScrollContainer>
       </Column>
diff --git a/app/javascript/mastodon/features/followers/index.js b/app/javascript/mastodon/features/followers/index.js
index b9ca7f3dd..ce56f270c 100644
--- a/app/javascript/mastodon/features/followers/index.js
+++ b/app/javascript/mastodon/features/followers/index.js
@@ -73,7 +73,6 @@ class Followers extends ImmutablePureComponent {
           shouldUpdateScroll={shouldUpdateScroll}
           prepend={<HeaderContainer accountId={this.props.params.accountId} hideTabs />}
           alwaysPrepend
-          alwaysShowScrollbar
           emptyMessage={emptyMessage}
         >
           {accountIds.map(id =>
diff --git a/app/javascript/mastodon/features/following/index.js b/app/javascript/mastodon/features/following/index.js
index b3e160240..bda0438a0 100644
--- a/app/javascript/mastodon/features/following/index.js
+++ b/app/javascript/mastodon/features/following/index.js
@@ -73,7 +73,6 @@ class Following extends ImmutablePureComponent {
           shouldUpdateScroll={shouldUpdateScroll}
           prepend={<HeaderContainer accountId={this.props.params.accountId} hideTabs />}
           alwaysPrepend
-          alwaysShowScrollbar
           emptyMessage={emptyMessage}
         >
           {accountIds.map(id =>
diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json
index 2d73f84e0..798c7bfd8 100644
--- a/app/javascript/mastodon/locales/ar.json
+++ b/app/javascript/mastodon/locales/ar.json
@@ -1,5 +1,5 @@
 {
-  "account.add_or_remove_from_list": "Add or Remove from lists",
+  "account.add_or_remove_from_list": "اضافو أو حذف مِن القوائم",
   "account.badges.bot": "روبوت",
   "account.block": "حظر @{name}",
   "account.block_domain": "إخفاء كل شيئ قادم من إسم النطاق {domain}",
@@ -17,6 +17,7 @@
   "account.follows_you": "يتابعك",
   "account.hide_reblogs": "إخفاء ترقيات @{name}",
   "account.link_verified_on": "تم التحقق مِن مالك هذا الرابط بتاريخ {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "وسائط",
   "account.mention": "أُذكُر @{name}",
   "account.moved_to": "{name} إنتقل إلى :",
@@ -112,7 +113,7 @@
   "emoji_button.search_results": "نتائج البحث",
   "emoji_button.symbols": "رموز",
   "emoji_button.travel": "أماكن و أسفار",
-  "empty_column.account_timeline": "No toots here!",
+  "empty_column.account_timeline": "ليس هناك تبويقات!",
   "empty_column.blocks": "لم تقم بحظر أي مستخدِم بعد.",
   "empty_column.community": "الخط الزمني المحلي فارغ. أكتب شيئا ما للعامة كبداية !",
   "empty_column.direct": "لم تتلق أية رسالة خاصة مباشِرة بعد. سوف يتم عرض الرسائل المباشرة هنا إن قمت بإرسال واحدة أو تلقيت البعض منها.",
@@ -138,12 +139,12 @@
   "getting_started.open_source_notice": "ماستدون برنامج مفتوح المصدر. يمكنك المساهمة، أو الإبلاغ عن تقارير الأخطاء، على جيت هب {github}.",
   "getting_started.security": "الأمان",
   "getting_started.terms": "شروط الخدمة",
-  "hashtag.column_header.tag_mode.all": "and {additional}",
-  "hashtag.column_header.tag_mode.any": "or {additional}",
-  "hashtag.column_header.tag_mode.none": "without {additional}",
-  "hashtag.column_settings.tag_mode.all": "All of these",
-  "hashtag.column_settings.tag_mode.any": "Any of these",
-  "hashtag.column_settings.tag_mode.none": "None of these",
+  "hashtag.column_header.tag_mode.all": "و {additional}",
+  "hashtag.column_header.tag_mode.any": "أو {additional}",
+  "hashtag.column_header.tag_mode.none": "بدون {additional}",
+  "hashtag.column_settings.tag_mode.all": "كلها",
+  "hashtag.column_settings.tag_mode.any": "أي كان مِن هذه",
+  "hashtag.column_settings.tag_mode.none": "لا شيء مِن هذه",
   "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
   "home.column_settings.basic": "أساسية",
   "home.column_settings.show_reblogs": "عرض الترقيات",
@@ -321,7 +322,7 @@
   "status.show_less_all": "طي الكل",
   "status.show_more": "أظهر المزيد",
   "status.show_more_all": "توسيع الكل",
-  "status.show_thread": "Show thread",
+  "status.show_thread": "الكشف عن المحادثة",
   "status.unmute_conversation": "فك الكتم عن المحادثة",
   "status.unpin": "فك التدبيس من الملف الشخصي",
   "suggestions.dismiss": "إلغاء الإقتراح",
diff --git a/app/javascript/mastodon/locales/ast.json b/app/javascript/mastodon/locales/ast.json
index e22c46e9e..bb6d5c167 100644
--- a/app/javascript/mastodon/locales/ast.json
+++ b/app/javascript/mastodon/locales/ast.json
@@ -17,6 +17,7 @@
   "account.follows_you": "Síguete",
   "account.hide_reblogs": "Hide boosts from @{name}",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Media",
   "account.mention": "Mentar a @{name}",
   "account.moved_to": "{name} has moved to:",
@@ -208,7 +209,7 @@
   "navigation_bar.follow_requests": "Solicitúes de siguimientu",
   "navigation_bar.info": "Tocante a esta instancia",
   "navigation_bar.keyboard_shortcuts": "Atayos",
-  "navigation_bar.lists": "Lists",
+  "navigation_bar.lists": "Llistes",
   "navigation_bar.logout": "Zarrar sesión",
   "navigation_bar.mutes": "Usuarios silenciaos",
   "navigation_bar.personal": "Personal",
diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json
index 1a5a70593..c82fd8c81 100644
--- a/app/javascript/mastodon/locales/bg.json
+++ b/app/javascript/mastodon/locales/bg.json
@@ -17,6 +17,7 @@
   "account.follows_you": "Твой последовател",
   "account.hide_reblogs": "Hide boosts from @{name}",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Media",
   "account.mention": "Споменаване",
   "account.moved_to": "{name} has moved to:",
diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json
index bc1fd8950..e4e9f183d 100644
--- a/app/javascript/mastodon/locales/ca.json
+++ b/app/javascript/mastodon/locales/ca.json
@@ -1,5 +1,5 @@
 {
-  "account.add_or_remove_from_list": "Add or Remove from lists",
+  "account.add_or_remove_from_list": "Afegir o Treure de les llistes",
   "account.badges.bot": "Bot",
   "account.block": "Bloca @{name}",
   "account.block_domain": "Amaga-ho tot de {domain}",
@@ -17,6 +17,7 @@
   "account.follows_you": "Et segueix",
   "account.hide_reblogs": "Amaga els impulsos de @{name}",
   "account.link_verified_on": "La propietat d'aquest enllaç es va verificar el dia {date}",
+  "account.locked_info": "Aquest estat de privadesa del compte està definit com a bloquejat. El propietari revisa manualment qui pot seguir-lo.",
   "account.media": "Media",
   "account.mention": "Esmentar @{name}",
   "account.moved_to": "{name} s'ha mogut a:",
@@ -112,7 +113,7 @@
   "emoji_button.search_results": "Resultats de la cerca",
   "emoji_button.symbols": "Símbols",
   "emoji_button.travel": "Viatges i Llocs",
-  "empty_column.account_timeline": "No toots here!",
+  "empty_column.account_timeline": "No hi ha toots aquí!",
   "empty_column.blocks": "Encara no has bloquejat cap usuari.",
   "empty_column.community": "La línia de temps local és buida. Escriu alguna cosa públicament per fer rodar la pilota!",
   "empty_column.direct": "Encara no tens missatges directes. Quan enviïs o rebis un, es mostrarà aquí.",
@@ -138,12 +139,12 @@
   "getting_started.open_source_notice": "Mastodon és un programari de codi obert. Pots contribuir o informar de problemes a GitHub a {github}.",
   "getting_started.security": "Seguretat",
   "getting_started.terms": "Termes del servei",
-  "hashtag.column_header.tag_mode.all": "and {additional}",
-  "hashtag.column_header.tag_mode.any": "or {additional}",
-  "hashtag.column_header.tag_mode.none": "without {additional}",
-  "hashtag.column_settings.tag_mode.all": "All of these",
-  "hashtag.column_settings.tag_mode.any": "Any of these",
-  "hashtag.column_settings.tag_mode.none": "None of these",
+  "hashtag.column_header.tag_mode.all": "i {additional}",
+  "hashtag.column_header.tag_mode.any": "o {additional}",
+  "hashtag.column_header.tag_mode.none": "sense {additional}",
+  "hashtag.column_settings.tag_mode.all": "Tots aquests",
+  "hashtag.column_settings.tag_mode.any": "Qualsevol d’aquests",
+  "hashtag.column_settings.tag_mode.none": "Cap d’aquests",
   "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
   "home.column_settings.basic": "Bàsic",
   "home.column_settings.show_reblogs": "Mostrar impulsos",
@@ -321,11 +322,11 @@
   "status.show_less_all": "Mostra menys per a tot",
   "status.show_more": "Mostra més",
   "status.show_more_all": "Mostra més per a tot",
-  "status.show_thread": "Show thread",
+  "status.show_thread": "Mostra el fil",
   "status.unmute_conversation": "Activar conversació",
   "status.unpin": "Deslliga del perfil",
   "suggestions.dismiss": "Descartar suggeriment",
-  "suggestions.header": "És possible que t’interessi…",
+  "suggestions.header": "És possible que estiguis interessat en…",
   "tabs_bar.federated_timeline": "Federada",
   "tabs_bar.home": "Inici",
   "tabs_bar.local_timeline": "Local",
diff --git a/app/javascript/mastodon/locales/co.json b/app/javascript/mastodon/locales/co.json
index 45bf0dc43..1d8d61a7a 100644
--- a/app/javascript/mastodon/locales/co.json
+++ b/app/javascript/mastodon/locales/co.json
@@ -1,5 +1,5 @@
 {
-  "account.add_or_remove_from_list": "Add or Remove from lists",
+  "account.add_or_remove_from_list": "Aghjustà o toglie da e liste",
   "account.badges.bot": "Bot",
   "account.block": "Bluccà @{name}",
   "account.block_domain": "Piattà tuttu da {domain}",
@@ -17,6 +17,7 @@
   "account.follows_you": "Vi seguita",
   "account.hide_reblogs": "Piattà spartere da @{name}",
   "account.link_verified_on": "A prupietà di stu ligame hè stata verificata u {date}",
+  "account.locked_info": "U statutu di vita privata di u contu hè chjosu. U pruprietariu esamina manualmente e dumande d'abbunamentu.",
   "account.media": "Media",
   "account.mention": "Mintuvà @{name}",
   "account.moved_to": "{name} hè partutu nant'à:",
@@ -112,7 +113,7 @@
   "emoji_button.search_results": "Risultati di a cerca",
   "emoji_button.symbols": "Simbuli",
   "emoji_button.travel": "Lochi è Viaghju",
-  "empty_column.account_timeline": "No toots here!",
+  "empty_column.account_timeline": "Nisun statutu quì!",
   "empty_column.blocks": "Per avà ùn avete bluccatu manc'un utilizatore.",
   "empty_column.community": "Ùn c'hè nunda indè a linea lucale. Scrivete puru qualcosa!",
   "empty_column.direct": "Ùn avete ancu nisun missaghju direttu. S'è voi mandate o ricevete unu, u vidarete quì.",
@@ -138,12 +139,12 @@
   "getting_started.open_source_notice": "Mastodon ghjè un lugiziale liberu. Pudete cuntribuisce à u codice o a traduzione, o palisà un bug, nant'à GitHub: {github}.",
   "getting_started.security": "Sicurità",
   "getting_started.terms": "Cundizione di u serviziu",
-  "hashtag.column_header.tag_mode.all": "and {additional}",
-  "hashtag.column_header.tag_mode.any": "or {additional}",
-  "hashtag.column_header.tag_mode.none": "without {additional}",
-  "hashtag.column_settings.tag_mode.all": "All of these",
-  "hashtag.column_settings.tag_mode.any": "Any of these",
-  "hashtag.column_settings.tag_mode.none": "None of these",
+  "hashtag.column_header.tag_mode.all": "è {additional}",
+  "hashtag.column_header.tag_mode.any": "o {additional}",
+  "hashtag.column_header.tag_mode.none": "senza {additional}",
+  "hashtag.column_settings.tag_mode.all": "Tutti quessi",
+  "hashtag.column_settings.tag_mode.any": "Unu di quessi",
+  "hashtag.column_settings.tag_mode.none": "Nisunu di quessi",
   "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
   "home.column_settings.basic": "Bàsichi",
   "home.column_settings.show_reblogs": "Vede e spartere",
@@ -321,7 +322,7 @@
   "status.show_less_all": "Ripiegà tuttu",
   "status.show_more": "Slibrà",
   "status.show_more_all": "Slibrà tuttu",
-  "status.show_thread": "Show thread",
+  "status.show_thread": "Vede u filu",
   "status.unmute_conversation": "Ùn piattà più a cunversazione",
   "status.unpin": "Spuntarulà da u prufile",
   "suggestions.dismiss": "Righjittà a pruposta",
diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json
index 80fab00c3..0d7f42656 100644
--- a/app/javascript/mastodon/locales/cs.json
+++ b/app/javascript/mastodon/locales/cs.json
@@ -1,5 +1,5 @@
 {
-  "account.add_or_remove_from_list": "Add or Remove from lists",
+  "account.add_or_remove_from_list": "Přidat nebo odstranit ze seznamů",
   "account.badges.bot": "Robot",
   "account.block": "Zablokovat uživatele @{name}",
   "account.block_domain": "Skrýt vše z {domain}",
@@ -17,6 +17,7 @@
   "account.follows_you": "Sleduje vás",
   "account.hide_reblogs": "Skrýt boosty od uživatele @{name}",
   "account.link_verified_on": "Vlastnictví tohoto odkazu bylo zkontrolováno {date}",
+  "account.locked_info": "Stav soukromí tohoto účtu je nastaven na zamčeno. Jeho vlastník ručně posuzuje, kdo ho může sledovat.",
   "account.media": "Média",
   "account.mention": "Zmínit uživatele @{name}",
   "account.moved_to": "{name} se přesunul/a na:",
@@ -112,7 +113,7 @@
   "emoji_button.search_results": "Výsledky hledání",
   "emoji_button.symbols": "Symboly",
   "emoji_button.travel": "Cestování a místa",
-  "empty_column.account_timeline": "No toots here!",
+  "empty_column.account_timeline": "Tady nejsou žádné tooty!",
   "empty_column.blocks": "Ještě jste nezablokoval/a žádného uživatele.",
   "empty_column.community": "Místní časová osa je prázdná. Napište něco veřejně a rozhýbejte to tu!",
   "empty_column.direct": "Ještě nemáte žádné přímé zprávy. Pokud nějakou pošlete nebo dostanete, zobrazí se zde.",
@@ -138,13 +139,13 @@
   "getting_started.open_source_notice": "Mastodon je otevřený software. Na GitHubu k němu můžete přispět nebo nahlásit chyby: {github}.",
   "getting_started.security": "Zabezpečení",
   "getting_started.terms": "Podmínky používání",
-  "hashtag.column_header.tag_mode.all": "and {additional}",
-  "hashtag.column_header.tag_mode.any": "or {additional}",
-  "hashtag.column_header.tag_mode.none": "without {additional}",
-  "hashtag.column_settings.tag_mode.all": "All of these",
-  "hashtag.column_settings.tag_mode.any": "Any of these",
-  "hashtag.column_settings.tag_mode.none": "None of these",
-  "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
+  "hashtag.column_header.tag_mode.all": "a {additional}",
+  "hashtag.column_header.tag_mode.any": "nebo {additional}",
+  "hashtag.column_header.tag_mode.none": "bez {additional}",
+  "hashtag.column_settings.tag_mode.all": "Všechny z těchto",
+  "hashtag.column_settings.tag_mode.any": "Jakékoliv z těchto",
+  "hashtag.column_settings.tag_mode.none": "Žádné z těchto",
+  "hashtag.column_settings.tag_toggle": "Zahrnout v tomto sloupci dodatečné hashtagy",
   "home.column_settings.basic": "Základní",
   "home.column_settings.show_reblogs": "Zobrazit boosty",
   "home.column_settings.show_replies": "Zobrazit odpovědi",
@@ -173,7 +174,7 @@
   "keyboard_shortcuts.profile": "k otevření autorova profilu",
   "keyboard_shortcuts.reply": "k odpovězení",
   "keyboard_shortcuts.requests": "k otevření seznamu požadavků o sledování",
-  "keyboard_shortcuts.search": "k zaměření na vyhledávání",
+  "keyboard_shortcuts.search": "k zaměření na hledání",
   "keyboard_shortcuts.start": "k otevření sloupce „začínáme“",
   "keyboard_shortcuts.toggle_hidden": "k zobrazení/skrytí textu za varováním o obsahu",
   "keyboard_shortcuts.toot": "k napsání úplně nového tootu",
@@ -188,7 +189,7 @@
   "lists.edit": "Upravit seznam",
   "lists.new.create": "Přidat seznam",
   "lists.new.title_placeholder": "Název nového seznamu",
-  "lists.search": "Hledejte mezi uživateli, které sledujete",
+  "lists.search": "Hledejte mezi lidmi, které sledujete",
   "lists.subheading": "Vaše seznamy",
   "loading_indicator.label": "Načítám...",
   "media_gallery.toggle_visible": "Přepínat viditelnost",
@@ -276,7 +277,7 @@
   "report.submit": "Odeslat",
   "report.target": "Nahlásit {target}",
   "search.placeholder": "Hledat",
-  "search_popout.search_format": "Pokročilé vyhledávání",
+  "search_popout.search_format": "Pokročilé hledání",
   "search_popout.tips.full_text": "Jednoduchý textový výpis příspěvků, které jste napsal/a, oblíbil/a si, boostnul/a, nebo v nich byl/a zmíněn/a, včetně odpovídajících přezdívek, zobrazovaných jmen a hashtagů.",
   "search_popout.tips.hashtag": "hashtag",
   "search_popout.tips.status": "příspěvek",
@@ -321,7 +322,7 @@
   "status.show_less_all": "Zobrazit méně pro všechny",
   "status.show_more": "Zobrazit více",
   "status.show_more_all": "Zobrazit více pro všechny",
-  "status.show_thread": "Show thread",
+  "status.show_thread": "Zobrazit vlákno",
   "status.unmute_conversation": "Přestat ignorovat konverzaci",
   "status.unpin": "Odepnout z profilu",
   "suggestions.dismiss": "Odmítnout návrh",
diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json
index 4047b54d6..b1fb76934 100644
--- a/app/javascript/mastodon/locales/cy.json
+++ b/app/javascript/mastodon/locales/cy.json
@@ -17,6 +17,7 @@
   "account.follows_you": "Yn eich dilyn chi",
   "account.hide_reblogs": "Cuddio bwstiau o @{name}",
   "account.link_verified_on": "Gwiriwyd perchnogaeth y ddolen yma ar {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Cyfryngau",
   "account.mention": "Crybwyll @{name}",
   "account.moved_to": "Mae @{name} wedi symud i:",
diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json
index 133f9e696..8fb002ae8 100644
--- a/app/javascript/mastodon/locales/da.json
+++ b/app/javascript/mastodon/locales/da.json
@@ -1,5 +1,5 @@
 {
-  "account.add_or_remove_from_list": "Add or Remove from lists",
+  "account.add_or_remove_from_list": "Tilføj eller fjern fra lister",
   "account.badges.bot": "Robot",
   "account.block": "Bloker @{name}",
   "account.block_domain": "Skjul alt fra {domain}",
@@ -17,6 +17,7 @@
   "account.follows_you": "Følger dig",
   "account.hide_reblogs": "Skjul fremhævelserne fra @{name}",
   "account.link_verified_on": "Ejerskabet af dette link blev tjekket den %{date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Medie",
   "account.mention": "Nævn @{name}",
   "account.moved_to": "{name} er flyttet til:",
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index 6bba6114e..5ac95122f 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -1,10 +1,10 @@
 {
-  "account.add_or_remove_from_list": "Add or Remove from lists",
+  "account.add_or_remove_from_list": "Hinzufügen oder Entfernen von Listen",
   "account.badges.bot": "Bot",
   "account.block": "@{name} blockieren",
   "account.block_domain": "Alles von {domain} verstecken",
   "account.blocked": "Blockiert",
-  "account.direct": "Direct Message @{name}",
+  "account.direct": "Direktnachricht an @{name}",
   "account.disclaimer_full": "Das Profil wird möglicherweise unvollständig wiedergegeben.",
   "account.domain_blocked": "Domain versteckt",
   "account.edit_profile": "Profil bearbeiten",
@@ -17,6 +17,7 @@
   "account.follows_you": "Folgt dir",
   "account.hide_reblogs": "Geteilte Beiträge von @{name} verbergen",
   "account.link_verified_on": "Besitz dieses Links wurde geprüft am {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Medien",
   "account.mention": "@{name} erwähnen",
   "account.moved_to": "{name} ist umgezogen auf:",
@@ -112,7 +113,7 @@
   "emoji_button.search_results": "Suchergebnisse",
   "emoji_button.symbols": "Symbole",
   "emoji_button.travel": "Reisen und Orte",
-  "empty_column.account_timeline": "No toots here!",
+  "empty_column.account_timeline": "Keine Beiträge!",
   "empty_column.blocks": "Du hast keine Profile blockiert.",
   "empty_column.community": "Die lokale Zeitleiste ist leer. Schreibe einen öffentlichen Beitrag, um den Ball ins Rollen zu bringen!",
   "empty_column.direct": "Du hast noch keine Direktnachrichten erhalten. Wenn du eine sendest oder empfängst, wird sie hier zu sehen sein.",
@@ -138,12 +139,12 @@
   "getting_started.open_source_notice": "Mastodon ist quelloffene Software. Du kannst auf GitHub unter {github} dazu beitragen oder Probleme melden.",
   "getting_started.security": "Sicherheit",
   "getting_started.terms": "Nutzungsbedingungen",
-  "hashtag.column_header.tag_mode.all": "and {additional}",
-  "hashtag.column_header.tag_mode.any": "or {additional}",
-  "hashtag.column_header.tag_mode.none": "without {additional}",
-  "hashtag.column_settings.tag_mode.all": "All of these",
-  "hashtag.column_settings.tag_mode.any": "Any of these",
-  "hashtag.column_settings.tag_mode.none": "None of these",
+  "hashtag.column_header.tag_mode.all": "und {additional}",
+  "hashtag.column_header.tag_mode.any": "oder {additional}",
+  "hashtag.column_header.tag_mode.none": "ohne {additional}",
+  "hashtag.column_settings.tag_mode.all": "All diese",
+  "hashtag.column_settings.tag_mode.any": "Eine von diesen",
+  "hashtag.column_settings.tag_mode.none": "Keine von diesen",
   "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
   "home.column_settings.basic": "Einfach",
   "home.column_settings.show_reblogs": "Geteilte Beiträge anzeigen",
@@ -321,11 +322,11 @@
   "status.show_less_all": "Zeige weniger für alles",
   "status.show_more": "Mehr anzeigen",
   "status.show_more_all": "Zeige mehr für alles",
-  "status.show_thread": "Show thread",
+  "status.show_thread": "Zeige Thread",
   "status.unmute_conversation": "Stummschaltung von Thread aufheben",
   "status.unpin": "Vom Profil lösen",
-  "suggestions.dismiss": "Dismiss suggestion",
-  "suggestions.header": "You might be interested in…",
+  "suggestions.dismiss": "Hinweis ausblenden",
+  "suggestions.header": "Du bist vielleicht interessiert in…",
   "tabs_bar.federated_timeline": "Föderation",
   "tabs_bar.home": "Startseite",
   "tabs_bar.local_timeline": "Lokal",
diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json
index 57988fbd8..1015aba1b 100644
--- a/app/javascript/mastodon/locales/defaultMessages.json
+++ b/app/javascript/mastodon/locales/defaultMessages.json
@@ -644,6 +644,10 @@
         "id": "account.link_verified_on"
       },
       {
+        "defaultMessage": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
+        "id": "account.locked_info"
+      },
+      {
         "defaultMessage": "Follows you",
         "id": "account.follows_you"
       },
diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json
index ba332be3f..468a4c728 100644
--- a/app/javascript/mastodon/locales/el.json
+++ b/app/javascript/mastodon/locales/el.json
@@ -1,5 +1,5 @@
 {
-  "account.add_or_remove_from_list": "Add or Remove from lists",
+  "account.add_or_remove_from_list": "Προσθήκη ή αφαίρεση από λίστες",
   "account.badges.bot": "Μποτ",
   "account.block": "Απόκλεισε τον/την @{name}",
   "account.block_domain": "Απόκρυψε τα πάντα από το {domain}",
@@ -17,6 +17,7 @@
   "account.follows_you": "Σε ακολουθεί",
   "account.hide_reblogs": "Απόκρυψη προωθήσεων από @{name}",
   "account.link_verified_on": "Η ιδιοκτησία αυτού του συνδέσμου εκλέχθηκε την {date}",
+  "account.locked_info": "Η κατάσταση απορρήτου αυτού του λογαριασμού είναι κλειδωμένη. Ο ιδιοκτήτης επιβεβαιώνει χειροκίνητα ποιος μπορεί να τον ακολουθήσει.",
   "account.media": "Πολυμέσα",
   "account.mention": "Ανάφερε @{name}",
   "account.moved_to": "{name} μεταφέρθηκε στο:",
@@ -112,7 +113,7 @@
   "emoji_button.search_results": "Αποτελέσματα αναζήτησης",
   "emoji_button.symbols": "Σύμβολα",
   "emoji_button.travel": "Ταξίδια & Τοποθεσίες",
-  "empty_column.account_timeline": "No toots here!",
+  "empty_column.account_timeline": "Δεν έχει τουτ εδώ!",
   "empty_column.blocks": "Δεν έχεις αποκλείσει κανέναν χρήστη ακόμα.",
   "empty_column.community": "Η τοπική ροή είναι κενή. Γράψε κάτι δημόσιο παραμύθι ν' αρχινίσει!",
   "empty_column.direct": "Δεν έχεις προσωπικά μηνύματα ακόμα. Όταν στείλεις ή λάβεις κανένα, θα εμφανιστεί εδώ.",
@@ -138,12 +139,12 @@
   "getting_started.open_source_notice": "Το Mastodon είναι ελεύθερο λογισμικό. Μπορείς να συνεισφέρεις ή να αναφέρεις ζητήματα στο GitHub στο {github}.",
   "getting_started.security": "Ασφάλεια",
   "getting_started.terms": "Όροι χρήσης",
-  "hashtag.column_header.tag_mode.all": "and {additional}",
-  "hashtag.column_header.tag_mode.any": "or {additional}",
-  "hashtag.column_header.tag_mode.none": "without {additional}",
-  "hashtag.column_settings.tag_mode.all": "All of these",
-  "hashtag.column_settings.tag_mode.any": "Any of these",
-  "hashtag.column_settings.tag_mode.none": "None of these",
+  "hashtag.column_header.tag_mode.all": "και {additional}",
+  "hashtag.column_header.tag_mode.any": "ή {additional}",
+  "hashtag.column_header.tag_mode.none": "χωρίς {additional}",
+  "hashtag.column_settings.tag_mode.all": "Όλα αυτα",
+  "hashtag.column_settings.tag_mode.any": "Οποιοδήποτε από αυτά",
+  "hashtag.column_settings.tag_mode.none": "Κανένα από αυτά",
   "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
   "home.column_settings.basic": "Βασικά",
   "home.column_settings.show_reblogs": "Εμφάνιση προωθήσεων",
@@ -233,7 +234,7 @@
   "notifications.group": "{count} ειδοποιήσεις",
   "onboarding.done": "Όλα έτοιμα",
   "onboarding.next": "Επόμενο",
-  "onboarding.page_five.public_timelines": "Η τοπική ροή δείχνει τις δημόσιες δημοσιεύσεις από όσους εδρεύουν στον κόμβο {domain}. Η ομοσπονδιακή ροή δείχνει τις δημόσιες δημοσιεύσεις εκείνων που οι χρήστες του {domain} ακολουθούν. Αυτές οι είναι Δημόσιες Ροές, ένας ωραίος τρόπος να ανακαλύψεις καινούριους ανθρώπους.",
+  "onboarding.page_five.public_timelines": "Η τοπική ροή δείχνει τις δημόσιες δημοσιεύσεις από όσους εδρεύουν στον κόμβο {domain}. Η ομοσπονδιακή ροή δείχνει τις δημόσιες δημοσιεύσεις εκείνων που οι χρήστες του {domain} ακολουθούν. Αυτές είναι οι Δημόσιες Ροές, ένας ωραίος τρόπος να ανακαλύψεις καινούριους ανθρώπους.",
   "onboarding.page_four.home": "Η αρχική ροή δείχνει καταστάσεις από ανθρώπους που ακολουθείς.",
   "onboarding.page_four.notifications": "Η στήλη ειδοποιήσεων δείχνει πότε κάποιος αλληλεπιδράει μαζί σου.",
   "onboarding.page_one.federation": "Το Mastodon είναι ένα δίκτυο ανεξάρτητων εξυπηρετητών (servers) που συνεργάζονται δημιουργώντας ένα μεγαλύτερο κοινωνικό δίκτυο. Τους εξυπηρετητές αυτούς τους λέμε κόμβους.",
@@ -321,11 +322,11 @@
   "status.show_less_all": "Δείξε λιγότερα για όλα",
   "status.show_more": "Δείξε περισσότερα",
   "status.show_more_all": "Δείξε περισσότερα για όλα",
-  "status.show_thread": "Show thread",
+  "status.show_thread": "Εμφάνιση νήματος",
   "status.unmute_conversation": "Διέκοψε την αποσιώπηση της συζήτησης",
   "status.unpin": "Ξεκαρφίτσωσε από το προφίλ",
-  "suggestions.dismiss": "Dismiss suggestion",
-  "suggestions.header": "You might be interested in…",
+  "suggestions.dismiss": "Απόρριψη πρότασης",
+  "suggestions.header": "Ίσως να ενδιαφέρεσαι για…",
   "tabs_bar.federated_timeline": "Ομοσπονδιακή",
   "tabs_bar.home": "Αρχική",
   "tabs_bar.local_timeline": "Τοπικά",
diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
index f7a303cdd..37304d8d1 100644
--- a/app/javascript/mastodon/locales/eo.json
+++ b/app/javascript/mastodon/locales/eo.json
@@ -1,5 +1,5 @@
 {
-  "account.add_or_remove_from_list": "Add or Remove from lists",
+  "account.add_or_remove_from_list": "Aldoni al aŭ forigi el listoj",
   "account.badges.bot": "Roboto",
   "account.block": "Bloki @{name}",
   "account.block_domain": "Kaŝi ĉion de {domain}",
@@ -11,12 +11,13 @@
   "account.endorse": "Montri en profilo",
   "account.follow": "Sekvi",
   "account.followers": "Sekvantoj",
-  "account.followers.empty": "No one follows this user yet.",
+  "account.followers.empty": "Neniu ankoraŭ sekvas ĉi tiun uzanton.",
   "account.follows": "Sekvatoj",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows.empty": "Ĉi tiu uzanto ne ankoraŭ sekvas iun.",
   "account.follows_you": "Sekvas vin",
   "account.hide_reblogs": "Kaŝi diskonigojn de @{name}",
-  "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.link_verified_on": "Proprieto de ĉi tiu ligilo estis kontrolita je {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Aŭdovidaĵoj",
   "account.mention": "Mencii @{name}",
   "account.moved_to": "{name} moviĝis al:",
@@ -91,9 +92,9 @@
   "confirmations.mute.confirm": "Silentigi",
   "confirmations.mute.message": "Ĉu vi certas, ke vi volas silentigi {name}?",
   "confirmations.redraft.confirm": "Forigi kaj reskribi",
-  "confirmations.redraft.message": "Ĉu vi certas, ke vi volas forigi tiun mesaĝon kaj reskribi ĝin? Vi perdos ĉiujn respondojn, diskonigojn kaj stelumojn ligitajn al ĝi.",
-  "confirmations.reply.confirm": "Reply",
-  "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
+  "confirmations.redraft.message": "Ĉu vi certas ke vi volas forigi tiun mesaĝon kaj reskribi ĝin? Ĉiuj diskonigoj kaj stelumoj estos perditaj, kaj respondoj al la originala mesaĝo estos orfigitaj.",
+  "confirmations.reply.confirm": "Respondi",
+  "confirmations.reply.message": "Respondi nun anstataŭigos la mesaĝon ke vi aktuale skribas. Ĉu vi certas ke vi volas daŭrigi?",
   "confirmations.unfollow.confirm": "Ne plu sekvi",
   "confirmations.unfollow.message": "Ĉu vi certas, ke vi volas ĉesi sekvi {name}?",
   "embed.instructions": "Enkorpigu ĉi tiun mesaĝon en vian retejon per kopio de la suba kodo.",
@@ -112,20 +113,20 @@
   "emoji_button.search_results": "Serĉaj rezultoj",
   "emoji_button.symbols": "Simboloj",
   "emoji_button.travel": "Vojaĝoj kaj lokoj",
-  "empty_column.account_timeline": "No toots here!",
-  "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.account_timeline": "Neniu mesaĝo ĉi tie!",
+  "empty_column.blocks": "Vi ne ankoraŭ blokis iun uzanton.",
   "empty_column.community": "La loka tempolinio estas malplena. Skribu ion por plenigi ĝin!",
   "empty_column.direct": "Vi ankoraŭ ne havas rektan mesaĝon. Kiam vi sendos aŭ ricevos iun, ĝi aperos ĉi tie.",
-  "empty_column.domain_blocks": "There are no hidden domains yet.",
-  "empty_column.favourited_statuses": "You don't have any favourite toots yet. When you favourite one, it will show up here.",
-  "empty_column.favourites": "No one has favourited this toot yet. When someone does, they will show up here.",
-  "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.domain_blocks": "Ankoraŭ estas neniu domajno blokita.",
+  "empty_column.favourited_statuses": "Vi ne ankoraŭ havas iun stelumitan mesaĝon. Kiam vi stelumos iun, tiu aperos ĉi tie.",
+  "empty_column.favourites": "Neniu ankoraŭ stelumis ĉi tiun mesaĝon. Kiam iu faros ĝin, tiu aperos ĉi tie.",
+  "empty_column.follow_requests": "Vi ne ankoraŭ havas iun peton de sekvado. Kiam vi ricevos unu, ĝi aperos ĉi tie.",
   "empty_column.hashtag": "Ankoraŭ estas nenio per ĉi tiu kradvorto.",
   "empty_column.home": "Via hejma tempolinio estas malplena! Vizitu {public} aŭ uzu la serĉilon por renkonti aliajn uzantojn.",
   "empty_column.home.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.lists": "You don't have any lists yet. When you create one, it will show up here.",
-  "empty_column.mutes": "You haven't muted any users yet.",
+  "empty_column.mutes": "Vi ne ankoraŭ silentigis iun uzanton.",
   "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",
   "follow_request.authorize": "Rajtigi",
@@ -141,40 +142,40 @@
   "hashtag.column_header.tag_mode.all": "and {additional}",
   "hashtag.column_header.tag_mode.any": "or {additional}",
   "hashtag.column_header.tag_mode.none": "without {additional}",
-  "hashtag.column_settings.tag_mode.all": "All of these",
-  "hashtag.column_settings.tag_mode.any": "Any of these",
-  "hashtag.column_settings.tag_mode.none": "None of these",
+  "hashtag.column_settings.tag_mode.all": "Ĉiuj",
+  "hashtag.column_settings.tag_mode.any": "Iu ajn",
+  "hashtag.column_settings.tag_mode.none": "Neniu",
   "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
   "home.column_settings.basic": "Bazaj agordoj",
   "home.column_settings.show_reblogs": "Montri diskonigojn",
   "home.column_settings.show_replies": "Montri respondojn",
   "keyboard_shortcuts.back": "por reveni",
-  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.blocked": "por malfermi la liston de blokitaj uzantoj",
   "keyboard_shortcuts.boost": "por diskonigi",
   "keyboard_shortcuts.column": "por fokusigi mesaĝon en unu el la kolumnoj",
   "keyboard_shortcuts.compose": "por fokusigi la tekstujon",
   "keyboard_shortcuts.description": "Priskribo",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "por malfermi la kolumnon de rektaj mesaĝoj",
   "keyboard_shortcuts.down": "por iri suben en la listo",
   "keyboard_shortcuts.enter": "por malfermi mesaĝon",
   "keyboard_shortcuts.favourite": "por stelumi",
-  "keyboard_shortcuts.favourites": "to open favourites list",
-  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.favourites": "por malfermi la liston de stelumoj",
+  "keyboard_shortcuts.federated": "por malfermi la frataran tempolinion",
   "keyboard_shortcuts.heading": "Klavaraj mallongigoj",
-  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.home": "por malfermi la hejman tempolinion",
   "keyboard_shortcuts.hotkey": "Rapidklavo",
   "keyboard_shortcuts.legend": "por montri ĉi tiun noton",
-  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.local": "por malfermi la lokan tempolinion",
   "keyboard_shortcuts.mention": "por mencii la aŭtoron",
-  "keyboard_shortcuts.muted": "to open muted users list",
-  "keyboard_shortcuts.my_profile": "to open your profile",
-  "keyboard_shortcuts.notifications": "to open notifications column",
-  "keyboard_shortcuts.pinned": "to open pinned toots list",
+  "keyboard_shortcuts.muted": "por malfermi la liston de silentigitaj uzantoj",
+  "keyboard_shortcuts.my_profile": "por malfermi vian profilon",
+  "keyboard_shortcuts.notifications": "por malfermi la kolumnon de sciigoj",
+  "keyboard_shortcuts.pinned": "por malfermi la liston de alpinglitaj mesaĝoj",
   "keyboard_shortcuts.profile": "por malfermi la profilon de la aŭtoro",
   "keyboard_shortcuts.reply": "por respondi",
-  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.requests": "por malfermi la liston de petoj de sekvado",
   "keyboard_shortcuts.search": "por fokusigi la serĉilon",
-  "keyboard_shortcuts.start": "to open \"get started\" column",
+  "keyboard_shortcuts.start": "por malfermi la kolumnon «por komenci»",
   "keyboard_shortcuts.toggle_hidden": "por montri/kaŝi tekston malantaŭ enhava averto",
   "keyboard_shortcuts.toot": "por komenci tute novan mesaĝon",
   "keyboard_shortcuts.unfocus": "por malfokusigi la tekstujon aŭ la serĉilon",
@@ -195,10 +196,10 @@
   "missing_indicator.label": "Ne trovita",
   "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.apps": "Mobile apps",
+  "navigation_bar.apps": "Telefonaj aplikaĵoj",
   "navigation_bar.blocks": "Blokitaj uzantoj",
   "navigation_bar.community_timeline": "Loka tempolinio",
-  "navigation_bar.compose": "Compose new toot",
+  "navigation_bar.compose": "Redakti novan mesaĝon",
   "navigation_bar.direct": "Rektaj mesaĝoj",
   "navigation_bar.discover": "Esplori",
   "navigation_bar.domain_blocks": "Kaŝitaj domajnoj",
@@ -291,7 +292,7 @@
   "status.cancel_reblog_private": "Eksdiskonigi",
   "status.cannot_reblog": "Ĉi tiu mesaĝo ne diskonigeblas",
   "status.delete": "Forigi",
-  "status.detailed_status": "Detailed conversation view",
+  "status.detailed_status": "Detala konversacia vido",
   "status.direct": "Rekte mesaĝi @{name}",
   "status.embed": "Enkorpigi",
   "status.favourite": "Stelumi",
@@ -305,11 +306,11 @@
   "status.open": "Grandigi",
   "status.pin": "Alpingli profile",
   "status.pinned": "Alpinglita mesaĝo",
-  "status.read_more": "Read more",
+  "status.read_more": "Legi pli",
   "status.reblog": "Diskonigi",
   "status.reblog_private": "Diskonigi al la originala atentaro",
   "status.reblogged_by": "{name} diskonigis",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
+  "status.reblogs.empty": "Neniu ankoraŭ diskonigis ĉi tiun mesaĝon. Kiam iu faris ĝin, tiu aperos ĉi tie.",
   "status.redraft": "Forigi kaj reskribi",
   "status.reply": "Respondi",
   "status.replyAll": "Respondi al la fadeno",
@@ -321,11 +322,11 @@
   "status.show_less_all": "Malgrandigi ĉiujn",
   "status.show_more": "Grandigi",
   "status.show_more_all": "Grandigi ĉiujn",
-  "status.show_thread": "Show thread",
+  "status.show_thread": "Montri fadenon",
   "status.unmute_conversation": "Malsilentigi konversacion",
   "status.unpin": "Depingli de profilo",
-  "suggestions.dismiss": "Dismiss suggestion",
-  "suggestions.header": "You might be interested in…",
+  "suggestions.dismiss": "Forigi la proponon",
+  "suggestions.header": "Vi povus interesiĝi pri…",
   "tabs_bar.federated_timeline": "Fratara tempolinio",
   "tabs_bar.home": "Hejmo",
   "tabs_bar.local_timeline": "Loka tempolinio",
@@ -334,7 +335,7 @@
   "trends.count_by_accounts": "{count} {rawCount, pluraj, unu {person} alia(j) {people}} parolas",
   "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",
+  "upload_button.label": "Aldoni aŭdovidaĵon (JPEG, PNG, GIF, WebM, MP4, MOV)",
   "upload_form.description": "Priskribi por misvidantaj homoj",
   "upload_form.focus": "Stuci",
   "upload_form.undo": "Forigi",
diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json
index 7b8cfe3f3..e05d7e010 100644
--- a/app/javascript/mastodon/locales/es.json
+++ b/app/javascript/mastodon/locales/es.json
@@ -17,6 +17,7 @@
   "account.follows_you": "Te sigue",
   "account.hide_reblogs": "Ocultar retoots de @{name}",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Media",
   "account.mention": "Mencionar a @{name}",
   "account.moved_to": "{name} se ha mudado a:",
diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json
index 8ce3b9ba3..de4197604 100644
--- a/app/javascript/mastodon/locales/eu.json
+++ b/app/javascript/mastodon/locales/eu.json
@@ -1,5 +1,5 @@
 {
-  "account.add_or_remove_from_list": "Add or Remove from lists",
+  "account.add_or_remove_from_list": "Gehitu edo kendu zerrendetatik",
   "account.badges.bot": "Bot",
   "account.block": "Blokeatu @{name}",
   "account.block_domain": "Ezkutatu {domain} domeinuko guztia",
@@ -17,6 +17,7 @@
   "account.follows_you": "Jarraitzen dizu",
   "account.hide_reblogs": "Ezkutatu @{name}(r)en bultzadak",
   "account.link_verified_on": "Esteka honen jabetzaren egiaztaketa data: {date}",
+  "account.locked_info": "Kontu honen pribatutasun egoera blokeatuta gisa ezarri da. Jabeak eskuz erabakitzen du nork jarraitu diezaioken.",
   "account.media": "Media",
   "account.mention": "Aipatu @{name}",
   "account.moved_to": "{name} hona lekualdatu da:",
@@ -112,7 +113,7 @@
   "emoji_button.search_results": "Bilaketaren emaitzak",
   "emoji_button.symbols": "Sinboloak",
   "emoji_button.travel": "Bidaiak eta tokiak",
-  "empty_column.account_timeline": "No toots here!",
+  "empty_column.account_timeline": "Ez dago toot-ik hemen!",
   "empty_column.blocks": "Ez duzu erabiltzailerik blokeatu oraindik.",
   "empty_column.community": "Denbora-lerro lokala hutsik dago. Idatzi zerbait publikoki pilota biraka jartzeko!",
   "empty_column.direct": "Ez duzu mezu zuzenik oraindik. Baten bat bidali edo jasotzen duzunean, hemen agertuko da.",
@@ -138,12 +139,12 @@
   "getting_started.open_source_notice": "Mastodon software librea da. Ekarpenak egin ditzakezu edo akatsen berri eman GitHub bidez: {github}.",
   "getting_started.security": "Segurtasuna",
   "getting_started.terms": "Erabilera baldintzak",
-  "hashtag.column_header.tag_mode.all": "and {additional}",
-  "hashtag.column_header.tag_mode.any": "or {additional}",
-  "hashtag.column_header.tag_mode.none": "without {additional}",
-  "hashtag.column_settings.tag_mode.all": "All of these",
-  "hashtag.column_settings.tag_mode.any": "Any of these",
-  "hashtag.column_settings.tag_mode.none": "None of these",
+  "hashtag.column_header.tag_mode.all": "eta {osagarria}",
+  "hashtag.column_header.tag_mode.any": "edo {osagarria}",
+  "hashtag.column_header.tag_mode.none": "gabe {osagarria}",
+  "hashtag.column_settings.tag_mode.all": "Hauetako guztiak",
+  "hashtag.column_settings.tag_mode.any": "Hautako edozein",
+  "hashtag.column_settings.tag_mode.none": "Hauetako bat ere ez",
   "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
   "home.column_settings.basic": "Oinarrizkoa",
   "home.column_settings.show_reblogs": "Erakutsi bultzadak",
@@ -315,13 +316,13 @@
   "status.replyAll": "Erantzun harian",
   "status.report": "Salatu @{name}",
   "status.sensitive_toggle": "Egin klik ikusteko",
-  "status.sensitive_warning": "Eduki hunkigarria",
+  "status.sensitive_warning": "Kontuz: Eduki hunkigarria",
   "status.share": "Partekatu",
   "status.show_less": "Erakutsi gutxiago",
   "status.show_less_all": "Erakutsi denetarik gutxiago",
   "status.show_more": "Erakutsi gehiago",
   "status.show_more_all": "Erakutsi denetarik gehiago",
-  "status.show_thread": "Show thread",
+  "status.show_thread": "Erakutsi haria",
   "status.unmute_conversation": "Desmututu elkarrizketa",
   "status.unpin": "Desfinkatu profiletik",
   "suggestions.dismiss": "Errefusatu proposamena",
diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json
index befdb3804..4e00b4f2f 100644
--- a/app/javascript/mastodon/locales/fa.json
+++ b/app/javascript/mastodon/locales/fa.json
@@ -17,6 +17,7 @@
   "account.follows_you": "پیگیر شماست",
   "account.hide_reblogs": "پنهان کردن بازبوق‌های @{name}",
   "account.link_verified_on": "مالکیت این نشانی در تایخ {date} بررسی شد",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "عکس و ویدیو",
   "account.mention": "نام‌بردن از @{name}",
   "account.moved_to": "{name} منتقل شده است به:",
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
index bb1725533..a9de659e0 100644
--- a/app/javascript/mastodon/locales/fi.json
+++ b/app/javascript/mastodon/locales/fi.json
@@ -17,6 +17,7 @@
   "account.follows_you": "Seuraa sinua",
   "account.hide_reblogs": "Piilota buustaukset käyttäjältä @{name}",
   "account.link_verified_on": "Tämän linkin omistaja tarkistettiin {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Media",
   "account.mention": "Mainitse @{name}",
   "account.moved_to": "{name} on muuttanut instanssiin:",
diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json
index 85cafb18c..38fe07abd 100644
--- a/app/javascript/mastodon/locales/fr.json
+++ b/app/javascript/mastodon/locales/fr.json
@@ -1,5 +1,5 @@
 {
-  "account.add_or_remove_from_list": "Add or Remove from lists",
+  "account.add_or_remove_from_list": "Ajouter ou retirer des listes",
   "account.badges.bot": "Bot",
   "account.block": "Bloquer @{name}",
   "account.block_domain": "Tout masquer venant de {domain}",
@@ -17,6 +17,7 @@
   "account.follows_you": "Vous suit",
   "account.hide_reblogs": "Masquer les partages de @{name}",
   "account.link_verified_on": "La propriété de ce lien a été vérifiée le {date}",
+  "account.locked_info": "Ce compte est verrouillé. Son propriétaire approuve manuellement qui peut le ou la suivre.",
   "account.media": "Média",
   "account.mention": "Mentionner",
   "account.moved_to": "{name} a déménagé vers :",
@@ -112,7 +113,7 @@
   "emoji_button.search_results": "Résultats de la recherche",
   "emoji_button.symbols": "Symboles",
   "emoji_button.travel": "Lieux & Voyages",
-  "empty_column.account_timeline": "No toots here!",
+  "empty_column.account_timeline": "Aucun pouet ici !",
   "empty_column.blocks": "Vous n’avez bloqué aucun utilisateur pour le moment.",
   "empty_column.community": "Le fil public local est vide. Écrivez donc quelque chose pour le remplir !",
   "empty_column.direct": "Vous n’avez pas encore de messages directs. Lorsque vous en enverrez ou recevrez un, il s’affichera ici.",
@@ -138,12 +139,12 @@
   "getting_started.open_source_notice": "Mastodon est un logiciel libre. Vous pouvez contribuer et envoyer vos commentaires et rapports de bogues via {github} sur GitHub.",
   "getting_started.security": "Sécurité",
   "getting_started.terms": "Conditions d’utilisation",
-  "hashtag.column_header.tag_mode.all": "and {additional}",
-  "hashtag.column_header.tag_mode.any": "or {additional}",
-  "hashtag.column_header.tag_mode.none": "without {additional}",
-  "hashtag.column_settings.tag_mode.all": "All of these",
-  "hashtag.column_settings.tag_mode.any": "Any of these",
-  "hashtag.column_settings.tag_mode.none": "None of these",
+  "hashtag.column_header.tag_mode.all": "et {additional}",
+  "hashtag.column_header.tag_mode.any": "ou {additional}",
+  "hashtag.column_header.tag_mode.none": "sans {additional}",
+  "hashtag.column_settings.tag_mode.all": "Tous ces éléments",
+  "hashtag.column_settings.tag_mode.any": "Au moins un de ces éléments",
+  "hashtag.column_settings.tag_mode.none": "Aucun de ces éléments",
   "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
   "home.column_settings.basic": "Basique",
   "home.column_settings.show_reblogs": "Afficher les partages",
@@ -321,7 +322,7 @@
   "status.show_less_all": "Tout replier",
   "status.show_more": "Déplier",
   "status.show_more_all": "Tout déplier",
-  "status.show_thread": "Show thread",
+  "status.show_thread": "Afficher le fil",
   "status.unmute_conversation": "Ne plus masquer la conversation",
   "status.unpin": "Retirer du profil",
   "suggestions.dismiss": "Rejeter la suggestion",
diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json
index f2a8aec3a..02c27602f 100644
--- a/app/javascript/mastodon/locales/gl.json
+++ b/app/javascript/mastodon/locales/gl.json
@@ -1,5 +1,5 @@
 {
-  "account.add_or_remove_from_list": "Add or Remove from lists",
+  "account.add_or_remove_from_list": "Engadir ou Eliminar das listas",
   "account.badges.bot": "Bot",
   "account.block": "Bloquear @{name}",
   "account.block_domain": "Ocultar calquer contido de {domain}",
@@ -17,6 +17,7 @@
   "account.follows_you": "Séguena",
   "account.hide_reblogs": "Ocultar repeticións de @{name}",
   "account.link_verified_on": "A propiedade de esta ligazón foi comprobada en {date}",
+  "account.locked_info": "O estado da intimidade de esta conta estableceuse en pechado. A persoa dona da conta revisa quen pode seguila.",
   "account.media": "Medios",
   "account.mention": "Mencionar @{name}",
   "account.moved_to": "{name} marchou a:",
@@ -99,7 +100,7 @@
   "embed.instructions": "Copie o código inferior para incrustar no seu sitio web este estado.",
   "embed.preview": "Así será mostrado:",
   "emoji_button.activity": "Actividade",
-  "emoji_button.custom": "Personalizado",
+  "emoji_button.custom": "Persoalizado",
   "emoji_button.flags": "Marcas",
   "emoji_button.food": "Comida e Bebida",
   "emoji_button.label": "Insertar emoji",
@@ -112,7 +113,7 @@
   "emoji_button.search_results": "Resultados da busca",
   "emoji_button.symbols": "Símbolos",
   "emoji_button.travel": "Viaxes e Lugares",
-  "empty_column.account_timeline": "No toots here!",
+  "empty_column.account_timeline": "Sen toots por aquí!",
   "empty_column.blocks": "Non bloqueou ningunha usuaria polo de agora.",
   "empty_column.community": "A liña temporal local está baldeira. Escriba algo de xeito público para que rule!",
   "empty_column.direct": "Aínda non ten mensaxes directas. Cando envíe ou reciba unha, aparecerá aquí.",
@@ -138,12 +139,12 @@
   "getting_started.open_source_notice": "Mastodon é software de código aberto. Pode contribuír ou informar de fallos en GitHub en {github}.",
   "getting_started.security": "Seguridade",
   "getting_started.terms": "Termos do servizo",
-  "hashtag.column_header.tag_mode.all": "and {additional}",
-  "hashtag.column_header.tag_mode.any": "or {additional}",
-  "hashtag.column_header.tag_mode.none": "without {additional}",
-  "hashtag.column_settings.tag_mode.all": "All of these",
-  "hashtag.column_settings.tag_mode.any": "Any of these",
-  "hashtag.column_settings.tag_mode.none": "None of these",
+  "hashtag.column_header.tag_mode.all": "e {additional}",
+  "hashtag.column_header.tag_mode.any": "ou {additional}",
+  "hashtag.column_header.tag_mode.none": "sen {additional}",
+  "hashtag.column_settings.tag_mode.all": "Todos estos",
+  "hashtag.column_settings.tag_mode.any": "Calquera de estos",
+  "hashtag.column_settings.tag_mode.none": "Ningún de estos",
   "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
   "home.column_settings.basic": "Básico",
   "home.column_settings.show_reblogs": "Mostrar repeticións",
@@ -211,7 +212,7 @@
   "navigation_bar.lists": "Listas",
   "navigation_bar.logout": "Sair",
   "navigation_bar.mutes": "Usuarias acaladas",
-  "navigation_bar.personal": "Personal",
+  "navigation_bar.personal": "Persoal",
   "navigation_bar.pins": "Mensaxes fixadas",
   "navigation_bar.preferences": "Preferencias",
   "navigation_bar.public_timeline": "Liña temporal federada",
@@ -321,7 +322,7 @@
   "status.show_less_all": "Mostrar menos para todas",
   "status.show_more": "Mostrar máis",
   "status.show_more_all": "Mostrar máis para todas",
-  "status.show_thread": "Show thread",
+  "status.show_thread": "Mostrar fío",
   "status.unmute_conversation": "Non acalar a conversa",
   "status.unpin": "Despegar do perfil",
   "suggestions.dismiss": "Rexeitar suxestión",
diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json
index 3bcd825ae..9c891e2e8 100644
--- a/app/javascript/mastodon/locales/he.json
+++ b/app/javascript/mastodon/locales/he.json
@@ -17,6 +17,7 @@
   "account.follows_you": "במעקב אחריך",
   "account.hide_reblogs": "להסתיר הידהודים מאת @{name}",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "מדיה",
   "account.mention": "אזכור של @{name}",
   "account.moved_to": "החשבון {name} הועבר אל:",
diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json
index 33d2b3e37..ef6d482c1 100644
--- a/app/javascript/mastodon/locales/hr.json
+++ b/app/javascript/mastodon/locales/hr.json
@@ -17,6 +17,7 @@
   "account.follows_you": "te slijedi",
   "account.hide_reblogs": "Hide boosts from @{name}",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Media",
   "account.mention": "Spomeni @{name}",
   "account.moved_to": "{name} has moved to:",
diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json
index 51d65f98c..85a513904 100644
--- a/app/javascript/mastodon/locales/hu.json
+++ b/app/javascript/mastodon/locales/hu.json
@@ -17,6 +17,7 @@
   "account.follows_you": "Követnek téged",
   "account.hide_reblogs": "Rejtsd el a tülkölést @{name}-tól/től",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Média",
   "account.mention": "@{name} említése",
   "account.moved_to": "{name} átköltözött:",
diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json
index 76c96ff03..63a93b156 100644
--- a/app/javascript/mastodon/locales/hy.json
+++ b/app/javascript/mastodon/locales/hy.json
@@ -17,6 +17,7 @@
   "account.follows_you": "Հետեւում է քեզ",
   "account.hide_reblogs": "Թաքցնել @{name}֊ի տարածածները",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Մեդիա",
   "account.mention": "Նշել @{name}֊ին",
   "account.moved_to": "{name}֊ը տեղափոխվել է՝",
diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json
index 5280d96c2..eed96f869 100644
--- a/app/javascript/mastodon/locales/id.json
+++ b/app/javascript/mastodon/locales/id.json
@@ -17,6 +17,7 @@
   "account.follows_you": "Mengikuti anda",
   "account.hide_reblogs": "Sembunyikan boosts dari @{name}",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Media",
   "account.mention": "Balasan @{name}",
   "account.moved_to": "{name} telah pindah ke:",
diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json
index a8a162d82..a31cc12cb 100644
--- a/app/javascript/mastodon/locales/io.json
+++ b/app/javascript/mastodon/locales/io.json
@@ -17,6 +17,7 @@
   "account.follows_you": "Sequas tu",
   "account.hide_reblogs": "Hide boosts from @{name}",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Media",
   "account.mention": "Mencionar @{name}",
   "account.moved_to": "{name} has moved to:",
diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json
index 7907909c5..2c1b59be9 100644
--- a/app/javascript/mastodon/locales/it.json
+++ b/app/javascript/mastodon/locales/it.json
@@ -1,5 +1,5 @@
 {
-  "account.add_or_remove_from_list": "Add or Remove from lists",
+  "account.add_or_remove_from_list": "Aggiungi o togli dalle liste",
   "account.badges.bot": "Bot",
   "account.block": "Blocca @{name}",
   "account.block_domain": "Nascondi tutto da {domain}",
@@ -17,6 +17,7 @@
   "account.follows_you": "Ti segue",
   "account.hide_reblogs": "Nascondi condivisioni da @{name}",
   "account.link_verified_on": "La proprietà di questo link è stata controllata il {date}",
+  "account.locked_info": "Il livello di privacy di questo account è impostato a \"bloccato\". Il proprietario esamina manualmente le richieste di seguirlo.",
   "account.media": "Media",
   "account.mention": "Menziona @{name}",
   "account.moved_to": "{name} si è trasferito su:",
@@ -112,7 +113,7 @@
   "emoji_button.search_results": "Risultati della ricerca",
   "emoji_button.symbols": "Simboli",
   "emoji_button.travel": "Viaggi e luoghi",
-  "empty_column.account_timeline": "No toots here!",
+  "empty_column.account_timeline": "Non ci sono toot qui!",
   "empty_column.blocks": "Non hai ancora bloccato nessun utente.",
   "empty_column.community": "La timeline locale è vuota. Condividi qualcosa pubblicamente per dare inizio alla festa!",
   "empty_column.direct": "Non hai ancora nessun messaggio diretto. Quando ne manderai o riceverai qualcuno, apparirà qui.",
@@ -138,12 +139,12 @@
   "getting_started.open_source_notice": "Mastodon è un software open source. Puoi contribuire o segnalare errori su GitHub all'indirizzo {github}.",
   "getting_started.security": "Sicurezza",
   "getting_started.terms": "Condizioni del servizio",
-  "hashtag.column_header.tag_mode.all": "and {additional}",
-  "hashtag.column_header.tag_mode.any": "or {additional}",
-  "hashtag.column_header.tag_mode.none": "without {additional}",
-  "hashtag.column_settings.tag_mode.all": "All of these",
-  "hashtag.column_settings.tag_mode.any": "Any of these",
-  "hashtag.column_settings.tag_mode.none": "None of these",
+  "hashtag.column_header.tag_mode.all": "e {additional}",
+  "hashtag.column_header.tag_mode.any": "o {additional}",
+  "hashtag.column_header.tag_mode.none": "senza {additional}",
+  "hashtag.column_settings.tag_mode.all": "Tutti questi",
+  "hashtag.column_settings.tag_mode.any": "Uno o più di questi",
+  "hashtag.column_settings.tag_mode.none": "Nessuno di questi",
   "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
   "home.column_settings.basic": "Semplice",
   "home.column_settings.show_reblogs": "Mostra post condivisi",
@@ -321,7 +322,7 @@
   "status.show_less_all": "Mostra meno per tutti",
   "status.show_more": "Mostra di più",
   "status.show_more_all": "Mostra di più per tutti",
-  "status.show_thread": "Show thread",
+  "status.show_thread": "Mostra thread",
   "status.unmute_conversation": "Annulla silenzia conversazione",
   "status.unpin": "Non fissare in cima al profilo",
   "suggestions.dismiss": "Elimina suggerimento",
diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json
index 06fe71e6b..4e8862201 100644
--- a/app/javascript/mastodon/locales/ja.json
+++ b/app/javascript/mastodon/locales/ja.json
@@ -17,6 +17,7 @@
   "account.follows_you": "フォローされています",
   "account.hide_reblogs": "@{name}さんからのブーストを非表示",
   "account.link_verified_on": "このリンクの所有権は{date}に確認されました",
+  "account.locked_info": "このアカウントは承認制に設定されています。フォローするには所有者の確認が必要です。",
   "account.media": "メディア",
   "account.mention": "@{name}さんにトゥート",
   "account.moved_to": "{name}さんは引っ越しました:",
@@ -139,7 +140,7 @@
   "getting_started.find_friends": "Twitterの友達を探す",
   "getting_started.heading": "スタート",
   "getting_started.invite": "招待",
-  "getting_started.open_source_notice": "Mastodonはオープンソースソフトウェアです。誰でもGitHub({github})から開発に参加したり、問題を報告したりできます。",
+  "getting_started.open_source_notice": "Mastodonはオープンソースソフトウェアです。誰でもGitHub ( {github} ) から開発に参加したり、問題を報告したりできます。",
   "getting_started.security": "セキュリティ",
   "getting_started.terms": "プライバシーポリシー",
   "hashtag.column_header.tag_mode.all": "と {additional}",
diff --git a/app/javascript/mastodon/locales/ka.json b/app/javascript/mastodon/locales/ka.json
index cbb639136..07129594f 100644
--- a/app/javascript/mastodon/locales/ka.json
+++ b/app/javascript/mastodon/locales/ka.json
@@ -17,6 +17,7 @@
   "account.follows_you": "მოგყვებათ",
   "account.hide_reblogs": "დაიმალოს ბუსტები @{name}-სგან",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "მედია",
   "account.mention": "ასახელეთ @{name}",
   "account.moved_to": "{name} გადავიდა:",
diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json
index cd481b9e5..b445823e7 100644
--- a/app/javascript/mastodon/locales/ko.json
+++ b/app/javascript/mastodon/locales/ko.json
@@ -17,6 +17,7 @@
   "account.follows_you": "날 팔로우합니다",
   "account.hide_reblogs": "@{name}의 부스트를 숨기기",
   "account.link_verified_on": "{date}에 이 링크의 소유권이 확인 됨",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "미디어",
   "account.mention": "@{name}에게 글쓰기",
   "account.moved_to": "{name}는 계정을 이동했습니다:",
diff --git a/app/javascript/mastodon/locales/ms.json b/app/javascript/mastodon/locales/ms.json
index 3e243b743..d5176b01d 100644
--- a/app/javascript/mastodon/locales/ms.json
+++ b/app/javascript/mastodon/locales/ms.json
@@ -17,6 +17,7 @@
   "account.follows_you": "Follows you",
   "account.hide_reblogs": "Hide boosts from @{name}",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Media",
   "account.mention": "Mention @{name}",
   "account.moved_to": "{name} has moved to:",
diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json
index d8dea93d6..0af479355 100644
--- a/app/javascript/mastodon/locales/nl.json
+++ b/app/javascript/mastodon/locales/nl.json
@@ -1,5 +1,5 @@
 {
-  "account.add_or_remove_from_list": "Add or Remove from lists",
+  "account.add_or_remove_from_list": "Toevoegen of verwijderen vanuit lijsten",
   "account.badges.bot": "Bot",
   "account.block": "Blokkeer @{name}",
   "account.block_domain": "Verberg alles van {domain}",
@@ -17,6 +17,7 @@
   "account.follows_you": "Volgt jou",
   "account.hide_reblogs": "Verberg boosts van @{name}",
   "account.link_verified_on": "Eigendom van deze link is gecontroleerd op {date}",
+  "account.locked_info": "De privacystatus van dit account is op besloten gezet. De eigenaar bepaalt handmatig wie hen kan volgen.",
   "account.media": "Media",
   "account.mention": "Vermeld @{name}",
   "account.moved_to": "{name} is verhuisd naar:",
@@ -112,7 +113,7 @@
   "emoji_button.search_results": "Zoekresultaten",
   "emoji_button.symbols": "Symbolen",
   "emoji_button.travel": "Reizen en plekken",
-  "empty_column.account_timeline": "No toots here!",
+  "empty_column.account_timeline": "Hier zijn geen toots!",
   "empty_column.blocks": "Jij hebt nog geen enkele gebruiker geblokkeerd.",
   "empty_column.community": "De lokale tijdlijn is nog leeg. Toot iets in het openbaar om de bal aan het rollen te krijgen!",
   "empty_column.direct": "Je hebt nog geen directe berichten. Wanneer je er een verzend of ontvangt, zijn deze hier te zien.",
@@ -138,12 +139,12 @@
   "getting_started.open_source_notice": "Mastodon is vrije software. Je kunt bijdragen of problemen melden op GitHub via {github}.",
   "getting_started.security": "Beveiliging",
   "getting_started.terms": "Voorwaarden",
-  "hashtag.column_header.tag_mode.all": "and {additional}",
-  "hashtag.column_header.tag_mode.any": "or {additional}",
-  "hashtag.column_header.tag_mode.none": "without {additional}",
-  "hashtag.column_settings.tag_mode.all": "All of these",
-  "hashtag.column_settings.tag_mode.any": "Any of these",
-  "hashtag.column_settings.tag_mode.none": "None of these",
+  "hashtag.column_header.tag_mode.all": "en {additional}",
+  "hashtag.column_header.tag_mode.any": "of {additional}",
+  "hashtag.column_header.tag_mode.none": "zonder {additional}",
+  "hashtag.column_settings.tag_mode.all": "Allemaal",
+  "hashtag.column_settings.tag_mode.any": "Een van deze",
+  "hashtag.column_settings.tag_mode.none": "Geen van deze",
   "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
   "home.column_settings.basic": "Algemeen",
   "home.column_settings.show_reblogs": "Boosts tonen",
@@ -209,7 +210,7 @@
   "navigation_bar.info": "Over deze server",
   "navigation_bar.keyboard_shortcuts": "Sneltoetsen",
   "navigation_bar.lists": "Lijsten",
-  "navigation_bar.logout": "Afmelden",
+  "navigation_bar.logout": "Uitloggen",
   "navigation_bar.mutes": "Genegeerde gebruikers",
   "navigation_bar.personal": "Persoonlijk",
   "navigation_bar.pins": "Vastgezette toots",
@@ -321,7 +322,7 @@
   "status.show_less_all": "Alles minder tonen",
   "status.show_more": "Meer tonen",
   "status.show_more_all": "Alles meer tonen",
-  "status.show_thread": "Show thread",
+  "status.show_thread": "Gesprek tonen",
   "status.unmute_conversation": "Gesprek niet langer negeren",
   "status.unpin": "Van profielpagina losmaken",
   "suggestions.dismiss": "Suggestie verwerpen",
diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json
index ea4e76de4..39c236564 100644
--- a/app/javascript/mastodon/locales/no.json
+++ b/app/javascript/mastodon/locales/no.json
@@ -17,6 +17,7 @@
   "account.follows_you": "Følger deg",
   "account.hide_reblogs": "Skjul fremhevinger fra @{name}",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Media",
   "account.mention": "Nevn @{name}",
   "account.moved_to": "{name} har flyttet til:",
diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json
index a94e0336d..072781e77 100644
--- a/app/javascript/mastodon/locales/oc.json
+++ b/app/javascript/mastodon/locales/oc.json
@@ -150,32 +150,32 @@
   "home.column_settings.show_reblogs": "Mostrar los partatges",
   "home.column_settings.show_replies": "Mostrar las responsas",
   "keyboard_shortcuts.back": "anar enrèire",
-  "keyboard_shortcuts.blocked": "per dobrir la lista d’utilizaires blocats",
+  "keyboard_shortcuts.blocked": "dobrir la lista d’utilizaires blocats",
   "keyboard_shortcuts.boost": "partejar",
   "keyboard_shortcuts.column": "centrar un estatut a una colomna",
   "keyboard_shortcuts.compose": "anar al camp tèxte",
-  "keyboard_shortcuts.description": "Descripcion",
-  "keyboard_shortcuts.direct": "per dobrir la colomna de messatges dirèctes",
+  "keyboard_shortcuts.description": "descripcion",
+  "keyboard_shortcuts.direct": "dobrir la colomna de messatges dirèctes",
   "keyboard_shortcuts.down": "far davalar dins la lista",
   "keyboard_shortcuts.enter": "dobrir los estatuts",
   "keyboard_shortcuts.favourite": "apondre als favorits",
-  "keyboard_shortcuts.favourites": "per dobrir la lista de favorits",
-  "keyboard_shortcuts.federated": "per dobrir lo flux public global",
+  "keyboard_shortcuts.favourites": "dobrir la lista de favorits",
+  "keyboard_shortcuts.federated": "dobrir lo flux public global",
   "keyboard_shortcuts.heading": "Acorchis clavièr",
-  "keyboard_shortcuts.home": "per dobrir lo flux public local",
+  "keyboard_shortcuts.home": "dobrir lo flux public local",
   "keyboard_shortcuts.hotkey": "Acorchis",
   "keyboard_shortcuts.legend": "mostrar aquesta legenda",
-  "keyboard_shortcuts.local": "per dobrir lo flux public local",
+  "keyboard_shortcuts.local": "dobrir lo flux public local",
   "keyboard_shortcuts.mention": "mencionar l’autor",
-  "keyboard_shortcuts.muted": "per dobrir la lista dels utilizaires silenciats",
-  "keyboard_shortcuts.my_profile": "per dobrir vòstre perfil",
-  "keyboard_shortcuts.notifications": "per dobrir la colomna de notificacions",
-  "keyboard_shortcuts.pinned": "per dobrir la lista dels tuts penjats",
-  "keyboard_shortcuts.profile": "per dobrir lo perfil de l’autor",
+  "keyboard_shortcuts.muted": "dobrir la lista dels utilizaires silenciats",
+  "keyboard_shortcuts.my_profile": "dobrir vòstre perfil",
+  "keyboard_shortcuts.notifications": "dobrir la colomna de notificacions",
+  "keyboard_shortcuts.pinned": "dobrir la lista dels tuts penjats",
+  "keyboard_shortcuts.profile": "dobrir lo perfil de l’autor",
   "keyboard_shortcuts.reply": "respondre",
-  "keyboard_shortcuts.requests": "per dorbir la lista de demanda d’abonament",
+  "keyboard_shortcuts.requests": "dorbir la lista de demanda d’abonament",
   "keyboard_shortcuts.search": "anar a la recèrca",
-  "keyboard_shortcuts.start": "per dobrir la colomna «Per començar»",
+  "keyboard_shortcuts.start": "dobrir la colomna « Per començar »",
   "keyboard_shortcuts.toggle_hidden": "mostrar/amagar lo tèxte dels avertiments",
   "keyboard_shortcuts.toot": "començar un estatut tot novèl",
   "keyboard_shortcuts.unfocus": "quitar lo camp tèxte/de recèrca",
diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json
index 61811c53d..31c581aad 100644
--- a/app/javascript/mastodon/locales/pt-BR.json
+++ b/app/javascript/mastodon/locales/pt-BR.json
@@ -1,5 +1,5 @@
 {
-  "account.add_or_remove_from_list": "Add or Remove from lists",
+  "account.add_or_remove_from_list": "Adicionar ou remover de listas",
   "account.badges.bot": "Robô",
   "account.block": "Bloquear @{name}",
   "account.block_domain": "Esconder tudo de {domain}",
@@ -17,6 +17,7 @@
   "account.follows_you": "Segue você",
   "account.hide_reblogs": "Esconder compartilhamentos de @{name}",
   "account.link_verified_on": "A posse desse link foi verificada em {date}",
+  "account.locked_info": "Essa conta está trancada. Se você a seguir sua solicitação será revisada manualmente.",
   "account.media": "Mídia",
   "account.mention": "Mencionar @{name}",
   "account.moved_to": "{name} se mudou para:",
@@ -112,7 +113,7 @@
   "emoji_button.search_results": "Resultados da busca",
   "emoji_button.symbols": "Símbolos",
   "emoji_button.travel": "Viagens & Lugares",
-  "empty_column.account_timeline": "No toots here!",
+  "empty_column.account_timeline": "Não há toots aqui!",
   "empty_column.blocks": "Você ainda não bloqueou nenhum usuário.",
   "empty_column.community": "A timeline local está vazia. Escreva algo publicamente para começar!",
   "empty_column.direct": "Você não tem nenhuma mensagem direta ainda. Quando você enviar ou receber uma, as mensagens aparecerão por aqui.",
@@ -138,12 +139,12 @@
   "getting_started.open_source_notice": "Mastodon é um software de código aberto. Você pode contribuir ou reportar problemas na página do GitHub do projeto: {github}.",
   "getting_started.security": "Segurança",
   "getting_started.terms": "Termos de serviço",
-  "hashtag.column_header.tag_mode.all": "and {additional}",
-  "hashtag.column_header.tag_mode.any": "or {additional}",
-  "hashtag.column_header.tag_mode.none": "without {additional}",
-  "hashtag.column_settings.tag_mode.all": "All of these",
-  "hashtag.column_settings.tag_mode.any": "Any of these",
-  "hashtag.column_settings.tag_mode.none": "None of these",
+  "hashtag.column_header.tag_mode.all": "e {additional}",
+  "hashtag.column_header.tag_mode.any": "ou {additional}",
+  "hashtag.column_header.tag_mode.none": "sem {additional}",
+  "hashtag.column_settings.tag_mode.all": "Todas essas",
+  "hashtag.column_settings.tag_mode.any": "Qualquer uma dessas",
+  "hashtag.column_settings.tag_mode.none": "Nenhuma dessas",
   "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
   "home.column_settings.basic": "Básico",
   "home.column_settings.show_reblogs": "Mostrar compartilhamentos",
@@ -321,11 +322,11 @@
   "status.show_less_all": "Mostrar menos para todas as mensagens",
   "status.show_more": "Mostrar mais",
   "status.show_more_all": "Mostrar mais para todas as mensagens",
-  "status.show_thread": "Show thread",
+  "status.show_thread": "Mostrar sequência",
   "status.unmute_conversation": "Desativar silêncio desta conversa",
   "status.unpin": "Desafixar do perfil",
-  "suggestions.dismiss": "Dismiss suggestion",
-  "suggestions.header": "You might be interested in…",
+  "suggestions.dismiss": "Ignorar a sugestão",
+  "suggestions.header": "Você pode se interessar por…",
   "tabs_bar.federated_timeline": "Global",
   "tabs_bar.home": "Página inicial",
   "tabs_bar.local_timeline": "Local",
diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json
index c03001080..cf56df025 100644
--- a/app/javascript/mastodon/locales/pt.json
+++ b/app/javascript/mastodon/locales/pt.json
@@ -17,6 +17,7 @@
   "account.follows_you": "É teu seguidor",
   "account.hide_reblogs": "Esconder partilhas de @{name}",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Media",
   "account.mention": "Mencionar @{name}",
   "account.moved_to": "{name} mudou a sua conta para:",
diff --git a/app/javascript/mastodon/locales/ro.json b/app/javascript/mastodon/locales/ro.json
index fc196e820..d262b57b7 100644
--- a/app/javascript/mastodon/locales/ro.json
+++ b/app/javascript/mastodon/locales/ro.json
@@ -17,6 +17,7 @@
   "account.follows_you": "Te urmărește",
   "account.hide_reblogs": "Ascunde redistribuirile de la @{name}",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Media",
   "account.mention": "Menționează @{name}",
   "account.moved_to": "{name} a fost mutat la:",
diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json
index 3e2979c1b..b1eb76afb 100644
--- a/app/javascript/mastodon/locales/ru.json
+++ b/app/javascript/mastodon/locales/ru.json
@@ -17,6 +17,7 @@
   "account.follows_you": "Подписан(а) на Вас",
   "account.hide_reblogs": "Скрыть продвижения от @{name}",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Медиа",
   "account.mention": "Упомянуть",
   "account.moved_to": "Ищите {name} здесь:",
diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json
index e03cdef89..7b14118a3 100644
--- a/app/javascript/mastodon/locales/sk.json
+++ b/app/javascript/mastodon/locales/sk.json
@@ -1,5 +1,5 @@
 {
-  "account.add_or_remove_from_list": "Add or Remove from lists",
+  "account.add_or_remove_from_list": "Pridaj, alebo odstráň zo zoznamov",
   "account.badges.bot": "Bot",
   "account.block": "Blokuj @{name}",
   "account.block_domain": "Ukry všetko z {domain}",
@@ -13,10 +13,11 @@
   "account.followers": "Sledujúci",
   "account.followers.empty": "Tohto užívateľa ešte nikto nenásleduje.",
   "account.follows": "Následuje",
-  "account.follows.empty": "Tento užívateľ ešte nikoho nenásleduje.",
+  "account.follows.empty": "Tento užívateľ tu ešte nikoho nenásleduje.",
   "account.follows_you": "Následuje ťa",
   "account.hide_reblogs": "Skryť povýšenia od @{name}",
   "account.link_verified_on": "Vlastníctvo tohto odkazu bolo skontrolované {date}",
+  "account.locked_info": "Stav súkromia pre tento účet je nastavený na zamknutý. Jeho vlastník sám prehodnocuje, kto ho môže sledovať.",
   "account.media": "Médiá",
   "account.mention": "Spomeň @{name}",
   "account.moved_to": "{name} sa presunul/a na:",
@@ -24,7 +25,7 @@
   "account.mute_notifications": "Stĺmiť oboznámenia od @{name}",
   "account.muted": "Utíšený/á",
   "account.posts": "Hlášky",
-  "account.posts_with_replies": "Príspevky s odpoveďami",
+  "account.posts_with_replies": "Hlášky s odpoveďami",
   "account.report": "Nahlás @{name}",
   "account.requested": "Čaká na schválenie. Kliknite pre zrušenie žiadosti",
   "account.share": "Zdieľať @{name} profil",
@@ -55,7 +56,7 @@
   "column.lists": "Zoznamy",
   "column.mutes": "Ignorovaní užívatelia",
   "column.notifications": "Oboznámenia",
-  "column.pins": "Pripnuté príspevky",
+  "column.pins": "Pripnuté hlášky",
   "column.public": "Federovaná časová os",
   "column_back_button.label": "Späť",
   "column_header.hide_settings": "Skryť nastavenia",
@@ -112,7 +113,7 @@
   "emoji_button.search_results": "Nájdené",
   "emoji_button.symbols": "Symboly",
   "emoji_button.travel": "Cestovanie a miesta",
-  "empty_column.account_timeline": "No toots here!",
+  "empty_column.account_timeline": "Niesú tu žiadne príspevky!",
   "empty_column.blocks": "Ešte si nikoho nezablokoval/a.",
   "empty_column.community": "Lokálna časová os je prázdna. Napíšte niečo, aby sa to tu začalo hýbať!",
   "empty_column.direct": "Ešte nemáš žiadne súkromné správy. Keď nejakú pošleš, alebo dostaneš, ukáže sa tu.",
@@ -138,12 +139,12 @@
   "getting_started.open_source_notice": "Mastodon je softvér s otvoreným kódom. Nahlásiť chyby, alebo prispievať môžeš na GitHube v {github}.",
   "getting_started.security": "Zabezpečenie",
   "getting_started.terms": "Podmienky prevozu",
-  "hashtag.column_header.tag_mode.all": "and {additional}",
-  "hashtag.column_header.tag_mode.any": "or {additional}",
-  "hashtag.column_header.tag_mode.none": "without {additional}",
-  "hashtag.column_settings.tag_mode.all": "All of these",
-  "hashtag.column_settings.tag_mode.any": "Any of these",
-  "hashtag.column_settings.tag_mode.none": "None of these",
+  "hashtag.column_header.tag_mode.all": "a {additional}",
+  "hashtag.column_header.tag_mode.any": "alebo {additional}",
+  "hashtag.column_header.tag_mode.none": "bez {additional}",
+  "hashtag.column_settings.tag_mode.all": "Všetky tieto",
+  "hashtag.column_settings.tag_mode.any": "Hociktorý z týchto",
+  "hashtag.column_settings.tag_mode.none": "Žiaden z týchto",
   "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
   "home.column_settings.basic": "Základné",
   "home.column_settings.show_reblogs": "Zobraziť povýšené",
@@ -193,8 +194,8 @@
   "loading_indicator.label": "Načítam...",
   "media_gallery.toggle_visible": "Zapnúť/Vypnúť viditeľnosť",
   "missing_indicator.label": "Nenájdené",
-  "missing_indicator.sublabel": "Tento zdroj sa nepodarilo nájsť",
-  "mute_modal.hide_notifications": "Skryť notifikácie od tohoto užívateľa?",
+  "missing_indicator.sublabel": "Tento zdroj sa ešte nepodarilo nájsť",
+  "mute_modal.hide_notifications": "Skryť oboznámenia od tohoto užívateľa?",
   "navigation_bar.apps": "Mobilné aplikácie",
   "navigation_bar.blocks": "Blokovaní užívatelia",
   "navigation_bar.community_timeline": "Lokálna časová os",
@@ -233,7 +234,7 @@
   "notifications.group": "{count} oznámenia",
   "onboarding.done": "Koniec",
   "onboarding.next": "Ďalej",
-  "onboarding.page_five.public_timelines": "Lokálna časová os zobrazuje verejné správy od všetkých na {domain}. Federovaná časová os zobrazuje verejné správy od všetkých tých, čo následujú užívatrľov {domain} z iných serverov. Tieto sú takzvané Verejné Časové Osi, výborná možnosť ako nájsť a spoznať nových ľudí.",
+  "onboarding.page_five.public_timelines": "Lokálna časová os zobrazuje verejné správy od všetkých na {domain}. Federovaná časová os zobrazuje verejné správy od všetkých tých, čo následujú užívateľov {domain} z iných serverov. Tieto sú takzvané Verejné Časové Osi, výborná možnosť ako nájsť a spoznať nových ľudí.",
   "onboarding.page_four.home": "Domovská časová os zobrazí správy od ľudí ktorých sledujete.",
   "onboarding.page_four.notifications": "Stĺpec s notifikáciami zobrazí keď budete s niekým komunikovať.",
   "onboarding.page_one.federation": "Mastodon je sieť nezávislých serverov, spojením ktorých vzniká jedna veľká federovaná sociálna sieť.",
@@ -284,8 +285,8 @@
   "search_popout.tips.user": "používateľ",
   "search_results.accounts": "Ľudia",
   "search_results.hashtags": "Haštagy",
-  "search_results.statuses": "Príspevky",
-  "search_results.total": "{count, number} {count, plural, jeden {výsledok} ostatné {výsledky}}",
+  "search_results.statuses": "Hlášky",
+  "search_results.total": "{count, number} {count, plural, one {výsledok} many {výsledkov} other {výsledky}}",
   "standalone.public_title": "Náhľad dovnútra...",
   "status.block": "Blokovať @{name}",
   "status.cancel_reblog_private": "Nezdieľaj",
@@ -321,9 +322,9 @@
   "status.show_less_all": "Všetkým ukáž menej",
   "status.show_more": "Ukáž viac",
   "status.show_more_all": "Všetkým ukáž viac",
-  "status.show_thread": "Show thread",
-  "status.unmute_conversation": "Prestať ignorovať konverzáciu",
-  "status.unpin": "Odopnúť z profilu",
+  "status.show_thread": "Ukáž diskusné vlákno",
+  "status.unmute_conversation": "Prestaň ignorovať konverzáciu",
+  "status.unpin": "Odopni z profilu",
   "suggestions.dismiss": "Zavrhni návrh",
   "suggestions.header": "Mohlo by ťa zaujímať…",
   "tabs_bar.federated_timeline": "Federovaná",
@@ -331,7 +332,7 @@
   "tabs_bar.local_timeline": "Lokálna",
   "tabs_bar.notifications": "Notifikácie",
   "tabs_bar.search": "Hľadaj",
-  "trends.count_by_accounts": "{count} {rawCount, viacerí, jeden {person} iní {people}} diskutujú",
+  "trends.count_by_accounts": "{count} {rawCount, plural, one {človek vraví} other {ľudia vravia}}",
   "ui.beforeunload": "Čo máš rozpísané sa stratí, ak opustíš Mastodon.",
   "upload_area.title": "Pretiahni a pusť pre nahratie",
   "upload_button.label": "Pridať médiálny súbor (JPEG, PNG, GIF, WebM, MP4, MOV)",
diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json
index 080ab8ffc..b85769f96 100644
--- a/app/javascript/mastodon/locales/sl.json
+++ b/app/javascript/mastodon/locales/sl.json
@@ -17,6 +17,7 @@
   "account.follows_you": "Ti sledi",
   "account.hide_reblogs": "Skrij sunke od @{name}",
   "account.link_verified_on": "Lastništvo te povezave je bilo preverjeno {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Mediji",
   "account.mention": "Omeni @{name}",
   "account.moved_to": "{name} se je premaknil na:",
diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json
index feaeb95c1..6bc84adc3 100644
--- a/app/javascript/mastodon/locales/sr-Latn.json
+++ b/app/javascript/mastodon/locales/sr-Latn.json
@@ -17,6 +17,7 @@
   "account.follows_you": "Prati Vas",
   "account.hide_reblogs": "Sakrij podrške koje daje korisnika @{name}",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Mediji",
   "account.mention": "Pomeni korisnika @{name}",
   "account.moved_to": "{name} se pomerio na:",
diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json
index 7e3c3f213..a6dc1fc06 100644
--- a/app/javascript/mastodon/locales/sr.json
+++ b/app/javascript/mastodon/locales/sr.json
@@ -17,6 +17,7 @@
   "account.follows_you": "Прати Вас",
   "account.hide_reblogs": "Сакриј подршке које даје корисника @{name}",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Медији",
   "account.mention": "Помени корисника @{name}",
   "account.moved_to": "{name} се померио на:",
diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json
index 6910b181d..940f49c78 100644
--- a/app/javascript/mastodon/locales/sv.json
+++ b/app/javascript/mastodon/locales/sv.json
@@ -17,6 +17,7 @@
   "account.follows_you": "Följer dig",
   "account.hide_reblogs": "Dölj knuffar från @{name}",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Media",
   "account.mention": "Nämna @{name}",
   "account.moved_to": "{name} har flyttat till:",
diff --git a/app/javascript/mastodon/locales/ta.json b/app/javascript/mastodon/locales/ta.json
index 3e243b743..d5176b01d 100644
--- a/app/javascript/mastodon/locales/ta.json
+++ b/app/javascript/mastodon/locales/ta.json
@@ -17,6 +17,7 @@
   "account.follows_you": "Follows you",
   "account.hide_reblogs": "Hide boosts from @{name}",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Media",
   "account.mention": "Mention @{name}",
   "account.moved_to": "{name} has moved to:",
diff --git a/app/javascript/mastodon/locales/te.json b/app/javascript/mastodon/locales/te.json
index e5cfe4240..86616ff42 100644
--- a/app/javascript/mastodon/locales/te.json
+++ b/app/javascript/mastodon/locales/te.json
@@ -17,6 +17,7 @@
   "account.follows_you": "మిమ్మల్ని అనుసరిస్తున్నారు",
   "account.hide_reblogs": "@{name} నుంచి బూస్ట్ లను దాచిపెట్టు",
   "account.link_verified_on": "ఈ లంకె యొక్క యాజమాన్యం {date}న పరీక్షించబడింది",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "మీడియా",
   "account.mention": "@{name}ను ప్రస్తావించు",
   "account.moved_to": "{name} ఇక్కడికి మారారు:",
diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json
index a85cc63dc..92dd29871 100644
--- a/app/javascript/mastodon/locales/th.json
+++ b/app/javascript/mastodon/locales/th.json
@@ -17,6 +17,7 @@
   "account.follows_you": "Follows you",
   "account.hide_reblogs": "Hide boosts from @{name}",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Media",
   "account.mention": "Mention @{name}",
   "account.moved_to": "{name} has moved to:",
diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json
index e1d0f33c4..64a6204aa 100644
--- a/app/javascript/mastodon/locales/tr.json
+++ b/app/javascript/mastodon/locales/tr.json
@@ -17,6 +17,7 @@
   "account.follows_you": "Seni takip ediyor",
   "account.hide_reblogs": "Hide boosts from @{name}",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Media",
   "account.mention": "Bahset @{name}",
   "account.moved_to": "{name} has moved to:",
diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json
index 08c30833b..c75940c25 100644
--- a/app/javascript/mastodon/locales/uk.json
+++ b/app/javascript/mastodon/locales/uk.json
@@ -17,6 +17,7 @@
   "account.follows_you": "Підписаний(-а) на Вас",
   "account.hide_reblogs": "Сховати передмухи від @{name}",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "Медіа",
   "account.mention": "Згадати @{name}",
   "account.moved_to": "{name} переїхав на:",
diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json
index e24910153..da5cf4798 100644
--- a/app/javascript/mastodon/locales/zh-CN.json
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -17,6 +17,7 @@
   "account.follows_you": "关注了你",
   "account.hide_reblogs": "隐藏来自 @{name} 的转嘟",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "媒体",
   "account.mention": "提及 @{name}",
   "account.moved_to": "{name} 已经迁移到:",
diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json
index 7e1d4c73b..16e803d92 100644
--- a/app/javascript/mastodon/locales/zh-HK.json
+++ b/app/javascript/mastodon/locales/zh-HK.json
@@ -17,6 +17,7 @@
   "account.follows_you": "關注你",
   "account.hide_reblogs": "隱藏 @{name} 的轉推",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "媒體",
   "account.mention": "提及 @{name}",
   "account.moved_to": "{name} 已經遷移到:",
diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json
index 4261c9345..d2256c259 100644
--- a/app/javascript/mastodon/locales/zh-TW.json
+++ b/app/javascript/mastodon/locales/zh-TW.json
@@ -17,6 +17,7 @@
   "account.follows_you": "關注你",
   "account.hide_reblogs": "隱藏來自 @{name} 的轉推",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
   "account.media": "媒體",
   "account.mention": "提到 @{name}",
   "account.moved_to": "{name} 已經移至:",
diff --git a/app/javascript/mastodon/reducers/timelines.js b/app/javascript/mastodon/reducers/timelines.js
index 664d65151..beab2ea03 100644
--- a/app/javascript/mastodon/reducers/timelines.js
+++ b/app/javascript/mastodon/reducers/timelines.js
@@ -26,10 +26,10 @@ const initialTimeline = ImmutableMap({
   items: ImmutableList(),
 });
 
-const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial) => {
+const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial, isLoadingRecent) => {
   return state.update(timeline, initialTimeline, map => map.withMutations(mMap => {
     mMap.set('isLoading', false);
-    if (!next) mMap.set('hasMore', false);
+    if (!next && !isLoadingRecent) mMap.set('hasMore', false);
 
     if (!statuses.isEmpty()) {
       mMap.update('items', ImmutableList(), oldIds => {
@@ -126,7 +126,7 @@ export default function timelines(state = initialState, action) {
   case TIMELINE_EXPAND_FAIL:
     return state.update(action.timeline, initialTimeline, map => map.set('isLoading', false));
   case TIMELINE_EXPAND_SUCCESS:
-    return expandNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next, action.partial);
+    return expandNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next, action.partial, action.isLoadingRecent);
   case TIMELINE_UPDATE:
     return updateTimeline(state, action.timeline, fromJS(action.status));
   case TIMELINE_DELETE:
diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss
index 4f96204f2..6132dd1ae 100644
--- a/app/javascript/styles/mastodon/forms.scss
+++ b/app/javascript/styles/mastodon/forms.scss
@@ -420,6 +420,7 @@ code {
     border: 1px solid darken($ui-base-color, 14%);
     border-radius: 4px;
     padding: 10px;
+    padding-right: 30px;
     height: 41px;
   }
 
diff --git a/app/models/custom_emoji.rb b/app/models/custom_emoji.rb
index b99ed01f0..d3cc70504 100644
--- a/app/models/custom_emoji.rb
+++ b/app/models/custom_emoji.rb
@@ -31,6 +31,8 @@ class CustomEmoji < ApplicationRecord
 
   has_attached_file :image, styles: { static: { format: 'png', convert_options: '-coalesce -strip' } }
 
+  before_validation :downcase_domain
+
   validates_attachment :image, content_type: { content_type: 'image/png' }, presence: true, size: { less_than: LIMIT }
   validates :shortcode, uniqueness: { scope: :domain }, format: { with: /\A#{SHORTCODE_RE_FRAGMENT}\z/ }, length: { minimum: 2 }
 
@@ -73,4 +75,8 @@ class CustomEmoji < ApplicationRecord
   def remove_entity_cache
     Rails.cache.delete(EntityCache.instance.to_key(:emoji, shortcode, domain))
   end
+
+  def downcase_domain
+    self.domain = domain.downcase unless domain.nil?
+  end
 end
diff --git a/app/models/custom_emoji_filter.rb b/app/models/custom_emoji_filter.rb
index c4bc310bb..7649055d2 100644
--- a/app/models/custom_emoji_filter.rb
+++ b/app/models/custom_emoji_filter.rb
@@ -26,7 +26,7 @@ class CustomEmojiFilter
     when 'remote'
       CustomEmoji.remote
     when 'by_domain'
-      CustomEmoji.where(domain: value)
+      CustomEmoji.where(domain: value.downcase)
     when 'shortcode'
       CustomEmoji.search(value)
     else
diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb
index 8a39e09b7..34c75e3bf 100644
--- a/app/models/form/admin_settings.rb
+++ b/app/models/form/admin_settings.rb
@@ -46,6 +46,8 @@ class Form::AdminSettings
     :preview_sensitive_media=,
     :custom_css,
     :custom_css=,
+    :profile_directory,
+    :profile_directory=,
     to: Setting
   )
 
diff --git a/app/models/user.rb b/app/models/user.rb
index 5a21419bf..66896257a 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -73,7 +73,7 @@ class User < ApplicationRecord
 
   validates :locale, inclusion: I18n.available_locales.map(&:to_s), if: :locale?
   validates_with BlacklistedEmailValidator, if: :email_changed?
-  validates_with EmailMxValidator, if: :email_changed?
+  validates_with EmailMxValidator, if: :validate_email_dns?
 
   scope :recent, -> { order(id: :desc) }
   scope :admins, -> { where(admin: true) }
@@ -360,4 +360,8 @@ class User < ApplicationRecord
   def needs_feed_update?
     last_sign_in_at < ACTIVE_DURATION.ago
   end
+
+  def validate_email_dns?
+    email_changed? && !(Rails.env.test? || Rails.env.development?)
+  end
 end
diff --git a/app/policies/account_policy.rb b/app/policies/account_policy.rb
index efabe80d0..07bae68ef 100644
--- a/app/policies/account_policy.rb
+++ b/app/policies/account_policy.rb
@@ -33,6 +33,10 @@ class AccountPolicy < ApplicationPolicy
     staff?
   end
 
+  def remove_header?
+    staff?
+  end
+
   def subscribe?
     admin?
   end
diff --git a/app/serializers/activitypub/actor_serializer.rb b/app/serializers/activitypub/actor_serializer.rb
index 5054bd683..72c30dc73 100644
--- a/app/serializers/activitypub/actor_serializer.rb
+++ b/app/serializers/activitypub/actor_serializer.rb
@@ -105,7 +105,7 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer
   end
 
   def virtual_tags
-    object.emojis
+    object.emojis + object.tags
   end
 
   def virtual_attachments
@@ -119,6 +119,24 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer
   class CustomEmojiSerializer < ActivityPub::EmojiSerializer
   end
 
+  class TagSerializer < ActiveModel::Serializer
+    include RoutingHelper
+
+    attributes :type, :href, :name
+
+    def type
+      'Hashtag'
+    end
+
+    def href
+      explore_hashtag_url(object)
+    end
+
+    def name
+      "##{object.name}"
+    end
+  end
+
   class Account::FieldSerializer < ActiveModel::Serializer
     attributes :type, :name, :value
 
diff --git a/app/services/pubsubhubbub/subscribe_service.rb b/app/services/pubsubhubbub/subscribe_service.rb
index 2dba05b12..550da6328 100644
--- a/app/services/pubsubhubbub/subscribe_service.rb
+++ b/app/services/pubsubhubbub/subscribe_service.rb
@@ -19,31 +19,18 @@ class Pubsubhubbub::SubscribeService < BaseService
   private
 
   def process_subscribe
-    case subscribe_status
-    when :invalid_topic
+    if account.nil?
       ['Invalid topic URL', 422]
-    when :invalid_callback
+    elsif !valid_callback?
       ['Invalid callback URL', 422]
-    when :callback_not_allowed
+    elsif blocked_domain?
       ['Callback URL not allowed', 403]
-    when :valid
+    else
       confirm_subscription
       ['', 202]
     end
   end
 
-  def subscribe_status
-    if account.nil?
-      :invalid_topic
-    elsif !valid_callback?
-      :invalid_callback
-    elsif blocked_domain?
-      :callback_not_allowed
-    else
-      :valid
-    end
-  end
-
   def confirm_subscription
     subscription = locate_subscription
     Pubsubhubbub::ConfirmationWorker.perform_async(subscription.id, 'subscribe', secret, lease_seconds)
@@ -58,12 +45,7 @@ class Pubsubhubbub::SubscribeService < BaseService
   end
 
   def locate_subscription
-    subscription = Subscription.find_by(account: account, callback_url: callback)
-
-    if subscription.nil?
-      subscription = Subscription.new(account: account, callback_url: callback)
-    end
-
+    subscription = Subscription.find_or_initialize_by(account: account, callback_url: callback)
     subscription.domain = domain
     subscription.save!
     subscription
diff --git a/app/services/update_account_service.rb b/app/services/update_account_service.rb
index 36665177d..01756a73d 100644
--- a/app/services/update_account_service.rb
+++ b/app/services/update_account_service.rb
@@ -12,6 +12,9 @@ class UpdateAccountService < BaseService
       check_links(account)
       process_hashtags(account)
     end
+  rescue Mastodon::DimensionsValidationError => de
+    account.errors.add(:avatar, de.message)
+    false
   end
 
   private
diff --git a/app/validators/email_mx_validator.rb b/app/validators/email_mx_validator.rb
index 8d1e58b38..5b4c684b2 100644
--- a/app/validators/email_mx_validator.rb
+++ b/app/validators/email_mx_validator.rb
@@ -4,7 +4,6 @@ require 'resolv'
 
 class EmailMxValidator < ActiveModel::Validator
   def validate(user)
-    return if Rails.env.test? || Rails.env.development?
     user.errors.add(:email, I18n.t('users.invalid_email')) if invalid_mx?(user.email)
   end
 
@@ -15,13 +14,23 @@ class EmailMxValidator < ActiveModel::Validator
 
     return true if domain.nil?
 
-    records = Resolv::DNS.new.getresources(domain, Resolv::DNS::Resource::IN::MX).to_a.map { |e| e.exchange.to_s }
-    records = Resolv::DNS.new.getresources(domain, Resolv::DNS::Resource::IN::A).to_a.map { |e| e.address.to_s } if records.empty?
+    hostnames = []
+    ips       = []
 
-    records.empty? || on_blacklist?(records)
+    Resolv::DNS.open do |dns|
+      dns.timeouts = 1
+
+      hostnames = dns.getresources(domain, Resolv::DNS::Resource::IN::MX).to_a.map { |e| e.exchange.to_s }
+
+      ([domain] + hostnames).uniq.each do |hostname|
+        ips.concat(dns.getresources(hostname, Resolv::DNS::Resource::IN::A).to_a.map { |e| e.address.to_s })
+      end
+    end
+
+    ips.empty? || on_blacklist?(hostnames + ips)
   end
 
   def on_blacklist?(values)
-    EmailDomainBlock.where(domain: values).any?
+    EmailDomainBlock.where(domain: values.uniq).any?
   end
 end
diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml
index 9ceae007d..370e7e470 100644
--- a/app/views/accounts/_header.html.haml
+++ b/app/views/accounts/_header.html.haml
@@ -1,12 +1,9 @@
 .public-account-header{:class => ("inactive" if account.moved?)}
   .public-account-header__image
-    = image_tag account.header.url, class: 'parallax'
+    = image_tag (current_account&.user&.setting_auto_play_gif ? account.header_original_url : account.header_static_url), class: 'parallax'
   .public-account-header__bar
     = link_to short_account_url(account), class: 'avatar' do
-      - if current_account&.user&.setting_auto_play_gif
-        = image_tag account.avatar_original_url
-      - else
-        = image_tag account.avatar_static_url
+      = image_tag (current_account&.user&.setting_auto_play_gif ? account.avatar_original_url : account.avatar_static_url)
     .public-account-header__tabs
       .public-account-header__tabs__name
         %h1
diff --git a/app/views/admin/accounts/_account.html.haml b/app/views/admin/accounts/_account.html.haml
index 0fadaae1e..1e1bb1812 100644
--- a/app/views/admin/accounts/_account.html.haml
+++ b/app/views/admin/accounts/_account.html.haml
@@ -13,3 +13,6 @@
       %time.time-ago{ datetime: account.user_current_sign_in_at.iso8601, title: l(account.user_current_sign_in_at) }= l account.user_current_sign_in_at
     - else
       \-
+  %td
+    = table_link_to 'circle', t('admin.accounts.web'), web_path("accounts/#{account.id}")
+    = table_link_to 'globe', t('admin.accounts.public'), TagManager.instance.url_for(account)
diff --git a/app/views/admin/accounts/index.html.haml b/app/views/admin/accounts/index.html.haml
index 0d31eee36..91fddadf8 100644
--- a/app/views/admin/accounts/index.html.haml
+++ b/app/views/admin/accounts/index.html.haml
@@ -41,6 +41,7 @@
         %th= t('admin.accounts.role')
         %th= t('admin.accounts.most_recent_ip')
         %th= t('admin.accounts.most_recent_activity')
+        %th
     %tbody
       = render @accounts
 
diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml
index c1a5fc1bd..e9f765107 100644
--- a/app/views/admin/accounts/show.html.haml
+++ b/app/views/admin/accounts/show.html.haml
@@ -16,11 +16,18 @@
 
       %tr
         %th= t('admin.accounts.avatar')
-        %th
+        %td
           = link_to @account.avatar.url(:original) do
             = image_tag @account.avatar.url(:original), alt: '', width: 40, height: 40, class: 'avatar'
           - if @account.local? && @account.avatar?
             = table_link_to 'trash', t('admin.accounts.remove_avatar'), remove_avatar_admin_account_path(@account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') } if can?(:remove_avatar, @account)
+      %tr
+        %th= t('admin.accounts.header')
+        %td
+          = link_to @account.header.url(:original) do
+            = image_tag @account.header.url(:original), alt: '', width: 128, height: 40, class: 'header'
+          - if @account.local? && @account.header?
+            = table_link_to 'trash', t('admin.accounts.remove_header'), remove_header_admin_account_path(@account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') } if can?(:remove_header, @account)
 
       - if @account.local?
         %tr
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index 1996eef4d..fa3d70e9e 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -58,6 +58,12 @@
           - else
             %span.pull-right.negative-hint= fa_icon 'times fw'
         %li
+          = link_to t('admin.dashboard.feature_profile_directory'), edit_admin_settings_path
+          - if @profile_directory
+            %span.pull-right.positive-hint= fa_icon 'check fw'
+          - else
+            %span.pull-right.negative-hint= fa_icon 'times fw'
+        %li
           = link_to t('admin.dashboard.feature_relay'), admin_relays_path
           - if @relay_enabled
             %span.pull-right.positive-hint= fa_icon 'check fw'
diff --git a/app/views/admin/settings/edit.html.haml b/app/views/admin/settings/edit.html.haml
index 7c378bf73..bcf57aac0 100644
--- a/app/views/admin/settings/edit.html.haml
+++ b/app/views/admin/settings/edit.html.haml
@@ -62,6 +62,9 @@
   .fields-group
     = f.input :preview_sensitive_media, as: :boolean, wrapper: :with_label, label: t('admin.settings.preview_sensitive_media.title'), hint: t('admin.settings.preview_sensitive_media.desc_html')
 
+  .fields-group
+    = f.input :profile_directory, as: :boolean, wrapper: :with_label, label: t('admin.settings.profile_directory.title'), hint: t('admin.settings.profile_directory.desc_html')
+
   %hr.spacer/
 
   .fields-group
diff --git a/app/views/layouts/public.html.haml b/app/views/layouts/public.html.haml
index ee49ed06c..5545df54c 100644
--- a/app/views/layouts/public.html.haml
+++ b/app/views/layouts/public.html.haml
@@ -6,7 +6,8 @@
           = link_to root_url, class: 'brand' do
             = image_tag asset_pack_path('logo_full.svg'), alt: 'Mastodon'
 
-          = link_to t('directories.directory'), explore_path, class: 'nav-link'
+          - if Setting.profile_directory
+            = link_to t('directories.directory'), explore_path, class: 'nav-link'
           = link_to t('about.about_this'), about_more_path, class: 'nav-link'
           = link_to t('about.apps'), 'https://joinmastodon.org/apps', class: 'nav-link'
         .nav-center
diff --git a/app/views/settings/profiles/show.html.haml b/app/views/settings/profiles/show.html.haml
index 212c6cb44..05cc93d69 100644
--- a/app/views/settings/profiles/show.html.haml
+++ b/app/views/settings/profiles/show.html.haml
@@ -26,8 +26,9 @@
   .fields-group
     = f.input :bot, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.bot')
 
-  .fields-group
-    = f.input :discoverable, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.discoverable_html', min_followers: Account::MIN_FOLLOWERS_DISCOVERY, path: explore_path)
+  - if Setting.profile_directory
+    .fields-group
+      = f.input :discoverable, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.discoverable_html', min_followers: Account::MIN_FOLLOWERS_DISCOVERY, path: explore_path)
 
   %hr.spacer/