diff --git a/.nvmrc b/.nvmrc
index 48ef2c10bab..d4c3d320cc8 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-20.9
+20.10
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 030f311101a..bc66bc4adda 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -45,48 +45,6 @@ Metrics/PerceivedComplexity:
 RSpec/ExampleLength:
   Max: 22
 
-RSpec/LetSetup:
-  Exclude:
-    - 'spec/controllers/api/v1/accounts/statuses_controller_spec.rb'
-    - 'spec/controllers/api/v1/filters_controller_spec.rb'
-    - 'spec/controllers/api/v2/admin/accounts_controller_spec.rb'
-    - 'spec/controllers/api/v2/filters/keywords_controller_spec.rb'
-    - 'spec/controllers/api/v2/filters/statuses_controller_spec.rb'
-    - 'spec/controllers/auth/confirmations_controller_spec.rb'
-    - 'spec/controllers/auth/passwords_controller_spec.rb'
-    - 'spec/controllers/auth/sessions_controller_spec.rb'
-    - 'spec/controllers/follower_accounts_controller_spec.rb'
-    - 'spec/controllers/following_accounts_controller_spec.rb'
-    - 'spec/controllers/oauth/authorized_applications_controller_spec.rb'
-    - 'spec/controllers/oauth/tokens_controller_spec.rb'
-    - 'spec/controllers/settings/imports_controller_spec.rb'
-    - 'spec/lib/activitypub/activity/delete_spec.rb'
-    - 'spec/lib/vacuum/applications_vacuum_spec.rb'
-    - 'spec/lib/vacuum/preview_cards_vacuum_spec.rb'
-    - 'spec/models/account_spec.rb'
-    - 'spec/models/account_statuses_cleanup_policy_spec.rb'
-    - 'spec/models/canonical_email_block_spec.rb'
-    - 'spec/models/status_spec.rb'
-    - 'spec/models/user_spec.rb'
-    - 'spec/services/account_statuses_cleanup_service_spec.rb'
-    - 'spec/services/activitypub/fetch_featured_collection_service_spec.rb'
-    - 'spec/services/activitypub/fetch_remote_status_service_spec.rb'
-    - 'spec/services/activitypub/process_account_service_spec.rb'
-    - 'spec/services/activitypub/process_collection_service_spec.rb'
-    - 'spec/services/batched_remove_status_service_spec.rb'
-    - 'spec/services/block_domain_service_spec.rb'
-    - 'spec/services/bulk_import_service_spec.rb'
-    - 'spec/services/delete_account_service_spec.rb'
-    - 'spec/services/import_service_spec.rb'
-    - 'spec/services/notify_service_spec.rb'
-    - 'spec/services/remove_status_service_spec.rb'
-    - 'spec/services/report_service_spec.rb'
-    - 'spec/services/resolve_account_service_spec.rb'
-    - 'spec/services/suspend_account_service_spec.rb'
-    - 'spec/services/unallow_domain_service_spec.rb'
-    - 'spec/services/unsuspend_account_service_spec.rb'
-    - 'spec/workers/scheduler/user_cleanup_scheduler_spec.rb'
-
 RSpec/MultipleExpectations:
   Max: 8
 
diff --git a/Gemfile.lock b/Gemfile.lock
index 3b33402b228..9b78ca733e1 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -131,20 +131,20 @@ GEM
     attr_required (1.0.1)
     awrence (1.2.1)
     aws-eventstream (1.3.0)
-    aws-partitions (1.857.0)
-    aws-sdk-core (3.188.0)
-      aws-eventstream (~> 1, >= 1.0.2)
+    aws-partitions (1.860.0)
+    aws-sdk-core (3.189.0)
+      aws-eventstream (~> 1, >= 1.3.0)
       aws-partitions (~> 1, >= 1.651.0)
-      aws-sigv4 (~> 1.5)
+      aws-sigv4 (~> 1.8)
       jmespath (~> 1, >= 1.6.1)
-    aws-sdk-kms (1.73.0)
+    aws-sdk-kms (1.74.0)
       aws-sdk-core (~> 3, >= 3.188.0)
       aws-sigv4 (~> 1.1)
-    aws-sdk-s3 (1.140.0)
-      aws-sdk-core (~> 3, >= 3.188.0)
+    aws-sdk-s3 (1.141.0)
+      aws-sdk-core (~> 3, >= 3.189.0)
       aws-sdk-kms (~> 1)
-      aws-sigv4 (~> 1.6)
-    aws-sigv4 (1.7.0)
+      aws-sigv4 (~> 1.8)
+    aws-sigv4 (1.8.0)
       aws-eventstream (~> 1, >= 1.0.2)
     azure-storage-blob (2.0.3)
       azure-storage-common (~> 2.0)
@@ -220,7 +220,7 @@ GEM
       database_cleaner-core (~> 2.0.0)
     database_cleaner-core (2.0.1)
     date (3.3.4)
-    debug (1.9.0)
+    debug (1.9.1)
       irb (~> 1.10)
       reline (>= 0.3.8)
     debug_inspector (1.1.0)
@@ -376,8 +376,8 @@ GEM
       rainbow (>= 2.2.2, < 4.0)
       terminal-table (>= 1.5.1)
     idn-ruby (0.1.5)
-    io-console (0.6.0)
-    irb (1.10.1)
+    io-console (0.7.1)
+    irb (1.11.0)
       rdoc
       reline (>= 0.3.8)
     jmespath (1.6.2)
@@ -540,7 +540,7 @@ GEM
       activesupport (>= 7.0.0)
       rack
       railties (>= 7.0.0)
-    psych (5.1.1.1)
+    psych (5.1.2)
       stringio
     public_suffix (5.0.4)
     puma (6.4.0)
@@ -614,7 +614,7 @@ GEM
       link_header (~> 0.0, >= 0.0.8)
     rdf-normalize (0.6.1)
       rdf (~> 3.2)
-    rdoc (6.6.1)
+    rdoc (6.6.2)
       psych (>= 4.0.0)
     redcarpet (3.6.0)
     redis (4.8.1)
diff --git a/app/controllers/admin/follow_recommendations_controller.rb b/app/controllers/admin/follow_recommendations_controller.rb
index 841e3cc7fbf..a54e41bd8c1 100644
--- a/app/controllers/admin/follow_recommendations_controller.rb
+++ b/app/controllers/admin/follow_recommendations_controller.rb
@@ -8,7 +8,7 @@ module Admin
       authorize :follow_recommendation, :show?
 
       @form     = Form::AccountBatch.new
-      @accounts = filtered_follow_recommendations
+      @accounts = filtered_follow_recommendations.page(params[:page])
     end
 
     def update
diff --git a/app/controllers/api/v1/suggestions_controller.rb b/app/controllers/api/v1/suggestions_controller.rb
index 9737ae5cb62..9ba1cef63ca 100644
--- a/app/controllers/api/v1/suggestions_controller.rb
+++ b/app/controllers/api/v1/suggestions_controller.rb
@@ -3,22 +3,23 @@
 class Api::V1::SuggestionsController < Api::BaseController
   include Authorization
 
-  before_action -> { doorkeeper_authorize! :read }
+  before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, only: :index
+  before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, except: :index
   before_action :require_user!
+  before_action :set_suggestions
 
   def index
-    suggestions = suggestions_source.get(current_account, limit: limit_param(DEFAULT_ACCOUNTS_LIMIT))
-    render json: suggestions.map(&:account), each_serializer: REST::AccountSerializer
+    render json: @suggestions.get(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:offset].to_i).map(&:account), each_serializer: REST::AccountSerializer
   end
 
   def destroy
-    suggestions_source.remove(current_account, params[:id])
+    @suggestions.remove(params[:id])
     render_empty
   end
 
   private
 
-  def suggestions_source
-    AccountSuggestions::PastInteractionsSource.new
+  def set_suggestions
+    @suggestions = AccountSuggestions.new(current_account)
   end
 end
diff --git a/app/controllers/api/v2/suggestions_controller.rb b/app/controllers/api/v2/suggestions_controller.rb
index 35eb276c01f..8516796e860 100644
--- a/app/controllers/api/v2/suggestions_controller.rb
+++ b/app/controllers/api/v2/suggestions_controller.rb
@@ -3,17 +3,23 @@
 class Api::V2::SuggestionsController < Api::BaseController
   include Authorization
 
-  before_action -> { doorkeeper_authorize! :read }
+  before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, only: :index
+  before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, except: :index
   before_action :require_user!
   before_action :set_suggestions
 
   def index
-    render json: @suggestions, each_serializer: REST::SuggestionSerializer
+    render json: @suggestions.get(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:offset].to_i), each_serializer: REST::SuggestionSerializer
+  end
+
+  def destroy
+    @suggestions.remove(params[:id])
+    render_empty
   end
 
   private
 
   def set_suggestions
-    @suggestions = AccountSuggestions.get(current_account, limit_param(DEFAULT_ACCOUNTS_LIMIT))
+    @suggestions = AccountSuggestions.new(current_account)
   end
 end
diff --git a/app/javascript/mastodon/features/account/components/header.jsx b/app/javascript/mastodon/features/account/components/header.jsx
index 29b46cb43d4..97f68c2217d 100644
--- a/app/javascript/mastodon/features/account/components/header.jsx
+++ b/app/javascript/mastodon/features/account/components/header.jsx
@@ -35,6 +35,8 @@ import FollowRequestNoteContainer from '../containers/follow_request_note_contai
 const messages = defineMessages({
   unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
   follow: { id: 'account.follow', defaultMessage: 'Follow' },
+  followBack: { id: 'account.follow_back', defaultMessage: 'Follow back' },
+  mutual: { id: 'account.mutual', defaultMessage: 'Mutual' },
   cancel_follow_request: { id: 'account.cancel_follow_request', defaultMessage: 'Withdraw follow request' },
   requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' },
   unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
@@ -82,6 +84,20 @@ const titleFromAccount = account => {
   return `${prefix} (@${acct})`;
 };
 
+const messageForFollowButton = relationship => {
+  if(!relationship) return messages.follow;
+
+  if (relationship.get('following') && relationship.get('followed_by')) {
+    return messages.mutual;
+  } else if (!relationship.get('following') && relationship.get('followed_by')) {
+    return messages.followBack;
+  } else if (relationship.get('following')) {
+    return messages.unfollow;
+  } else {
+    return messages.follow;
+  }
+};
+
 const dateFormatOptions = {
   month: 'short',
   day: 'numeric',
@@ -253,9 +269,7 @@ class Header extends ImmutablePureComponent {
     let info = [];
     let menu = [];
 
-    if (me !== account.get('id') && account.getIn(['relationship', 'followed_by'])) {
-      info.push(<span key='followed_by' className='relationship-tag'><FormattedMessage id='account.follows_you' defaultMessage='Follows you' /></span>);
-    } else if (me !== account.get('id') && account.getIn(['relationship', 'blocking'])) {
+    if (me !== account.get('id') && account.getIn(['relationship', 'blocking'])) {
       info.push(<span key='blocked' className='relationship-tag'><FormattedMessage id='account.blocked' defaultMessage='Blocked' /></span>);
     }
 
@@ -281,7 +295,7 @@ class Header extends ImmutablePureComponent {
       } else if (account.getIn(['relationship', 'requested'])) {
         actionBtn = <Button text={intl.formatMessage(messages.cancel_follow_request)} title={intl.formatMessage(messages.requested)} onClick={this.props.onFollow} />;
       } else if (!account.getIn(['relationship', 'blocking'])) {
-        actionBtn = <Button disabled={account.getIn(['relationship', 'blocked_by'])} className={classNames({ 'button--destructive': account.getIn(['relationship', 'following']) })} text={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={signedIn ? this.props.onFollow : this.props.onInteractionModal} />;
+        actionBtn = <Button disabled={account.getIn(['relationship', 'blocked_by'])} className={classNames({ 'button--destructive': account.getIn(['relationship', 'following']) })} text={intl.formatMessage(messageForFollowButton(account.get('relationship')))} onClick={signedIn ? this.props.onFollow : this.props.onInteractionModal} />;
       } else if (account.getIn(['relationship', 'blocking'])) {
         actionBtn = <Button text={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.props.onBlock} />;
       }
diff --git a/app/javascript/mastodon/features/onboarding/profile.jsx b/app/javascript/mastodon/features/onboarding/profile.jsx
index 09e6b2c6c69..daaef6065c6 100644
--- a/app/javascript/mastodon/features/onboarding/profile.jsx
+++ b/app/javascript/mastodon/features/onboarding/profile.jsx
@@ -26,6 +26,8 @@ const messages = defineMessages({
   uploadAvatar: { id: 'onboarding.profile.upload_avatar', defaultMessage: 'Upload profile picture' },
 });
 
+const nullIfMissing = path => path.endsWith('missing.png') ? null : path;
+
 export const Profile = () => {
   const account = useAppSelector(state => state.getIn(['accounts', me]));
   const [displayName, setDisplayName] = useState(account.get('display_name'));
@@ -61,8 +63,8 @@ export const Profile = () => {
     setHeader(e.target?.files?.[0]);
   }, [setHeader]);
 
-  const avatarPreview = useMemo(() => avatar ? URL.createObjectURL(avatar) : account.get('avatar'), [avatar, account]);
-  const headerPreview = useMemo(() => header ? URL.createObjectURL(header) : account.get('header'), [header, account]);
+  const avatarPreview = useMemo(() => avatar ? URL.createObjectURL(avatar) : nullIfMissing(account.get('avatar')), [avatar, account]);
+  const headerPreview = useMemo(() => header ? URL.createObjectURL(header) : nullIfMissing(account.get('header')), [header, account]);
 
   const handleSubmit = useCallback(() => {
     setIsSaving(true);
diff --git a/app/javascript/mastodon/locales/af.json b/app/javascript/mastodon/locales/af.json
index d3cc40c60c0..6c37cdf5ca4 100644
--- a/app/javascript/mastodon/locales/af.json
+++ b/app/javascript/mastodon/locales/af.json
@@ -30,7 +30,6 @@
   "account.followers.empty": "Hierdie gebruiker het nog nie volgers nie.",
   "account.following": "Volg",
   "account.follows.empty": "Die gebruiker volg nog niemand.",
-  "account.follows_you": "Volg jou",
   "account.go_to_profile": "Gaan na profiel",
   "account.hide_reblogs": "Versteek plasings wat deur @{name} aangestuur is",
   "account.joined_short": "Aangesluit",
diff --git a/app/javascript/mastodon/locales/an.json b/app/javascript/mastodon/locales/an.json
index b2134551bfb..23899b10072 100644
--- a/app/javascript/mastodon/locales/an.json
+++ b/app/javascript/mastodon/locales/an.json
@@ -35,7 +35,6 @@
   "account.following": "Seguindo",
   "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Seguindo}}",
   "account.follows.empty": "Este usuario encara no sigue a dengún.",
-  "account.follows_you": "Te sigue",
   "account.go_to_profile": "Ir ta lo perfil",
   "account.hide_reblogs": "Amagar retutz de @{name}",
   "account.joined_short": "S'unió",
diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json
index 979bc9a701f..c0d07cc6484 100644
--- a/app/javascript/mastodon/locales/ar.json
+++ b/app/javascript/mastodon/locales/ar.json
@@ -37,7 +37,6 @@
   "account.following": "الاشتراكات",
   "account.following_counter": "{count, plural, zero{لا يُتابِع أحدًا} one {يُتابِعُ واحد} two{يُتابِعُ اِثنان} few{يُتابِعُ {counter}} many{يُتابِعُ {counter}} other {يُتابِعُ {counter}}}",
   "account.follows.empty": "لا يُتابع هذا المُستخدمُ أيَّ أحدٍ حتى الآن.",
-  "account.follows_you": "يُتابِعُك",
   "account.go_to_profile": "اذهب إلى الملف الشخصي",
   "account.hide_reblogs": "إخفاء المعاد نشرها مِن @{name}",
   "account.in_memoriam": "في الذكرى.",
diff --git a/app/javascript/mastodon/locales/ast.json b/app/javascript/mastodon/locales/ast.json
index 98f622c2934..c4238edcd40 100644
--- a/app/javascript/mastodon/locales/ast.json
+++ b/app/javascript/mastodon/locales/ast.json
@@ -38,7 +38,6 @@
   "account.following": "Siguiendo",
   "account.following_counter": "{count, plural,one {Sigue a {counter}} other {Sigue a {counter}}}",
   "account.follows.empty": "Esti perfil nun sigue a naide.",
-  "account.follows_you": "Síguete",
   "account.go_to_profile": "Dir al perfil",
   "account.hide_reblogs": "Anubrir los artículos compartíos de @{name}",
   "account.in_memoriam": "N'alcordanza.",
diff --git a/app/javascript/mastodon/locales/be.json b/app/javascript/mastodon/locales/be.json
index f94a8b79ff9..773af40343c 100644
--- a/app/javascript/mastodon/locales/be.json
+++ b/app/javascript/mastodon/locales/be.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "Няма допісаў",
   "account.featured_tags.title": "Тэгі, выбраныя {name}",
   "account.follow": "Падпісацца",
+  "account.follow_back": "Падпісацца ў адказ",
   "account.followers": "Падпісчыкі",
   "account.followers.empty": "Ніхто пакуль не падпісаны на гэтага карыстальніка.",
   "account.followers_counter": "{count, plural, one {{counter} падпісчык} few {{counter} падпісчыкі} many {{counter} падпісчыкаў} other {{counter} падпісчыка}}",
   "account.following": "Падпіскі",
   "account.following_counter": "{count, plural, one {{counter} падпіска} few {{counter} падпіскі} many {{counter} падпісак} other {{counter} падпіскі}}",
   "account.follows.empty": "Карыстальнік ні на каго не падпісаны.",
-  "account.follows_you": "Падпісаны на вас",
   "account.go_to_profile": "Перайсці да профілю",
   "account.hide_reblogs": "Схаваць пашырэнні ад @{name}",
   "account.in_memoriam": "У памяць.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Не апавяшчаць",
   "account.mute_short": "Ігнараваць",
   "account.muted": "Ігнаруецца",
+  "account.mutual": "Узаемныя",
   "account.no_bio": "Апісанне адсутнічае.",
   "account.open_original_page": "Адкрыць арыгінальную старонку",
   "account.posts": "Допісы",
diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json
index f57d868c7a8..8a6d6040d66 100644
--- a/app/javascript/mastodon/locales/bg.json
+++ b/app/javascript/mastodon/locales/bg.json
@@ -38,7 +38,6 @@
   "account.following": "Последвано",
   "account.following_counter": "{count, plural, one {{counter} последван} other {{counter} последвани}}",
   "account.follows.empty": "Потребителят още никого не следва.",
-  "account.follows_you": "Следва ви",
   "account.go_to_profile": "Към профила",
   "account.hide_reblogs": "Скриване на подсилвания от @{name}",
   "account.in_memoriam": "В памет на.",
diff --git a/app/javascript/mastodon/locales/bn.json b/app/javascript/mastodon/locales/bn.json
index b6e4fbb965f..fe3d2a627c3 100644
--- a/app/javascript/mastodon/locales/bn.json
+++ b/app/javascript/mastodon/locales/bn.json
@@ -37,7 +37,6 @@
   "account.following": "অনুসরণ করা হচ্ছে",
   "account.following_counter": "{count, plural,one {{counter} জনকে অনুসরণ} other {{counter} জনকে অনুসরণ}}",
   "account.follows.empty": "এই সদস্য কাউকে এখনো ফলো করেন না.",
-  "account.follows_you": "আপনাকে ফলো করে",
   "account.go_to_profile": "প্রোফাইলে যান",
   "account.hide_reblogs": "@{name}'র সমর্থনগুলি লুকিয়ে ফেলুন",
   "account.in_memoriam": "স্মৃতিতে.",
diff --git a/app/javascript/mastodon/locales/br.json b/app/javascript/mastodon/locales/br.json
index 39cd7324194..bea8b27b77e 100644
--- a/app/javascript/mastodon/locales/br.json
+++ b/app/javascript/mastodon/locales/br.json
@@ -36,7 +36,6 @@
   "account.following": "Koumanantoù",
   "account.following_counter": "{count, plural, one{{counter} C'houmanant} two{{counter} Goumanant} other {{counter} a Goumanant}}",
   "account.follows.empty": "An implijer·ez-mañ na heul den ebet.",
-  "account.follows_you": "Ho heuilh",
   "account.go_to_profile": "Gwelet ar profil",
   "account.hide_reblogs": "Kuzh skignadennoù gant @{name}",
   "account.joined_short": "Amañ abaoe",
diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json
index 5ae49325f6e..86f1fb47650 100644
--- a/app/javascript/mastodon/locales/ca.json
+++ b/app/javascript/mastodon/locales/ca.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "No hi ha tuts",
   "account.featured_tags.title": "etiquetes destacades de {name}",
   "account.follow": "Segueix",
+  "account.follow_back": "Segueix",
   "account.followers": "Seguidors",
   "account.followers.empty": "A aquest usuari encara no el segueix ningú.",
   "account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} Seguidors}}",
   "account.following": "Seguint",
   "account.following_counter": "{count, plural, other {{counter} Seguint-ne}}",
   "account.follows.empty": "Aquest usuari encara no segueix ningú.",
-  "account.follows_you": "Et segueix",
   "account.go_to_profile": "Vés al perfil",
   "account.hide_reblogs": "Amaga els impulsos de @{name}",
   "account.in_memoriam": "En Memòria.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Silencia les notificacions",
   "account.mute_short": "Silencia",
   "account.muted": "Silenciat",
+  "account.mutual": "Mutu",
   "account.no_bio": "No s'ha proporcionat cap descripció.",
   "account.open_original_page": "Obre la pàgina original",
   "account.posts": "Tuts",
diff --git a/app/javascript/mastodon/locales/ckb.json b/app/javascript/mastodon/locales/ckb.json
index 7e964183292..f1cafd1ac47 100644
--- a/app/javascript/mastodon/locales/ckb.json
+++ b/app/javascript/mastodon/locales/ckb.json
@@ -36,7 +36,6 @@
   "account.following": "بەدوادا",
   "account.following_counter": "{count, plural, one {{counter} شوێنکەوتوو} other {{counter} شوێنکەوتوو}}",
   "account.follows.empty": "ئەم بەکارهێنەرە تا ئێستا شوێن کەس نەکەوتووە.",
-  "account.follows_you": "شوێنت دەکەوێت",
   "account.go_to_profile": "بڕۆ بۆ پڕۆفایلی",
   "account.hide_reblogs": "داشاردنی بووستەکان لە @{name}",
   "account.joined_short": "بەشداری کردووە",
diff --git a/app/javascript/mastodon/locales/co.json b/app/javascript/mastodon/locales/co.json
index d4bb2f82ba4..9f90c3d211d 100644
--- a/app/javascript/mastodon/locales/co.json
+++ b/app/javascript/mastodon/locales/co.json
@@ -19,7 +19,6 @@
   "account.followers_counter": "{count, plural, one {{counter} Abbunatu} other {{counter} Abbunati}}",
   "account.following_counter": "{count, plural, one {{counter} Abbunamentu} other {{counter} Abbunamenti}}",
   "account.follows.empty": "St'utilizatore ùn seguita nisunu.",
-  "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.",
diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json
index 16bf5020c50..e18cabcec1f 100644
--- a/app/javascript/mastodon/locales/cs.json
+++ b/app/javascript/mastodon/locales/cs.json
@@ -38,7 +38,6 @@
   "account.following": "Sledujete",
   "account.following_counter": "{count, plural, one {{counter} Sledovaný} few {{counter} Sledovaní} many {{counter} Sledovaných} other {{counter} Sledovaných}}",
   "account.follows.empty": "Tento uživatel zatím nikoho nesleduje.",
-  "account.follows_you": "Sleduje vás",
   "account.go_to_profile": "Přejít na profil",
   "account.hide_reblogs": "Skrýt boosty od @{name}",
   "account.in_memoriam": "In Memoriam.",
diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json
index 4ecf48735e1..17133631a5e 100644
--- a/app/javascript/mastodon/locales/cy.json
+++ b/app/javascript/mastodon/locales/cy.json
@@ -37,7 +37,6 @@
   "account.following": "Yn dilyn",
   "account.following_counter": "{count, plural, one {Yn dilyn: {counter}} other {Yn dilyn: {counter}}}",
   "account.follows.empty": "Nid yw'r defnyddiwr hwn yn dilyn unrhyw un eto.",
-  "account.follows_you": "Yn eich dilyn chi",
   "account.go_to_profile": "Mynd i'r proffil",
   "account.hide_reblogs": "Cuddio hybiau gan @{name}",
   "account.in_memoriam": "Er Cof.",
diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json
index 04fc43734f3..3bc830ad27f 100644
--- a/app/javascript/mastodon/locales/da.json
+++ b/app/javascript/mastodon/locales/da.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "Ingen indlæg",
   "account.featured_tags.title": "{name}s fremhævede hashtags",
   "account.follow": "Følg",
+  "account.follow_back": "Følg tilbage",
   "account.followers": "Følgere",
   "account.followers.empty": "Ingen følger denne bruger endnu.",
   "account.followers_counter": "{count, plural, one {{counter} Følger} other {{counter} Følgere}}",
   "account.following": "Følger",
   "account.following_counter": "{count, plural, one {{counter} Følges} other {{counter} Følges}}",
   "account.follows.empty": "Denne bruger følger ikke nogen endnu.",
-  "account.follows_you": "Følger dig",
   "account.go_to_profile": "Gå til profil",
   "account.hide_reblogs": "Skjul boosts fra @{name}",
   "account.in_memoriam": "Til minde om.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Slå lyden fra for notifikationer",
   "account.mute_short": "Skjul (mute)",
   "account.muted": "Skjult (muted)",
+  "account.mutual": "Fælles",
   "account.no_bio": "Ingen beskrivelse til rådighed.",
   "account.open_original_page": "Åbn oprindelig side",
   "account.posts": "Indlæg",
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index cf545e48c1b..ddcbc58cee5 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "Keine Beiträge",
   "account.featured_tags.title": "Von {name} vorgestellte Hashtags",
   "account.follow": "Folgen",
+  "account.follow_back": "Ebenfalls folgen",
   "account.followers": "Follower",
   "account.followers.empty": "Diesem Profil folgt noch niemand.",
   "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Follower}}",
   "account.following": "Folge ich",
   "account.following_counter": "{count, plural, one {{counter} Folge ich} other {{counter} Folge ich}}",
   "account.follows.empty": "Dieses Profil folgt noch niemandem.",
-  "account.follows_you": "Folgt dir",
   "account.go_to_profile": "Profil aufrufen",
   "account.hide_reblogs": "Geteilte Beiträge von @{name} ausblenden",
   "account.in_memoriam": "Zum Andenken.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Benachrichtigungen stummschalten",
   "account.mute_short": "Stummschalten",
   "account.muted": "Stummgeschaltet",
+  "account.mutual": "Gegenseitig",
   "account.no_bio": "Keine Beschreibung verfügbar.",
   "account.open_original_page": "Ursprüngliche Seite öffnen",
   "account.posts": "Beiträge",
diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json
index 02ce7120a38..83a986ba4e1 100644
--- a/app/javascript/mastodon/locales/el.json
+++ b/app/javascript/mastodon/locales/el.json
@@ -36,7 +36,6 @@
   "account.following": "Ακολουθείτε",
   "account.following_counter": "{count, plural, one {{counter} Ακολουθεί} other {{counter} Ακολουθούν}}",
   "account.follows.empty": "Αυτός ο χρήστης δεν ακολουθεί κανέναν ακόμα.",
-  "account.follows_you": "Σε ακολουθεί",
   "account.go_to_profile": "Μετάβαση στο προφίλ",
   "account.hide_reblogs": "Απόκρυψη ενισχύσεων από @{name}",
   "account.in_memoriam": "Εις μνήμην.",
diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json
index 1a67fecb605..37e5efa5bad 100644
--- a/app/javascript/mastodon/locales/en-GB.json
+++ b/app/javascript/mastodon/locales/en-GB.json
@@ -38,7 +38,6 @@
   "account.following": "Following",
   "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
   "account.follows.empty": "This user doesn't follow anyone yet.",
-  "account.follows_you": "Follows you",
   "account.go_to_profile": "Go to profile",
   "account.hide_reblogs": "Hide boosts from @{name}",
   "account.in_memoriam": "In Memoriam.",
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index 05d7d16564c..be7209d04ad 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "No posts",
   "account.featured_tags.title": "{name}'s featured hashtags",
   "account.follow": "Follow",
+  "account.follow_back": "Follow back",
   "account.followers": "Followers",
   "account.followers.empty": "No one follows this user yet.",
   "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
   "account.following": "Following",
   "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
   "account.follows.empty": "This user doesn't follow anyone yet.",
-  "account.follows_you": "Follows you",
   "account.go_to_profile": "Go to profile",
   "account.hide_reblogs": "Hide boosts from @{name}",
   "account.in_memoriam": "In Memoriam.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Mute notifications",
   "account.mute_short": "Mute",
   "account.muted": "Muted",
+  "account.mutual": "Mutual",
   "account.no_bio": "No description provided.",
   "account.open_original_page": "Open original page",
   "account.posts": "Posts",
diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
index 2678d83a505..4daa699dcf5 100644
--- a/app/javascript/mastodon/locales/eo.json
+++ b/app/javascript/mastodon/locales/eo.json
@@ -38,7 +38,6 @@
   "account.following": "Sekvatoj",
   "account.following_counter": "{count, plural, one {{counter} Sekvato} other {{counter} Sekvatoj}}",
   "account.follows.empty": "La uzanto ankoraŭ ne sekvas iun ajn.",
-  "account.follows_you": "Sekvas vin",
   "account.go_to_profile": "Iri al profilo",
   "account.hide_reblogs": "Kaŝi diskonigojn de @{name}",
   "account.in_memoriam": "Memore.",
diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json
index 4573f4ab9dc..eb6c0f3e4fa 100644
--- a/app/javascript/mastodon/locales/es-AR.json
+++ b/app/javascript/mastodon/locales/es-AR.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "Sin mensajes",
   "account.featured_tags.title": "Etiquetas destacadas de {name}",
   "account.follow": "Seguir",
+  "account.follow_back": "Seguir también",
   "account.followers": "Seguidores",
   "account.followers.empty": "Todavía nadie sigue a este usuario.",
   "account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} seguidores}}",
   "account.following": "Siguiendo",
   "account.following_counter": "{count, plural, other {Siguiendo a {counter}}}",
   "account.follows.empty": "Todavía este usuario no sigue a nadie.",
-  "account.follows_you": "Te sigue",
   "account.go_to_profile": "Ir al perfil",
   "account.hide_reblogs": "Ocultar adhesiones de @{name}",
   "account.in_memoriam": "Cuenta conmemorativa.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Silenciar notificaciones",
   "account.mute_short": "Silenciar",
   "account.muted": "Silenciado",
+  "account.mutual": "Mutuo",
   "account.no_bio": "Sin descripción provista.",
   "account.open_original_page": "Abrir página original",
   "account.posts": "Mensajes",
diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json
index 0d26afef23f..fab5f6ec96e 100644
--- a/app/javascript/mastodon/locales/es-MX.json
+++ b/app/javascript/mastodon/locales/es-MX.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "Sin publicaciones",
   "account.featured_tags.title": "Etiquetas destacadas de {name}",
   "account.follow": "Seguir",
+  "account.follow_back": "Seguir también",
   "account.followers": "Seguidores",
   "account.followers.empty": "Todavía nadie sigue a este usuario.",
   "account.followers_counter": "{count, plural, one {{counter} Seguidor} other {{counter} Seguidores}}",
   "account.following": "Siguiendo",
   "account.following_counter": "{count, plural, other {{counter} Siguiendo}}",
   "account.follows.empty": "Este usuario todavía no sigue a nadie.",
-  "account.follows_you": "Te sigue",
   "account.go_to_profile": "Ir al perfil",
   "account.hide_reblogs": "Ocultar retoots de @{name}",
   "account.in_memoriam": "En memoria.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Silenciar notificaciones",
   "account.mute_short": "Silenciar",
   "account.muted": "Silenciado",
+  "account.mutual": "Mutuo",
   "account.no_bio": "Sin biografía.",
   "account.open_original_page": "Abrir página original",
   "account.posts": "Publicaciones",
diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json
index bbc8bcc7591..0d6149f5f51 100644
--- a/app/javascript/mastodon/locales/es.json
+++ b/app/javascript/mastodon/locales/es.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "Sin publicaciones",
   "account.featured_tags.title": "Etiquetas destacadas de {name}",
   "account.follow": "Seguir",
+  "account.follow_back": "Seguir también",
   "account.followers": "Seguidores",
   "account.followers.empty": "Todavía nadie sigue a este usuario.",
   "account.followers_counter": "{count, plural, one {{counter} Seguidor} other {{counter} Seguidores}}",
   "account.following": "Siguiendo",
   "account.following_counter": "{count, plural, other {Siguiendo a {counter}}}",
   "account.follows.empty": "Este usuario todavía no sigue a nadie.",
-  "account.follows_you": "Te sigue",
   "account.go_to_profile": "Ir al perfil",
   "account.hide_reblogs": "Ocultar impulsos de @{name}",
   "account.in_memoriam": "Cuenta conmemorativa.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Silenciar notificaciones",
   "account.mute_short": "Silenciar",
   "account.muted": "Silenciado",
+  "account.mutual": "Mutuo",
   "account.no_bio": "Sin biografía.",
   "account.open_original_page": "Abrir página original",
   "account.posts": "Publicaciones",
diff --git a/app/javascript/mastodon/locales/et.json b/app/javascript/mastodon/locales/et.json
index a41aa02f802..c3973341092 100644
--- a/app/javascript/mastodon/locales/et.json
+++ b/app/javascript/mastodon/locales/et.json
@@ -38,7 +38,6 @@
   "account.following": "Jälgib",
   "account.following_counter": "{count, plural, one {{counter} jälgitav} other {{counter} jälgitavat}}",
   "account.follows.empty": "See kasutaja ei jälgi veel kedagi.",
-  "account.follows_you": "Jälgib sind",
   "account.go_to_profile": "Mine profiilile",
   "account.hide_reblogs": "Peida @{name} jagamised",
   "account.in_memoriam": "In Memoriam.",
diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json
index 26ed7add1ed..c75f697e4c2 100644
--- a/app/javascript/mastodon/locales/eu.json
+++ b/app/javascript/mastodon/locales/eu.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "Bidalketarik ez",
   "account.featured_tags.title": "{name} erabiltzailearen nabarmendutako traolak",
   "account.follow": "Jarraitu",
+  "account.follow_back": "Jarraitu bueltan",
   "account.followers": "Jarraitzaileak",
   "account.followers.empty": "Ez du inork erabiltzaile hau jarraitzen oraindik.",
   "account.followers_counter": "{count, plural, one {Jarraitzaile {counter}} other {{counter} jarraitzaile}}",
   "account.following": "Jarraitzen",
   "account.following_counter": "{count, plural, one {{counter} jarraitzen} other {{counter} jarraitzen}}",
   "account.follows.empty": "Erabiltzaile honek ez du inor jarraitzen oraindik.",
-  "account.follows_you": "Jarraitzen dizu",
   "account.go_to_profile": "Joan profilera",
   "account.hide_reblogs": "Ezkutatu @{name} erabiltzailearen bultzadak",
   "account.in_memoriam": "Oroimenezkoa.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Mututu jakinarazpenak",
   "account.mute_short": "Mututu",
   "account.muted": "Mutututa",
+  "account.mutual": "Elkarrekikoa",
   "account.no_bio": "Ez da deskribapenik eman.",
   "account.open_original_page": "Ireki jatorrizko orria",
   "account.posts": "Bidalketa",
diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json
index 8e8930bfea5..a93d0f66176 100644
--- a/app/javascript/mastodon/locales/fa.json
+++ b/app/javascript/mastodon/locales/fa.json
@@ -38,7 +38,6 @@
   "account.following": "پی می‌گیرید",
   "account.following_counter": "{count, plural, one {{counter} پی‌گرفته} other {{counter} پی‌گرفته}}",
   "account.follows.empty": "این کاربر هنوز پی‌گیر کسی نیست.",
-  "account.follows_you": "پی‌گیرتان است",
   "account.go_to_profile": "رفتن به نمایه",
   "account.hide_reblogs": "نهفتن تقویت‌های ‎@{name}",
   "account.in_memoriam": "به یادبود.",
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
index e99de4d03a5..dab7eac1e9c 100644
--- a/app/javascript/mastodon/locales/fi.json
+++ b/app/javascript/mastodon/locales/fi.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "Ei julkaisuja",
   "account.featured_tags.title": "Käyttäjän {name} esillä pidettävät aihetunnisteet",
   "account.follow": "Seuraa",
+  "account.follow_back": "Seuraa takaisin",
   "account.followers": "Seuraajat",
   "account.followers.empty": "Kukaan ei seuraa tätä käyttäjää vielä.",
   "account.followers_counter": "{count, plural, one {{counter} seuraaja} other {{counter} seuraajaa}}",
   "account.following": "Seuratut",
   "account.following_counter": "{count, plural, one {{counter} seurattu} other {{counter} seurattua}}",
   "account.follows.empty": "Tämä käyttäjä ei vielä seuraa ketään.",
-  "account.follows_you": "Seuraa sinua",
   "account.go_to_profile": "Avaa profiili",
   "account.hide_reblogs": "Piilota käyttäjän @{name} tehostukset",
   "account.in_memoriam": "Muistoissamme.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Mykistä ilmoitukset",
   "account.mute_short": "Mykistä",
   "account.muted": "Mykistetty",
+  "account.mutual": "Molemmat",
   "account.no_bio": "Kuvausta ei ole annettu.",
   "account.open_original_page": "Avaa alkuperäinen sivu",
   "account.posts": "Julkaisut",
diff --git a/app/javascript/mastodon/locales/fil.json b/app/javascript/mastodon/locales/fil.json
index bc763588753..ec27c8f60e2 100644
--- a/app/javascript/mastodon/locales/fil.json
+++ b/app/javascript/mastodon/locales/fil.json
@@ -31,7 +31,6 @@
   "account.followers.empty": "Wala pang sumusunod sa tagagamit na ito.",
   "account.following": "Sinusundan",
   "account.follows.empty": "Wala pang sinusundan ang tagagamit na ito.",
-  "account.follows_you": "Sinusunod ka",
   "account.go_to_profile": "Pumunta sa profile",
   "account.hide_reblogs": "Itago ang mga pagpapalakas mula sa {name}",
   "account.in_memoriam": "Sa Alaala Ni.",
diff --git a/app/javascript/mastodon/locales/fo.json b/app/javascript/mastodon/locales/fo.json
index 45fafd15dc0..9259f201540 100644
--- a/app/javascript/mastodon/locales/fo.json
+++ b/app/javascript/mastodon/locales/fo.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "Einki uppslag",
   "account.featured_tags.title": "Tvíkrossar hjá {name}",
   "account.follow": "Fylg",
+  "account.follow_back": "Fylg aftur",
   "account.followers": "Fylgjarar",
   "account.followers.empty": "Ongar fylgjarar enn.",
   "account.followers_counter": "{count, plural, one {{counter} Fylgjari} other {{counter} Fylgjarar}}",
   "account.following": "Fylgir",
   "account.following_counter": "{count, plural, one {{counter} fylgir} other {{counter} fylgja}}",
   "account.follows.empty": "Hesin brúkari fylgir ongum enn.",
-  "account.follows_you": "Fylgir tær",
   "account.go_to_profile": "Far til vanga",
   "account.hide_reblogs": "Fjal lyft frá @{name}",
   "account.in_memoriam": "In memoriam.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Sløkk fráboðanir",
   "account.mute_short": "Doyv",
   "account.muted": "Sløkt/ur",
+  "account.mutual": "Sínamillum",
   "account.no_bio": "Lýsing vantar.",
   "account.open_original_page": "Opna upprunasíðuna",
   "account.posts": "Uppsløg",
diff --git a/app/javascript/mastodon/locales/fr-QC.json b/app/javascript/mastodon/locales/fr-QC.json
index e2067cc4606..f2d99412d2c 100644
--- a/app/javascript/mastodon/locales/fr-QC.json
+++ b/app/javascript/mastodon/locales/fr-QC.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "Aucune publication",
   "account.featured_tags.title": "Hashtags inclus de {name}",
   "account.follow": "Suivre",
+  "account.follow_back": "S'abonner en retour",
   "account.followers": "abonné·e·s",
   "account.followers.empty": "Personne ne suit ce compte pour l'instant.",
   "account.followers_counter": "{count, plural, one {{counter} Abonné·e} other {{counter} Abonné·e·s}}",
   "account.following": "Abonné·e",
   "account.following_counter": "{count, plural, one {{counter} Abonnement} other {{counter} Abonnements}}",
   "account.follows.empty": "Ce compte ne suit personne présentement.",
-  "account.follows_you": "Vous suit",
   "account.go_to_profile": "Voir ce profil",
   "account.hide_reblogs": "Masquer les boosts de @{name}",
   "account.in_memoriam": "En souvenir de",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Rendre les notifications muettes",
   "account.mute_short": "Rendre muet",
   "account.muted": "Masqué·e",
+  "account.mutual": "Mutuel",
   "account.no_bio": "Description manquante.",
   "account.open_original_page": "Ouvrir la page d'origine",
   "account.posts": "Publications",
diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json
index 7db4bf7bc4d..774702f98cb 100644
--- a/app/javascript/mastodon/locales/fr.json
+++ b/app/javascript/mastodon/locales/fr.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "Aucun message",
   "account.featured_tags.title": "Les hashtags en vedette de {name}",
   "account.follow": "Suivre",
+  "account.follow_back": "S'abonner en retour",
   "account.followers": "Abonné·e·s",
   "account.followers.empty": "Personne ne suit cet·te utilisateur·rice pour l’instant.",
   "account.followers_counter": "{count, plural, one {{counter} Abonné·e} other {{counter} Abonné·e·s}}",
   "account.following": "Abonnements",
   "account.following_counter": "{count, plural, one {{counter} Abonnement} other {{counter} Abonnements}}",
   "account.follows.empty": "Cet·te utilisateur·rice ne suit personne pour l’instant.",
-  "account.follows_you": "Vous suit",
   "account.go_to_profile": "Aller au profil",
   "account.hide_reblogs": "Masquer les partages de @{name}",
   "account.in_memoriam": "En mémoire de.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Désactiver les alertes",
   "account.mute_short": "Mettre en sourdine",
   "account.muted": "Masqué·e",
+  "account.mutual": "Mutuel",
   "account.no_bio": "Aucune description fournie.",
   "account.open_original_page": "Ouvrir la page d'origine",
   "account.posts": "Messages",
diff --git a/app/javascript/mastodon/locales/fy.json b/app/javascript/mastodon/locales/fy.json
index 2ec4a53bfd9..ea42ef91c09 100644
--- a/app/javascript/mastodon/locales/fy.json
+++ b/app/javascript/mastodon/locales/fy.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "Gjin berjochten",
   "account.featured_tags.title": "Utljochte hashtags fan {name}",
   "account.follow": "Folgje",
+  "account.follow_back": "Weromfolgje",
   "account.followers": "Folgers",
   "account.followers.empty": "Noch net ien folget dizze brûker.",
   "account.followers_counter": "{count, plural, one {{counter} folger} other {{counter} folgers}}",
   "account.following": "Folgjend",
   "account.following_counter": "{count, plural, one {{counter} folgjend} other {{counter} folgjend}}",
   "account.follows.empty": "Dizze brûker folget noch net ien.",
-  "account.follows_you": "Folget jo",
   "account.go_to_profile": "Gean nei profyl",
   "account.hide_reblogs": "Boosts fan @{name} ferstopje",
   "account.in_memoriam": "Yn memoriam.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Meldingen negearje",
   "account.mute_short": "Negearje",
   "account.muted": "Negearre",
+  "account.mutual": "Jimme folgje inoar",
   "account.no_bio": "Gjin omskriuwing opjûn.",
   "account.open_original_page": "Orizjinele side iepenje",
   "account.posts": "Berjochten",
diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json
index ee6b44c8849..f0f9c555496 100644
--- a/app/javascript/mastodon/locales/ga.json
+++ b/app/javascript/mastodon/locales/ga.json
@@ -35,7 +35,6 @@
   "account.following": "Ag leanúint",
   "account.following_counter": "{count, plural, one {Ag leanúint cúntas amháin} other {Ag leanúint {counter} cúntas}}",
   "account.follows.empty": "Ní leanann an t-úsáideoir seo duine ar bith fós.",
-  "account.follows_you": "Do do leanúint",
   "account.go_to_profile": "Téigh go dtí próifíl",
   "account.hide_reblogs": "Folaigh moltaí ó @{name}",
   "account.in_memoriam": "Cuimhneachán.",
diff --git a/app/javascript/mastodon/locales/gd.json b/app/javascript/mastodon/locales/gd.json
index 91333c1a0a5..11d83d6ceb1 100644
--- a/app/javascript/mastodon/locales/gd.json
+++ b/app/javascript/mastodon/locales/gd.json
@@ -37,7 +37,6 @@
   "account.following": "A’ leantainn",
   "account.following_counter": "{count, plural, one {A’ leantainn {counter}} two {A’ leantainn {counter}} few {A’ leantainn {counter}} other {A’ leantainn {counter}}}",
   "account.follows.empty": "Chan eil an cleachdaiche seo a’ leantainn neach sam bith fhathast.",
-  "account.follows_you": "Gad leantainn",
   "account.go_to_profile": "Tadhail air a’ phròifil",
   "account.hide_reblogs": "Falaich na brosnachaidhean o @{name}",
   "account.in_memoriam": "Mar chuimhneachan.",
diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json
index 08d7d49776a..bfd20a46739 100644
--- a/app/javascript/mastodon/locales/gl.json
+++ b/app/javascript/mastodon/locales/gl.json
@@ -38,7 +38,6 @@
   "account.following": "Seguindo",
   "account.following_counter": "{count, plural, one {{counter} Seguindo} other {{counter} Seguindo}}",
   "account.follows.empty": "Esta usuaria aínda non segue a ninguén.",
-  "account.follows_you": "Séguete",
   "account.go_to_profile": "Ir ao perfil",
   "account.hide_reblogs": "Agochar promocións de @{name}",
   "account.in_memoriam": "Lembranzas.",
diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json
index dda92c5c9a4..658e16b8560 100644
--- a/app/javascript/mastodon/locales/he.json
+++ b/app/javascript/mastodon/locales/he.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "אין חצרוצים",
   "account.featured_tags.title": "התגיות המועדפות של {name}",
   "account.follow": "לעקוב",
+  "account.follow_back": "החזרת עוקב",
   "account.followers": "עוקבים",
   "account.followers.empty": "אף אחד לא עוקב אחר המשתמש הזה עדיין.",
   "account.followers_counter": "{count, plural,one {עוקב אחד} other {{counter} עוקבים}}",
   "account.following": "נעקבים",
   "account.following_counter": "{count, plural,one {עוקב אחרי {counter}}other {עוקב אחרי {counter}}}",
   "account.follows.empty": "משתמש זה עדיין לא עוקב אחרי אף אחד.",
-  "account.follows_you": "במעקב אחריך",
   "account.go_to_profile": "מעבר לפרופיל",
   "account.hide_reblogs": "להסתיר הידהודים מאת @{name}",
   "account.in_memoriam": "פרופיל זכרון.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "השתקת התראות",
   "account.mute_short": "השתקה",
   "account.muted": "מושתק",
+  "account.mutual": "הדדיים",
   "account.no_bio": "לא סופק תיאור.",
   "account.open_original_page": "לפתיחת העמוד המקורי",
   "account.posts": "פוסטים",
diff --git a/app/javascript/mastodon/locales/hi.json b/app/javascript/mastodon/locales/hi.json
index 387941b5e47..76d7c1d3812 100644
--- a/app/javascript/mastodon/locales/hi.json
+++ b/app/javascript/mastodon/locales/hi.json
@@ -37,7 +37,6 @@
   "account.following": "फॉलोइंग",
   "account.following_counter": "{count, plural, one {{counter} निम्नलिखित} other {{counter} निम्नलिखित}}",
   "account.follows.empty": "यह यूज़र् अभी तक किसी को फॉलो नहीं करता है।",
-  "account.follows_you": "आपको फॉलो करता है",
   "account.go_to_profile": "प्रोफाइल में जाएँ",
   "account.hide_reblogs": "@{name} के बूस्ट छुपाएं",
   "account.in_memoriam": "याद में",
diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json
index 6da7d6cd8df..654991a0f9d 100644
--- a/app/javascript/mastodon/locales/hr.json
+++ b/app/javascript/mastodon/locales/hr.json
@@ -28,7 +28,6 @@
   "account.following": "Pratim",
   "account.following_counter": "{count, plural, one {{counter} praćeni} few{{counter} praćena} other {{counter} praćenih}}",
   "account.follows.empty": "Korisnik/ca još ne prati nikoga.",
-  "account.follows_you": "Prati te",
   "account.go_to_profile": "Idi na profil",
   "account.hide_reblogs": "Sakrij boostove od @{name}",
   "account.in_memoriam": "U sjećanje.",
diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json
index 386b15811a9..0d50e36feb5 100644
--- a/app/javascript/mastodon/locales/hu.json
+++ b/app/javascript/mastodon/locales/hu.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "Nincs bejegyzés",
   "account.featured_tags.title": "{name} kiemelt hashtagjei",
   "account.follow": "Követés",
+  "account.follow_back": "Viszontkövetés",
   "account.followers": "Követő",
   "account.followers.empty": "Ezt a felhasználót még senki sem követi.",
   "account.followers_counter": "{count, plural, one {{counter} Követő} other {{counter} Követő}}",
   "account.following": "Követve",
   "account.following_counter": "{count, plural, one {{counter} Követett} other {{counter} Követett}}",
   "account.follows.empty": "Ez a felhasználó még senkit sem követ.",
-  "account.follows_you": "Követ téged",
   "account.go_to_profile": "Ugrás a profilhoz",
   "account.hide_reblogs": "@{name} megtolásainak elrejtése",
   "account.in_memoriam": "Emlékünkben.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Értesítések némítása",
   "account.mute_short": "Némítás",
   "account.muted": "Némítva",
+  "account.mutual": "Kölcsönös",
   "account.no_bio": "Leírás nincs megadva.",
   "account.open_original_page": "Eredeti oldal megnyitása",
   "account.posts": "Bejegyzések",
diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json
index f2548c7d3a9..835105218d5 100644
--- a/app/javascript/mastodon/locales/hy.json
+++ b/app/javascript/mastodon/locales/hy.json
@@ -32,7 +32,6 @@
   "account.following": "Հետեւած",
   "account.following_counter": "{count, plural, one {{counter} Հետեւած} other {{counter} Հետեւած}}",
   "account.follows.empty": "Այս օգտատէրը դեռ ոչ մէկի չի հետեւում։",
-  "account.follows_you": "Հետեւում է քեզ",
   "account.go_to_profile": "Գնալ անձնական հաշիւ",
   "account.hide_reblogs": "Թաքցնել @{name}֊ի տարածածները",
   "account.joined_short": "Միացել է",
diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json
index 8ecf36125d8..5af20a97f7b 100644
--- a/app/javascript/mastodon/locales/id.json
+++ b/app/javascript/mastodon/locales/id.json
@@ -37,7 +37,6 @@
   "account.following": "Mengikuti",
   "account.following_counter": "{count, plural, other {{counter} Mengikuti}}",
   "account.follows.empty": "Pengguna ini belum mengikuti siapa pun.",
-  "account.follows_you": "Mengikuti Anda",
   "account.go_to_profile": "Buka profil",
   "account.hide_reblogs": "Sembunyikan boosts dari @{name}",
   "account.in_memoriam": "Mengenang.",
diff --git a/app/javascript/mastodon/locales/ie.json b/app/javascript/mastodon/locales/ie.json
index 966b2727200..a7cf1caab4a 100644
--- a/app/javascript/mastodon/locales/ie.json
+++ b/app/javascript/mastodon/locales/ie.json
@@ -38,7 +38,6 @@
   "account.following": "Sequent",
   "account.following_counter": "{count, plural, one {{counter} Sequent} other {{counter} Sequent}}",
   "account.follows.empty": "Ti-ci usator ancor ne seque quemcunc.",
-  "account.follows_you": "Seque te",
   "account.go_to_profile": "Ear a profil",
   "account.hide_reblogs": "Celar boosts de @{name}",
   "account.in_memoriam": "In Memoriam.",
@@ -140,6 +139,7 @@
   "compose.published.open": "Aperter",
   "compose.saved.body": "Posta conservat.",
   "compose_form.direct_message_warning_learn_more": "Aprender plu",
+  "compose_form.encryption_warning": "Postas in Mastodon ne es inciffrat de comense a fine. Ne posta quelcunc information sensitiv per Mastodon.",
   "compose_form.hashtag_warning": "Ti-ci posta ne va esser listat sur quelcunc hashtag pro que it ne es public. Solmen public postas posse esser serchat per hashtag.",
   "compose_form.lock_disclaimer": "Tui conto ne es {locked}. Quicunc posse sequer te por vider tui postas solmen por sequitores.",
   "compose_form.lock_disclaimer.lock": "cludet",
@@ -197,6 +197,7 @@
   "directory.new_arrivals": "Nov arivantes",
   "directory.recently_active": "Recentmen activ",
   "disabled_account_banner.account_settings": "Parametres del conto",
+  "disabled_account_banner.text": "Tui conto {disabledAccount} es actualmen desactivisat.",
   "dismissable_banner.community_timeline": "Tis-ci es li postas max recent de gente con contos che {domain}.",
   "dismissable_banner.dismiss": "Demisser",
   "dismissable_banner.explore_links": "Tis-ci es li novas max distribuet che li social retage hodie. Novas plu nov, postat de plu diferent persones, es monstrat plu alt.",
@@ -321,6 +322,7 @@
   "interaction_modal.on_another_server": "Sur un servitor diferent",
   "interaction_modal.on_this_server": "Sur ti-ci servitor",
   "interaction_modal.sign_in": "Tu ne ha initiat session che ti-ci servitor. U logia tui conto?",
+  "interaction_modal.sign_in_hint": "Nota: To es li websitu u tu adheret. Si tu ne rememora, sercha li benevenit-email in tui inbuxe. Tu anc posse introducter tui plen usator-nómine! (p.ex. @Mastodon@mastodon.social)",
   "interaction_modal.title.favourite": "Favoritisar li posta de {name}",
   "interaction_modal.title.follow": "Sequer {name}",
   "interaction_modal.title.reblog": "Boostar li posta de {name}",
@@ -351,10 +353,17 @@
   "keyboard_shortcuts.profile": "Aperter profil del autor",
   "keyboard_shortcuts.reply": "Responder al posta",
   "keyboard_shortcuts.requests": "Aperter liste de seque-petitiones",
+  "keyboard_shortcuts.search": "Infocar sercha-barre",
+  "keyboard_shortcuts.spoilers": "Monstrar/celar CW camp",
+  "keyboard_shortcuts.start": "Aperter \"Qualmen comensar\" columne",
+  "keyboard_shortcuts.toggle_hidden": "Monstrar/celar text detra CW",
   "keyboard_shortcuts.toggle_sensitivity": "Monstrar/celar medie",
   "keyboard_shortcuts.toot": "Crear un nov posta",
+  "keyboard_shortcuts.unfocus": "Desinfocar text-area de composition/serchar",
   "keyboard_shortcuts.up": "Mover ad-supra in li liste",
   "lightbox.close": "Cluder",
+  "lightbox.compress": "Compresser vise-buxe de image",
+  "lightbox.expand": "Expander vise-buxe de image",
   "lightbox.next": "Sequent",
   "lightbox.previous": "Precedent",
   "limited_account_hint.action": "Monstrar profil totvez",
@@ -382,6 +391,7 @@
   "mute_modal.hide_notifications": "Celar notificationes de ti-ci usator?",
   "mute_modal.indefinite": "Índefinit",
   "navigation_bar.about": "Information",
+  "navigation_bar.advanced_interface": "Aperter in li web-interfacie avansat",
   "navigation_bar.blocks": "Bloccat usatores",
   "navigation_bar.bookmarks": "Marcatores",
   "navigation_bar.community_timeline": "Local témpor-linea",
@@ -399,6 +409,7 @@
   "navigation_bar.lists": "Listes",
   "navigation_bar.logout": "Exear",
   "navigation_bar.mutes": "Silentiat usatores",
+  "navigation_bar.opened_in_classic_interface": "Postas, contos e altri specific págines es customalmen apertet in li classic web-interfacie.",
   "navigation_bar.personal": "Personal",
   "navigation_bar.pins": "Pinglat postas",
   "navigation_bar.preferences": "Preferenties",
@@ -421,12 +432,16 @@
   "notifications.clear_confirmation": "Vole tu vermen permanentmen aclarar omni tui notificationes?",
   "notifications.column_settings.admin.report": "Nov raportas:",
   "notifications.column_settings.admin.sign_up": "Nov registrationes:",
+  "notifications.column_settings.alert": "Notificationes sur li computator",
   "notifications.column_settings.favourite": "Favorites:",
   "notifications.column_settings.filter_bar.advanced": "Monstrar omni categories",
+  "notifications.column_settings.filter_bar.category": "Rapid filtre-barre",
+  "notifications.column_settings.filter_bar.show_bar": "Monstrar filtre-barre",
   "notifications.column_settings.follow": "Nov sequitores:",
   "notifications.column_settings.follow_request": "Nov petitiones de sequer:",
   "notifications.column_settings.mention": "Mentiones:",
   "notifications.column_settings.poll": "Resultates del balotation:",
+  "notifications.column_settings.push": "Notificationes push",
   "notifications.column_settings.reblog": "Boosts:",
   "notifications.column_settings.show": "Monstrar in columne",
   "notifications.column_settings.sound": "Far son",
@@ -444,6 +459,12 @@
   "notifications.grant_permission": "Dar permission.",
   "notifications.group": "{count} notificationes",
   "notifications.mark_as_read": "Marcar omni notificationes quam leet",
+  "notifications.permission_denied": "Notificationes sur li computator es índisponibil pro que on ha previamen rejectet un petition por navigator-permissiones",
+  "notifications.permission_denied_alert": "Notificationes sur li computator ne posse esser activisat, pro que navigator-permission ha esset previamen rejectet",
+  "notifications.permission_required": "Notificationes sur li computator es índisponibil pro que li besonat permission ne ha esset dat.",
+  "notifications_permission_banner.enable": "Activisar notificationes sur li computator",
+  "notifications_permission_banner.how_to_control": "Por reciver notificationes quande Mastodon ne es apert, activisa notificationes sur li computator. Tu posse decider precisimen quel species de interactiones genera notificationes per li buton {icon} in-supra quande ili es activisat.",
+  "notifications_permission_banner.title": "Nequande preterlassa quocunc",
   "onboarding.action.back": "Retroear",
   "onboarding.actions.back": "Retroear",
   "onboarding.actions.go_to_explore": "Ear a vider lu populari",
@@ -474,9 +495,14 @@
   "onboarding.steps.follow_people.title": "Personalisar tui hemal témpor-linea",
   "onboarding.steps.publish_status.body": "Saluta li munde con text, images, videos o balotationes {emoji}",
   "onboarding.steps.publish_status.title": "Crear tui unesim posta",
+  "onboarding.steps.setup_profile.body": "Ascresce tui interactiones per haver un profil detalliat.",
   "onboarding.steps.setup_profile.title": "Personalisar tui profil",
   "onboarding.steps.share_profile.body": "Di tui amics qualmen trovar te che Mastodon",
   "onboarding.steps.share_profile.title": "Partir tui profil Mastodon",
+  "onboarding.tips.2fa": "<strong>Savet tu?</strong> Tu posse securisar tui conto per activisar 2-factor autentication in tui parametres de conto. Ti functiona con quelcunc aplication TOTP quel tu selecte, null númere de telefon besonat!",
+  "onboarding.tips.accounts_from_other_servers": "<strong>Savet tu?</strong> Pro que Mastodon es decentralisat, quelc profiles queles tu trova va esser logiat che servitores altri quam tui. Totvez tu posse interacter con les sin grates! Lor servitores es in li duesim demí de lor usator-nómines!",
+  "onboarding.tips.migration": "<strong>Savet tu?</strong> Si tu senti que {domain} ne es un bonissim servitor por te futurimen, tu posse mover te a un altri Mastodon-servitor sin perdir tui sequitores. Tu posse mem etablisser tui propri servitor!",
+  "onboarding.tips.verification": "<strong>Savet tu?</strong> Tu posse verificar tui conto per metter un ligament a tui Mastodon-profil in tui propri websitu e adjunter li websitu a tui profil. Null payament o documentes besonat!",
   "password_confirmation.exceeds_maxlength": "Confirmation de passa-parol transpassa li maxim longore de passa-paroles",
   "password_confirmation.mismatching": "Confirmation de passa-parol ne egala",
   "picture_in_picture.restore": "Restaurar",
@@ -600,6 +626,8 @@
   "status.admin_status": "Aperter ti-ci posta in li interfacie de moderation",
   "status.block": "Bloccar @{name}",
   "status.bookmark": "Marcar",
+  "status.cancel_reblog_private": "Desboostar",
+  "status.cannot_reblog": "Ti-ci posta ne posse esser boostat",
   "status.copy": "Copiar ligament al posta",
   "status.delete": "Deleter",
   "status.detailed_status": "Detalliat vise de conversation",
@@ -627,6 +655,10 @@
   "status.pin": "Pinglar sur profil",
   "status.pinned": "Pinglat posta",
   "status.read_more": "Leer plu",
+  "status.reblog": "Boostar",
+  "status.reblog_private": "Boostar con li original visibilitá",
+  "status.reblogged_by": "{name} boostat",
+  "status.reblogs.empty": "Ancor nequi ha boostat ti-ci posta. Quande alqui fa it, ilu va aparir ci.",
   "status.redraft": "Deleter & redacter",
   "status.remove_bookmark": "Remover marcator",
   "status.replied_to": "Respondet a {name}",
@@ -664,6 +696,9 @@
   "trends.counter_by_accounts": "{count, plural, one {{counter} person} other {{counter} persones}} durant li ultim {days, plural, one {die} other {{days} dies}}",
   "trends.trending_now": "Actualmen populari",
   "ui.beforeunload": "Tui íncomplet posta va esser perdit si tu lassa Mastodon.",
+  "units.short.billion": "{count}B",
+  "units.short.million": "{count}M",
+  "units.short.thousand": "{count}K",
   "upload_area.title": "Trenar & lassar cader por cargar",
   "upload_button.label": "Adjunter images, un video o un audio-file",
   "upload_error.limit": "Límite de medie-cargationes transpassat.",
@@ -679,6 +714,7 @@
   "upload_modal.apply": "Aplicar",
   "upload_modal.applying": "Aplicant…",
   "upload_modal.choose_image": "Selecter image",
+  "upload_modal.description_placeholder": "Li Europan lingues es membres del sam familie. Lor separat existentie es un mite",
   "upload_modal.detect_text": "Detecter text del image",
   "upload_modal.edit_media": "Redacter medie",
   "upload_modal.hint": "Clicca o trena li circul por selecter li focal punctu quel va esser sempre visibil in omni previse-images.",
diff --git a/app/javascript/mastodon/locales/ig.json b/app/javascript/mastodon/locales/ig.json
index c24d28eea90..f163567f176 100644
--- a/app/javascript/mastodon/locales/ig.json
+++ b/app/javascript/mastodon/locales/ig.json
@@ -6,7 +6,6 @@
   "account.follow": "Soro",
   "account.followers": "Ndị na-eso",
   "account.following": "Na-eso",
-  "account.follows_you": "Na-eso gị",
   "account.mute": "Mee ogbi @{name}",
   "account.unfollow": "Kwụsị iso",
   "account_note.placeholder": "Click to add a note",
diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json
index ba44408935f..233b7684573 100644
--- a/app/javascript/mastodon/locales/io.json
+++ b/app/javascript/mastodon/locales/io.json
@@ -37,7 +37,6 @@
   "account.following": "Sequata",
   "account.following_counter": "{count, plural, one {{counter} Sequas} other {{counter} Sequanti}}",
   "account.follows.empty": "Ca uzanto ne sequa irgu til nun.",
-  "account.follows_you": "Sequas tu",
   "account.go_to_profile": "Irez al profilo",
   "account.hide_reblogs": "Celez repeti de @{name}",
   "account.in_memoriam": "Memorige.",
diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json
index 0df1a5e7539..46e83b4dbff 100644
--- a/app/javascript/mastodon/locales/is.json
+++ b/app/javascript/mastodon/locales/is.json
@@ -38,7 +38,6 @@
   "account.following": "Fylgist með",
   "account.following_counter": "{count, plural, one {Fylgist með: {counter}} other {Fylgist með: {counter}}}",
   "account.follows.empty": "Þessi notandi fylgist ennþá ekki með neinum.",
-  "account.follows_you": "Fylgir þér",
   "account.go_to_profile": "Fara í notandasnið",
   "account.hide_reblogs": "Fela endurbirtingar fyrir @{name}",
   "account.in_memoriam": "Minning.",
diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json
index fa659506a17..4fb4d88cbc8 100644
--- a/app/javascript/mastodon/locales/it.json
+++ b/app/javascript/mastodon/locales/it.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "Nessun post",
   "account.featured_tags.title": "Hashtag in evidenza di {name}",
   "account.follow": "Segui",
+  "account.follow_back": "Segui a tua volta",
   "account.followers": "Follower",
   "account.followers.empty": "Ancora nessuno segue questo utente.",
   "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Follower}}",
   "account.following": "Seguiti",
   "account.following_counter": "{count, plural, one {{counter} Seguiti} other {{counter} Seguiti}}",
   "account.follows.empty": "Questo utente non segue ancora nessuno.",
-  "account.follows_you": "Ti segue",
   "account.go_to_profile": "Vai al profilo",
   "account.hide_reblogs": "Nascondi potenziamenti da @{name}",
   "account.in_memoriam": "In memoria.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Silenzia notifiche",
   "account.mute_short": "Silenzia",
   "account.muted": "Mutato",
+  "account.mutual": "Reciproco",
   "account.no_bio": "Nessuna descrizione fornita.",
   "account.open_original_page": "Apri la pagina originale",
   "account.posts": "Post",
diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json
index 5ad9d6dc942..3246415759b 100644
--- a/app/javascript/mastodon/locales/ja.json
+++ b/app/javascript/mastodon/locales/ja.json
@@ -38,7 +38,6 @@
   "account.following": "フォロー中",
   "account.following_counter": "{counter} フォロー",
   "account.follows.empty": "まだ誰もフォローしていません。",
-  "account.follows_you": "フォローされています",
   "account.go_to_profile": "プロフィールページへ",
   "account.hide_reblogs": "@{name}さんからのブーストを非表示",
   "account.in_memoriam": "故人を偲んで。",
diff --git a/app/javascript/mastodon/locales/ka.json b/app/javascript/mastodon/locales/ka.json
index 875ac3c195a..9d977e93312 100644
--- a/app/javascript/mastodon/locales/ka.json
+++ b/app/javascript/mastodon/locales/ka.json
@@ -15,7 +15,6 @@
   "account.featured_tags.last_status_never": "პოსტები არ არის",
   "account.follow": "გაყოლა",
   "account.followers": "მიმდევრები",
-  "account.follows_you": "მოგყვებათ",
   "account.hide_reblogs": "დაიმალოს ბუსტები @{name}-სგან",
   "account.media": "მედია",
   "account.mention": "ასახელეთ @{name}",
diff --git a/app/javascript/mastodon/locales/kab.json b/app/javascript/mastodon/locales/kab.json
index e9d4b57de83..08c70a9405a 100644
--- a/app/javascript/mastodon/locales/kab.json
+++ b/app/javascript/mastodon/locales/kab.json
@@ -22,7 +22,6 @@
   "account.followers_counter": "{count, plural, one {{count} n umeḍfar} other {{count} n imeḍfaren}}",
   "account.following_counter": "{count, plural, one {{counter} yettwaḍfaren} other {{counter} yettwaḍfaren}}",
   "account.follows.empty": "Ar tura, amseqdac-agi ur yeṭṭafaṛ yiwen.",
-  "account.follows_you": "Yeṭṭafaṛ-ik",
   "account.hide_reblogs": "Ffer ayen i ibeṭṭu @{name}",
   "account.link_verified_on": "Taɣara n useɣwen-a tettwasenqed ass n {date}",
   "account.locked_info": "Amiḍan-agi uslig isekweṛ. D bab-is kan i izemren ad yeǧǧ, s ufus-is, win ara t-iḍefṛen.",
diff --git a/app/javascript/mastodon/locales/kk.json b/app/javascript/mastodon/locales/kk.json
index 189d792e386..e0e047ba14c 100644
--- a/app/javascript/mastodon/locales/kk.json
+++ b/app/javascript/mastodon/locales/kk.json
@@ -34,7 +34,6 @@
   "account.following": "Жазылым",
   "account.following_counter": "{count, plural, one {{counter} жазылым} other {{counter} жазылым}}",
   "account.follows.empty": "Бұл қолданушы әлі ешкімге жазылмаған.",
-  "account.follows_you": "Сізге жазылған",
   "account.go_to_profile": "Профиліне өту",
   "account.hide_reblogs": "@{name} бустарын жасыру",
   "account.joined_short": "Қосылған",
diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json
index 4606916c1dd..5746ab67a5c 100644
--- a/app/javascript/mastodon/locales/ko.json
+++ b/app/javascript/mastodon/locales/ko.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "게시물 없음",
   "account.featured_tags.title": "{name} 님의 추천 해시태그",
   "account.follow": "팔로우",
+  "account.follow_back": "맞팔로우",
   "account.followers": "팔로워",
   "account.followers.empty": "아직 아무도 이 사용자를 팔로우하고 있지 않습니다.",
   "account.followers_counter": "{counter} 팔로워",
   "account.following": "팔로잉",
   "account.following_counter": "{counter} 팔로잉",
   "account.follows.empty": "이 사용자는 아직 아무도 팔로우하고 있지 않습니다.",
-  "account.follows_you": "나를 팔로우합니다",
   "account.go_to_profile": "프로필로 이동",
   "account.hide_reblogs": "@{name}의 부스트를 숨기기",
   "account.in_memoriam": "고인의 계정입니다.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "알림 뮤트",
   "account.mute_short": "뮤트",
   "account.muted": "뮤트됨",
+  "account.mutual": "상호 팔로우",
   "account.no_bio": "제공된 설명이 없습니다.",
   "account.open_original_page": "원본 페이지 열기",
   "account.posts": "게시물",
diff --git a/app/javascript/mastodon/locales/ku.json b/app/javascript/mastodon/locales/ku.json
index b9405426774..7d8603cae17 100644
--- a/app/javascript/mastodon/locales/ku.json
+++ b/app/javascript/mastodon/locales/ku.json
@@ -36,7 +36,6 @@
   "account.following": "Dişopîne",
   "account.following_counter": "{count, plural, one {{counter} Dişopîne} other {{counter} Dişopîne}}",
   "account.follows.empty": "Ev bikarhêner hin kesekî heya niha neşopandiye.",
-  "account.follows_you": "Te dişopîne",
   "account.go_to_profile": "Biçe bo profîlê",
   "account.hide_reblogs": "Bilindkirinên ji @{name} veşêre",
   "account.in_memoriam": "Di bîranînê de.",
diff --git a/app/javascript/mastodon/locales/kw.json b/app/javascript/mastodon/locales/kw.json
index ca08ca836eb..8f384fe1257 100644
--- a/app/javascript/mastodon/locales/kw.json
+++ b/app/javascript/mastodon/locales/kw.json
@@ -20,7 +20,6 @@
   "account.followers_counter": "{count, plural, one {{counter} Holyer} other {{counter} Holyer}}",
   "account.following_counter": "{count, plural, one {Ow holya {counter}} other {Ow holya {counter}}}",
   "account.follows.empty": "Ny wra'n devnydhyer ma holya nagonan hwath.",
-  "account.follows_you": "Y'th hol",
   "account.hide_reblogs": "Kudha kenerthow a @{name}",
   "account.link_verified_on": "Perghenogeth an kolm ma a veu checkys dhe {date}",
   "account.locked_info": "Studh privetter an akont ma yw alhwedhys. An perghen a wra dasweles dre leuv piw a yll aga holya.",
diff --git a/app/javascript/mastodon/locales/lad.json b/app/javascript/mastodon/locales/lad.json
index ffdc044412d..c54ed2a7e9f 100644
--- a/app/javascript/mastodon/locales/lad.json
+++ b/app/javascript/mastodon/locales/lad.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "\"No ay publikasyones",
   "account.featured_tags.title": "Etiketas avaliadas de {name}",
   "account.follow": "Sige",
+  "account.follow_back": "Sige tamyen",
   "account.followers": "Suivantes",
   "account.followers.empty": "Por agora dingun no sige a este utilizador.",
   "account.followers_counter": "{count, plural, one {{counter} suivante} other {{counter} suivantes}}",
   "account.following": "Sigiendo",
   "account.following_counter": "{count, plural, other {Sigiendo a {counter}}}",
   "account.follows.empty": "Este utilizador ainda no sige a ningun.",
-  "account.follows_you": "Te sige",
   "account.go_to_profile": "Va al profil",
   "account.hide_reblogs": "Eskonde repartajasyones de @{name}",
   "account.in_memoriam": "De bendicha memoria.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Silensia avizos de @{name}",
   "account.mute_short": "Silensia",
   "account.muted": "Silensiado",
+  "account.mutual": "Mutual",
   "account.no_bio": "No ay deskripsion.",
   "account.open_original_page": "Avre pajina orijnala",
   "account.posts": "Publikasyones",
@@ -77,6 +78,10 @@
   "admin.dashboard.retention.average": "Media",
   "admin.dashboard.retention.cohort": "Mez de enrejistrasyon",
   "admin.dashboard.retention.cohort_size": "Muevos utilizadores",
+  "admin.impact_report.instance_accounts": "Profiles de kuentos esto efasaria",
+  "admin.impact_report.instance_followers": "Suivantes a los kualos nuestros utilizadores perderian",
+  "admin.impact_report.instance_follows": "Suivantes a los kualos sus utilizadores perderian",
+  "admin.impact_report.title": "Rezumen de impakto",
   "alert.rate_limited.message": "Por favor aprova dempues de {retry_time, time, medium}.",
   "alert.rate_limited.title": "Trafiko limitado",
   "alert.unexpected.message": "Afito un yerro no asperado.",
@@ -220,6 +225,7 @@
   "emoji_button.search_results": "Rizultados de bushkeda",
   "emoji_button.symbols": "Simbolos",
   "emoji_button.travel": "Viajes i lugares",
+  "empty_column.account_hides_collections": "Este utilizador desidio no mostrar esta enformasyon",
   "empty_column.account_suspended": "Kuento suspendido",
   "empty_column.account_timeline": "No ay publikasyones aki!",
   "empty_column.account_unavailable": "Profil no desponivle",
@@ -294,20 +300,34 @@
   "hashtag.column_settings.tag_mode.any": "Kualsekera de estos",
   "hashtag.column_settings.tag_mode.none": "Dinguno de estos",
   "hashtag.column_settings.tag_toggle": "Inkluir etiketas adisionalas en esta kolumna",
+  "hashtag.counter_by_accounts": "{count, plural, one {{counter} partisipante} other {{counter} partisipantes}}",
+  "hashtag.counter_by_uses": "{count, plural, one {{counter} publikasyon} other {{counter} publikasyones}}",
+  "hashtag.counter_by_uses_today": "{count, plural, one {{counter} publikasyon} other {{counter} publikasyones}} oy",
   "hashtag.follow": "Segir etiketa",
   "hashtag.unfollow": "Desegir etiketa",
+  "hashtags.and_other": "…i {count, plural, one {}other {# mas}}",
+  "home.actions.go_to_explore": "Ve los trendes",
+  "home.actions.go_to_suggestions": "Topa a djente para segir",
   "home.column_settings.basic": "Opsyones bazikas",
   "home.column_settings.show_reblogs": "Amostrar repartajasyones",
   "home.column_settings.show_replies": "Amostrar repuestas",
+  "home.explore_prompt.body": "Tu linya prinsipala es una mikstura de publikasyones kon etiketas a las kualas eskojites a segir, la djente a la kuala eskojites a segir i las publikasyones ke eyos repartajan. Si esta demaziado trankila, puedes:",
+  "home.explore_prompt.title": "Esta es tu baza prinsipala en Mastodon.",
   "home.hide_announcements": "Eskonde pregones",
+  "home.pending_critical_update.body": "Por favor aktualiza tu sirvidor de Mastodon pishin!",
   "home.pending_critical_update.link": "Ve aktualizasyones",
+  "home.pending_critical_update.title": "Aktualizasyon de seguridad kritika esta desponivle!",
   "home.show_announcements": "Amostra pregones",
   "interaction_modal.description.favourite": "Kon un kuento en Mastodon, puedes markar esta publikasyon komo favorita para ke el autor sepa ke te plaze i para guadrarla para dempues.",
   "interaction_modal.description.follow": "Kon un kuento en Mastodon, puedes segir a {name} para risivir sus publikasyones en tu linya temporal prinsipala.",
   "interaction_modal.description.reblog": "Kon un kuento en Mastodon, puedes repartajar esta publikasyon para amostrarla a tus suivantes.",
   "interaction_modal.description.reply": "Kon un kuento en Mastodon, puedes arispondir a esta publikasyon.",
+  "interaction_modal.login.action": "Va a tu sirvidor",
+  "interaction_modal.login.prompt": "Domeno del sirvidor de tu kuento, por enshemplo mastodon.social",
+  "interaction_modal.no_account_yet": "No tyenes kuento de Mastodon?",
   "interaction_modal.on_another_server": "En otro sirvidor",
   "interaction_modal.on_this_server": "En este sirvidor",
+  "interaction_modal.sign_in": "No estas konektado kon este sirvidor. Ande tyenes tu kuento?",
   "interaction_modal.title.favourite": "Endika ke te plaze publikasyon de {name}",
   "interaction_modal.title.follow": "Sige a {name}",
   "interaction_modal.title.reblog": "Repartaja publikasyon de {name}",
@@ -356,11 +376,13 @@
   "lightbox.previous": "Anterior",
   "limited_account_hint.action": "Amostra el profil entanto",
   "limited_account_hint.title": "Este profil fue eskondido por los moderadores de {domain}.",
+  "link_preview.author": "Publikasyon de {name}",
   "lists.account.add": "Adjusta a lista",
   "lists.account.remove": "Kita de lista",
   "lists.delete": "Efasa lista",
   "lists.edit": "Edita lista",
   "lists.edit.submit": "Troka titolo",
+  "lists.exclusive": "Eskonder estas publikasyones de linya prinsipala",
   "lists.new.create": "Adjusta lista",
   "lists.new.title_placeholder": "Titolo de mueva lista",
   "lists.replies_policy.followed": "Kualseker utilizardo segido",
@@ -377,6 +399,7 @@
   "mute_modal.hide_notifications": "Eskonder avizos de este utilizador?",
   "mute_modal.indefinite": "Indefinida",
   "navigation_bar.about": "Sovre mozotros",
+  "navigation_bar.advanced_interface": "Avre en la enterfaz avanzada",
   "navigation_bar.blocks": "Utilizadores blokados",
   "navigation_bar.bookmarks": "Markadores",
   "navigation_bar.community_timeline": "Linya de tiempo lokala",
@@ -394,6 +417,7 @@
   "navigation_bar.lists": "Listas",
   "navigation_bar.logout": "Salir",
   "navigation_bar.mutes": "Utilizadores silensiados",
+  "navigation_bar.opened_in_classic_interface": "Publikasyones, kuentos i otras pajinas espesifikas se avren kon preferensyas predeterminadas en la enterfaz web klasika.",
   "navigation_bar.personal": "Personal",
   "navigation_bar.pins": "Publikasyones fiksadas",
   "navigation_bar.preferences": "Preferensyas",
@@ -451,10 +475,33 @@
   "notifications_permission_banner.title": "Nunka te piedres niente",
   "onboarding.action.back": "Va atras",
   "onboarding.actions.back": "Va atras",
+  "onboarding.actions.go_to_explore": "Va a los trendes",
+  "onboarding.actions.go_to_home": "Va a tu linya prinsipala",
+  "onboarding.compose.template": "Ke haber, #Mastodon?",
+  "onboarding.follows.title": "Personaliza tu linya prinsipala",
+  "onboarding.profile.discoverable": "Faz ke mi profil apareska en bushkedas",
   "onboarding.profile.display_name": "Nombre amostrado",
+  "onboarding.profile.display_name_hint": "Tu nombre para amostrar.",
+  "onboarding.profile.note": "Tu deskripsyon",
+  "onboarding.profile.note_hint": "Puedes @enmentar a otra djente o #etiketas…",
+  "onboarding.profile.save_and_continue": "Guadra i kontinua",
+  "onboarding.profile.title": "Konfigurasyon de profil",
+  "onboarding.profile.upload_avatar": "Karga imaje de profil",
+  "onboarding.profile.upload_header": "Karga kavesera de profil",
+  "onboarding.share.message": "Soy {username} en #Mastodon! Segidme en {url}",
+  "onboarding.share.next_steps": "Posivles sigientes pasos:",
+  "onboarding.share.title": "Partaja tu profil",
+  "onboarding.start.skip": "No nesesitas ayudo para ampesar?",
+  "onboarding.start.title": "Lo logrates!",
+  "onboarding.steps.follow_people.body": "El buto de Mastodon es segir a djente interesante.",
+  "onboarding.steps.follow_people.title": "Personaliza tu linya prinsipala",
+  "onboarding.steps.publish_status.title": "Eskrive tu primera publikasyon",
+  "onboarding.steps.setup_profile.title": "Personaliza tu profil",
+  "onboarding.steps.share_profile.title": "Partaja tu profil de Mastodon",
   "picture_in_picture.restore": "Restora",
   "poll.closed": "Serrado",
   "poll.refresh": "Arefreska",
+  "poll.reveal": "Mira los rezultados",
   "poll.total_people": "{count, plural, one {# persona} other {# personas}}",
   "poll.total_votes": "{count, plural, one {# voto} other {# votos}}",
   "poll.vote": "Vota",
@@ -473,6 +520,7 @@
   "privacy.unlisted.short": "No listado",
   "privacy_policy.last_updated": "Ultima aktualizasyon: {date}",
   "privacy_policy.title": "Politika de privasita",
+  "recommended": "Rekomendado",
   "refresh": "Arefreska",
   "regeneration_indicator.label": "Eskargando…",
   "regeneration_indicator.sublabel": "Tu linya de tiempo prinsipala esta preparando!",
@@ -490,6 +538,7 @@
   "reply_indicator.cancel": "Anula",
   "report.block": "Bloka",
   "report.block_explanation": "No veras sus publikasyones. No podra ver tus publikasyones ni segirte. Podra saver ke le blokates.",
+  "report.categories.legal": "Legal",
   "report.categories.other": "Otros",
   "report.categories.spam": "Spam",
   "report.categories.violation": "El kontenido viola una o mas reglas del sirvidor",
@@ -503,9 +552,12 @@
   "report.forward_hint": "Este kuento es de otro sirvidor. Embiar una kopia anonimizada del raporto ayi tamyen?",
   "report.mute": "Silensia",
   "report.mute_explanation": "No veras sus publikasyones. Ainda pueden segirte i no va saver ke le silensiates.",
+  "report.next": "Sigiente",
   "report.placeholder": "Otros komentos",
   "report.reasons.dislike": "No me plaze",
   "report.reasons.dislike_description": "\"No es algo ke kero ver",
+  "report.reasons.legal": "Es ilegal",
+  "report.reasons.legal_description": "Kreyes ke esta violando la ley de tu paiz o el paiz del sirvidor",
   "report.reasons.other": "Es otra koza",
   "report.reasons.other_description": "El problem no es de las otras kategorias",
   "report.reasons.spam": "Es spam",
@@ -525,6 +577,7 @@
   "report.unfollow": "Desegir a @{name}",
   "report.unfollow_explanation": "Estas sigiendo este kuento. Para no ver sus publikasyones en tu linya de tiempo, puedes deshar de segirlo.",
   "report_notification.attached_statuses": "{count, plural, one {{count} publikasyon} other {{count} publikasyones}} atadas",
+  "report_notification.categories.legal": "Legal",
   "report_notification.categories.other": "Otros",
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Violasion de reglas",
@@ -537,8 +590,14 @@
   "search.quick_action.open_url": "Avre URL en Mastodon",
   "search.quick_action.status_search": "Publikasyones ke koresponden kon {x}",
   "search.search_or_paste": "Bushka o apega URL",
+  "search_popout.full_text_search_disabled_message": "No desponivle en {domain}.",
+  "search_popout.full_text_search_logged_out_message": "Solo desponivle kuando estas konektado kon tu kuento.",
+  "search_popout.language_code": "kodiche ISO de lingua",
+  "search_popout.options": "Opsyones de bushkeda",
   "search_popout.quick_actions": "Aksiones rapidas",
   "search_popout.recent": "Bushkedas resientes",
+  "search_popout.specific_date": "dato espesifiko",
+  "search_popout.user": "utilizador",
   "search_results.accounts": "Profiles",
   "search_results.all": "Todos",
   "search_results.hashtags": "Etiketas",
@@ -555,12 +614,32 @@
   "sign_in_banner.create_account": "Kriya kuento",
   "sign_in_banner.sign_in": "Konektate",
   "sign_in_banner.sso_redirect": "Konektate o enrejistrate",
+  "sign_in_banner.text": "Konektate para segir prefiles o etiketas, partajar publikasyones, arispondir a eyas i markar ke te plazen. Puedes tambyen enteraktuar dizde tu kuento en un sirvidor desferente.",
+  "status.admin_account": "Avre la enterfaz de moderasyon para @{name}",
+  "status.admin_domain": "Avre la enterfaz de moderasyon para @{domain}",
+  "status.admin_status": "Avre esto en la enterfaz de moderasyon",
+  "status.block": "Bloka a @{name}",
+  "status.bookmark": "Marka",
+  "status.cancel_reblog_private": "No repartaja",
+  "status.cannot_reblog": "Esta publikasyon no se puede repartajar",
+  "status.copy": "Kopia atadijo de publikasyon",
+  "status.delete": "Efasa",
+  "status.detailed_status": "Vista de konversasyon detalyada",
+  "status.direct": "Enmenta a @{name} en privado",
+  "status.direct_indicator": "Enmentadura privada",
+  "status.edit": "Edita",
+  "status.edited": "Editado {date}",
+  "status.edited_x_times": "Editado {count, plural, one {{count} vez} other {{count} vezes}}",
+  "status.embed": "Inkrusta",
+  "status.favourite": "Te plaze",
   "status.filter": "Filtra esta publikasyon",
   "status.filtered": "Filtrado",
   "status.hide": "Eskonde publikasyon",
   "status.history.created": "{name} kriyo {date}",
   "status.history.edited": "{name} edito {date}",
   "status.load_more": "Eskarga mas",
+  "status.media.open": "Klika para avrir",
+  "status.media.show": "Klika para amostrar",
   "status.media_hidden": "Multimedia eskondidos",
   "status.mention": "Enmenta a @{name}",
   "status.more": "Mas",
@@ -588,6 +667,7 @@
   "status.show_more": "Amostra mas",
   "status.show_more_all": "Amostra mas para todo",
   "status.show_original": "Amostra orijinal",
+  "status.title.with_attachments": "{user} publiko {attachmentCount, plural, one {un anekso} other {{attachmentCount} aneksos}}",
   "status.translate": "Trezlada",
   "status.translated_from_with": "Trezladado dizde {lang} kon {provider}",
   "status.uncached_media_warning": "Vista previa no desponivle",
@@ -636,6 +716,7 @@
   "upload_modal.preview_label": "Vista previa ({ratio})",
   "upload_progress.label": "Kargando...",
   "upload_progress.processing": "Prosesando…",
+  "username.taken": "Akel nombre de utilizador ya esta en uzo. Aprova otruno",
   "video.close": "Serra video",
   "video.download": "Abasha dosya",
   "video.exit_fullscreen": "Sal de ekran kompleto",
diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json
index 58c80e4117f..82f1669b1a2 100644
--- a/app/javascript/mastodon/locales/lt.json
+++ b/app/javascript/mastodon/locales/lt.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "Nėra įrašų",
   "account.featured_tags.title": "{name} rekomenduojamos grotažymės",
   "account.follow": "Sekti",
+  "account.follow_back": "Sekti atgal",
   "account.followers": "Sekėjai",
   "account.followers.empty": "Šio naudotojo dar niekas neseka.",
   "account.followers_counter": "{count, plural, one {{counter} sekėjas (-a)} few {{counter} sekėjai} many {{counter} sekėjo} other {{counter} sekėjų}}",
   "account.following": "Seka",
   "account.following_counter": "{count, plural, one {{counter} Seka} few {{counter} Seka} many {{counter} Seka} other {{counter} Seka}}",
   "account.follows.empty": "Šis (-i) naudotojas (-a) dar nieko neseka.",
-  "account.follows_you": "Seka tave",
   "account.go_to_profile": "Eiti į profilį",
   "account.hide_reblogs": "Slėpti pakėlimus iš @{name}",
   "account.in_memoriam": "Atminimui.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Nutildyti pranešimus",
   "account.mute_short": "Nutildyti",
   "account.muted": "Nutildytas",
+  "account.mutual": "Abipusis",
   "account.no_bio": "Nėra pateikto aprašymo.",
   "account.open_original_page": "Atidaryti originalinį puslapį",
   "account.posts": "Įrašai",
diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json
index 63ec6275ba5..c06a1d93682 100644
--- a/app/javascript/mastodon/locales/lv.json
+++ b/app/javascript/mastodon/locales/lv.json
@@ -37,7 +37,6 @@
   "account.following": "Seko",
   "account.following_counter": "{count, plural, one {{counter} sekojamais} other {{counter} sekojamie}}",
   "account.follows.empty": "Šis lietotājs pagaidām nevienam neseko.",
-  "account.follows_you": "Seko tev",
   "account.go_to_profile": "Doties uz profilu",
   "account.hide_reblogs": "Slēpt @{name} izceltas ziņas",
   "account.in_memoriam": "Piemiņai.",
diff --git a/app/javascript/mastodon/locales/mk.json b/app/javascript/mastodon/locales/mk.json
index 371218bbfec..bdef3f4a5fe 100644
--- a/app/javascript/mastodon/locales/mk.json
+++ b/app/javascript/mastodon/locales/mk.json
@@ -25,7 +25,6 @@
   "account.followers": "Следбеници",
   "account.followers.empty": "Никој не го следи овој корисник сеуште.",
   "account.follows.empty": "Корисникот не следи никој сеуште.",
-  "account.follows_you": "Те следи тебе",
   "account.hide_reblogs": "Сокриј буст од @{name}",
   "account.link_verified_on": "Сопстевноста на овај линк беше проверен на {date}",
   "account.locked_info": "Статусот на приватност на овај корисник е сетиран како заклучен. Корисникот одлучува кој можи да го следи него.",
diff --git a/app/javascript/mastodon/locales/ml.json b/app/javascript/mastodon/locales/ml.json
index b00cedc6fcc..11636646b26 100644
--- a/app/javascript/mastodon/locales/ml.json
+++ b/app/javascript/mastodon/locales/ml.json
@@ -26,7 +26,6 @@
   "account.following": "പിന്തുടരുന്നു",
   "account.following_counter": "{count, plural, one {{counter} പിന്തുടരുന്നു} other {{counter} പിന്തുടരുന്നു}}",
   "account.follows.empty": "ഈ ഉപയോക്താവ് ആരേയും ഇതുവരെ പിന്തുടരുന്നില്ല.",
-  "account.follows_you": "നിങ്ങളെ പിന്തുടരുന്നു",
   "account.go_to_profile": "പ്രൊഫൈലിലേക്ക് പോകാം",
   "account.hide_reblogs": "@{name} ബൂസ്റ്റ് ചെയ്തവ മറയ്കുക",
   "account.joined_short": "ജോയിൻ ചെയ്‌തിരിക്കുന്നു",
diff --git a/app/javascript/mastodon/locales/mr.json b/app/javascript/mastodon/locales/mr.json
index 75b75375b71..7f5b7d65248 100644
--- a/app/javascript/mastodon/locales/mr.json
+++ b/app/javascript/mastodon/locales/mr.json
@@ -35,7 +35,6 @@
   "account.following": "अनुसरण",
   "account.following_counter": "{count, plural, one {{counter} following} other {{counter} following}}",
   "account.follows.empty": "हा वापरकर्ता अजूनपर्यंत कोणाचा अनुयायी नाही.",
-  "account.follows_you": "तुमचा अनुयायी आहे",
   "account.go_to_profile": "प्रोफाइल वर जा",
   "account.hide_reblogs": "@{name} पासून सर्व बूस्ट लपवा",
   "account.joined_short": "सामील झाले",
diff --git a/app/javascript/mastodon/locales/ms.json b/app/javascript/mastodon/locales/ms.json
index 724e07ae76a..50a48db1ea0 100644
--- a/app/javascript/mastodon/locales/ms.json
+++ b/app/javascript/mastodon/locales/ms.json
@@ -37,7 +37,6 @@
   "account.following": "Mengikuti",
   "account.following_counter": "{count, plural, one {{counter} Diikuti} other {{counter} Diikuti}}",
   "account.follows.empty": "Pengguna ini belum mengikuti sesiapa.",
-  "account.follows_you": "Mengikuti anda",
   "account.go_to_profile": "Pergi ke profil",
   "account.hide_reblogs": "Sembunyikan galakan daripada @{name}",
   "account.in_memoriam": "Dalam Memoriam.",
diff --git a/app/javascript/mastodon/locales/my.json b/app/javascript/mastodon/locales/my.json
index 917419d172e..3ca03b616a9 100644
--- a/app/javascript/mastodon/locales/my.json
+++ b/app/javascript/mastodon/locales/my.json
@@ -38,7 +38,6 @@
   "account.following": "စောင့်ကြည့်နေသည်",
   "account.following_counter": "{count, plural, one {စောင့်ကြည့်ထားသူ {counter}} other {စောင့်ကြည့်ထားသူများ {counter}}}",
   "account.follows.empty": "ဤသူသည် မည်သူ့ကိုမျှ စောင့်ကြည့်ခြင်း မရှိသေးပါ။",
-  "account.follows_you": "သင့်ကို စောင့်ကြည့်နေသည်",
   "account.go_to_profile": "ပရိုဖိုင်းသို့ သွားရန်",
   "account.hide_reblogs": "@{name} ၏ မျှဝေမှုကို ဝှက်ထားရန်",
   "account.in_memoriam": "အမှတ်တရ",
diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json
index 295b420fd45..86617d4a54b 100644
--- a/app/javascript/mastodon/locales/nl.json
+++ b/app/javascript/mastodon/locales/nl.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "Geen berichten",
   "account.featured_tags.title": "Uitgelichte hashtags van {name}",
   "account.follow": "Volgen",
+  "account.follow_back": "Terugvolgen",
   "account.followers": "Volgers",
   "account.followers.empty": "Deze gebruiker heeft nog geen volgers of heeft deze verborgen.",
   "account.followers_counter": "{count, plural, one {{counter} volger} other {{counter} volgers}}",
   "account.following": "Volgend",
   "account.following_counter": "{count, plural, one {{counter} volgend} other {{counter} volgend}}",
   "account.follows.empty": "Deze gebruiker volgt nog niemand of heeft deze verborgen.",
-  "account.follows_you": "Volgt jou",
   "account.go_to_profile": "Ga naar profiel",
   "account.hide_reblogs": "Boosts van @{name} verbergen",
   "account.in_memoriam": "In memoriam.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Meldingen negeren",
   "account.mute_short": "Negeren",
   "account.muted": "Genegeerd",
+  "account.mutual": "Jullie volgen elkaar",
   "account.no_bio": "Geen beschrijving opgegeven.",
   "account.open_original_page": "Originele pagina openen",
   "account.posts": "Berichten",
diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json
index 4750b1adc33..3ef2f80eaf7 100644
--- a/app/javascript/mastodon/locales/nn.json
+++ b/app/javascript/mastodon/locales/nn.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "Ingen innlegg",
   "account.featured_tags.title": "{name} sine framheva emneknaggar",
   "account.follow": "Fylg",
+  "account.follow_back": "Følg tilbake",
   "account.followers": "Fylgjarar",
   "account.followers.empty": "Ingen fylgjer denne brukaren enno.",
   "account.followers_counter": "{count, plural, one {{counter} fylgjar} other {{counter} fylgjarar}}",
   "account.following": "Fylgjer",
   "account.following_counter": "{count, plural, one {Fylgjer {counter}} other {Fylgjer {counter}}}",
   "account.follows.empty": "Denne brukaren fylgjer ikkje nokon enno.",
-  "account.follows_you": "Fylgjer deg",
   "account.go_to_profile": "Gå til profil",
   "account.hide_reblogs": "Skjul framhevingar frå @{name}",
   "account.in_memoriam": "Til minne om.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Demp varslingar",
   "account.mute_short": "Demp",
   "account.muted": "Målbunden",
+  "account.mutual": "Felles",
   "account.no_bio": "Inga skildring er gjeven.",
   "account.open_original_page": "Opne originalsida",
   "account.posts": "Tut",
diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json
index 1739d4aa31f..29eaeddff59 100644
--- a/app/javascript/mastodon/locales/no.json
+++ b/app/javascript/mastodon/locales/no.json
@@ -38,7 +38,6 @@
   "account.following": "Følger",
   "account.following_counter": "{count, plural, one {{counter} som følges} other {{counter} som følges}}",
   "account.follows.empty": "Denne brukeren følger ikke noen enda.",
-  "account.follows_you": "Følger deg",
   "account.go_to_profile": "Gå til profil",
   "account.hide_reblogs": "Skjul fremhevinger fra @{name}",
   "account.in_memoriam": "Til minne om.",
diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json
index 3812057fb04..833bfe6acef 100644
--- a/app/javascript/mastodon/locales/oc.json
+++ b/app/javascript/mastodon/locales/oc.json
@@ -34,7 +34,6 @@
   "account.following": "Abonat",
   "account.following_counter": "{count, plural, one {{counter} Abonaments} other {{counter} Abonaments}}",
   "account.follows.empty": "Aqueste utilizaire sèc pas degun pel moment.",
-  "account.follows_you": "Vos sèc",
   "account.go_to_profile": "Anar al perfil",
   "account.hide_reblogs": "Rescondre los partatges de @{name}",
   "account.in_memoriam": "En Memòria.",
diff --git a/app/javascript/mastodon/locales/pa.json b/app/javascript/mastodon/locales/pa.json
index 708d8c3e981..132b695cda8 100644
--- a/app/javascript/mastodon/locales/pa.json
+++ b/app/javascript/mastodon/locales/pa.json
@@ -18,7 +18,6 @@
   "account.followers.empty": "ਇਸ ਵਰਤੋਂਕਾਰ ਨੂੰ ਹਾਲੇ ਕੋਈ ਫ਼ਾਲੋ ਨਹੀਂ ਕਰਦਾ ਹੈ।",
   "account.following": "ਫ਼ਾਲੋ ਕੀਤਾ",
   "account.follows.empty": "ਇਹ ਵਰਤੋਂਕਾਰ ਹਾਲੇ ਕਿਸੇ ਨੂੰ ਫ਼ਾਲੋ ਨਹੀਂ ਕਰਦਾ ਹੈ।",
-  "account.follows_you": "ਤੁਹਾਨੂੰ ਫ਼ਾਲੋ ਕਰੋ",
   "account.media": "ਮੀਡੀਆ",
   "account.muted": "ਮੌਨ ਕੀਤੀਆਂ",
   "account.posts": "ਪੋਸਟਾਂ",
diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json
index dc77f8f4e02..9a3710da711 100644
--- a/app/javascript/mastodon/locales/pl.json
+++ b/app/javascript/mastodon/locales/pl.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "Brak postów",
   "account.featured_tags.title": "Polecane hasztagi {name}",
   "account.follow": "Obserwuj",
+  "account.follow_back": "Obserwuj wzajemnie",
   "account.followers": "Obserwujący",
   "account.followers.empty": "Nikt jeszcze nie obserwuje tego użytkownika.",
   "account.followers_counter": "{count, plural, one {{counter} obserwujący} few {{counter} obserwujących} many {{counter} obserwujących} other {{counter} obserwujących}}",
   "account.following": "Obserwowani",
   "account.following_counter": "{count, plural, one {{counter} obserwowany} few {{counter} obserwowanych} many {{counter} obserwowanych} other {{counter} obserwowanych}}",
   "account.follows.empty": "Ten użytkownik nie obserwuje jeszcze nikogo.",
-  "account.follows_you": "Obserwuje Cię",
   "account.go_to_profile": "Przejdź do profilu",
   "account.hide_reblogs": "Ukryj podbicia od @{name}",
   "account.in_memoriam": "Ku pamięci.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Wycisz powiadomienia",
   "account.mute_short": "Wycisz",
   "account.muted": "Wyciszony",
+  "account.mutual": "Przyjaciele",
   "account.no_bio": "Brak opisu.",
   "account.open_original_page": "Otwórz stronę oryginalną",
   "account.posts": "Wpisy",
diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json
index e7975dd76e4..482cc8ee735 100644
--- a/app/javascript/mastodon/locales/pt-BR.json
+++ b/app/javascript/mastodon/locales/pt-BR.json
@@ -38,7 +38,6 @@
   "account.following": "Seguindo",
   "account.following_counter": "{count, plural, one {segue {counter}} other {segue {counter}}}",
   "account.follows.empty": "Nada aqui.",
-  "account.follows_you": "te segue",
   "account.go_to_profile": "Ir ao perfil",
   "account.hide_reblogs": "Ocultar boosts de @{name}",
   "account.in_memoriam": "Em memória.",
diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json
index d5055b0dc53..a6d0ffee9a5 100644
--- a/app/javascript/mastodon/locales/pt-PT.json
+++ b/app/javascript/mastodon/locales/pt-PT.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "Sem publicações",
   "account.featured_tags.title": "#Etiquetas destacadas por {name}",
   "account.follow": "Seguir",
+  "account.follow_back": "Seguir de volta",
   "account.followers": "Seguidores",
   "account.followers.empty": "Ainda ninguém segue este utilizador.",
   "account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} seguidores}}",
   "account.following": "A seguir",
   "account.following_counter": "{count, plural, other {A seguir {counter}}}",
   "account.follows.empty": "Este utilizador ainda não segue ninguém.",
-  "account.follows_you": "Segue-te",
   "account.go_to_profile": "Ir para o perfil",
   "account.hide_reblogs": "Esconder partilhas de @{name}",
   "account.in_memoriam": "Em Memória.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Silenciar notificações",
   "account.mute_short": "Silenciar",
   "account.muted": "Silenciada",
+  "account.mutual": "Mútuo",
   "account.no_bio": "Nenhuma descrição fornecida.",
   "account.open_original_page": "Abrir a página original",
   "account.posts": "Publicações",
diff --git a/app/javascript/mastodon/locales/ro.json b/app/javascript/mastodon/locales/ro.json
index 5355f9935ae..88dbfa4b8ec 100644
--- a/app/javascript/mastodon/locales/ro.json
+++ b/app/javascript/mastodon/locales/ro.json
@@ -36,7 +36,6 @@
   "account.following": "Urmăriți",
   "account.following_counter": "{count, plural, one {Un abonament} few {{counter} abonamente} other {{counter} de abonamente}}",
   "account.follows.empty": "Momentan acest utilizator nu are niciun abonament.",
-  "account.follows_you": "Este abonat la tine",
   "account.go_to_profile": "Mergi la profil",
   "account.hide_reblogs": "Ascunde distribuirile de la @{name}",
   "account.joined_short": "Înscris",
diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json
index f0c48236b91..37a8328c98e 100644
--- a/app/javascript/mastodon/locales/ru.json
+++ b/app/javascript/mastodon/locales/ru.json
@@ -38,7 +38,6 @@
   "account.following": "Подписки",
   "account.following_counter": "{count, plural, one {{counter} подписка} many {{counter} подписок} other {{counter} подписки}}",
   "account.follows.empty": "Этот пользователь пока ни на кого не подписался.",
-  "account.follows_you": "Подписан(а) на вас",
   "account.go_to_profile": "Перейти к профилю",
   "account.hide_reblogs": "Скрыть продвижения от @{name}",
   "account.in_memoriam": "В Памяти.",
diff --git a/app/javascript/mastodon/locales/sa.json b/app/javascript/mastodon/locales/sa.json
index 59379343b9c..051ec5d6f80 100644
--- a/app/javascript/mastodon/locales/sa.json
+++ b/app/javascript/mastodon/locales/sa.json
@@ -36,7 +36,6 @@
   "account.following": "अनुसरति",
   "account.following_counter": "{count, plural, one {{counter} अनुसृतः} two {{counter} अनुसृतौ} other {{counter} अनुसृताः}}",
   "account.follows.empty": "न कोऽप्यनुसृतो वर्तते",
-  "account.follows_you": "त्वामनुसरति",
   "account.go_to_profile": "प्रोफायिलं गच्छ",
   "account.hide_reblogs": "@{name} मित्रस्य प्रकाशनानि छिद्यन्ताम्",
   "account.in_memoriam": "स्मृत्याम्",
diff --git a/app/javascript/mastodon/locales/sc.json b/app/javascript/mastodon/locales/sc.json
index 7f29525e734..89e951f77d3 100644
--- a/app/javascript/mastodon/locales/sc.json
+++ b/app/javascript/mastodon/locales/sc.json
@@ -30,7 +30,6 @@
   "account.following": "Sighende",
   "account.following_counter": "{count, plural, one {Sighende a {counter}} other {Sighende a {counter}}}",
   "account.follows.empty": "Custa persone non sighit ancora a nemos.",
-  "account.follows_you": "Ti sighit",
   "account.hide_reblogs": "Cua is cumpartziduras de @{name}",
   "account.in_memoriam": "In memoriam.",
   "account.joined_short": "At aderidu",
diff --git a/app/javascript/mastodon/locales/sco.json b/app/javascript/mastodon/locales/sco.json
index 28dac9c2a28..0378cd2926e 100644
--- a/app/javascript/mastodon/locales/sco.json
+++ b/app/javascript/mastodon/locales/sco.json
@@ -35,7 +35,6 @@
   "account.following": "Follaein",
   "account.following_counter": "{count, plural, one {{counter} Follaein} other {{counter} Follaein}}",
   "account.follows.empty": "This uiser disnae follae oniebody yit.",
-  "account.follows_you": "Follaes ye",
   "account.go_to_profile": "Gang tae profile",
   "account.hide_reblogs": "Dinnae shaw heezes fae @{name}",
   "account.joined_short": "Jynt",
diff --git a/app/javascript/mastodon/locales/si.json b/app/javascript/mastodon/locales/si.json
index 835f699b82c..c2d2a41cc50 100644
--- a/app/javascript/mastodon/locales/si.json
+++ b/app/javascript/mastodon/locales/si.json
@@ -26,7 +26,6 @@
   "account.following": "අනුගමන",
   "account.following_counter": "{count, plural, one {අනුගමන {counter}} other {අනුගමන {counter}}}",
   "account.follows.empty": "තවමත් කිසිවෙක් අනුගමනය නොකරයි.",
-  "account.follows_you": "ඔබව අනුගමනය කරයි",
   "account.go_to_profile": "පැතිකඩට යන්න",
   "account.joined_short": "එක් වූ දිනය",
   "account.link_verified_on": "මෙම සබැඳියේ අයිතිය {date} දී පරීක්‍ෂා කෙරිණි",
diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json
index 0eb4041988a..02425d73e0f 100644
--- a/app/javascript/mastodon/locales/sk.json
+++ b/app/javascript/mastodon/locales/sk.json
@@ -38,7 +38,6 @@
   "account.following": "Sledujem",
   "account.following_counter": "{count, plural, one {{counter} Sledovaných} other {{counter} Sledujúcich}}",
   "account.follows.empty": "Tento používateľ ešte nikoho nesleduje.",
-  "account.follows_you": "Sleduje ťa",
   "account.go_to_profile": "Prejdi na profil",
   "account.hide_reblogs": "Skry zdieľania od @{name}",
   "account.in_memoriam": "In Memoriam.",
diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json
index 4ef2681f9a8..b3998b91104 100644
--- a/app/javascript/mastodon/locales/sl.json
+++ b/app/javascript/mastodon/locales/sl.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "Ni objav",
   "account.featured_tags.title": "Izpostavljeni ključniki {name}",
   "account.follow": "Sledi",
+  "account.follow_back": "Sledi nazaj",
   "account.followers": "Sledilci",
   "account.followers.empty": "Nihče ne sledi temu uporabniku.",
   "account.followers_counter": "{count, plural, one {ima {counter} sledilca} two {ima {counter} sledilca} few {ima {counter} sledilce} other {ima {counter} sledilcev}}",
   "account.following": "Sledim",
   "account.following_counter": "{count, plural, one {sledi {count} osebi} two {sledi {count} osebama} few {sledi {count} osebam} other {sledi {count} osebam}}",
   "account.follows.empty": "Ta uporabnik še ne sledi nikomur.",
-  "account.follows_you": "Vam sledi",
   "account.go_to_profile": "Pojdi na profil",
   "account.hide_reblogs": "Skrij izpostavitve od @{name}",
   "account.in_memoriam": "V spomin.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Utišaj obvestila",
   "account.mute_short": "Utišaj",
   "account.muted": "Utišan",
+  "account.mutual": "Vzajemno",
   "account.no_bio": "Ni opisa.",
   "account.open_original_page": "Odpri izvirno stran",
   "account.posts": "Objave",
diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json
index 710224e1cbb..48eac1487ec 100644
--- a/app/javascript/mastodon/locales/sq.json
+++ b/app/javascript/mastodon/locales/sq.json
@@ -38,7 +38,6 @@
   "account.following": "Ndjekje",
   "account.following_counter": "{count, plural, one {{counter} i Ndjekur} other {{counter} të Ndjekur}}",
   "account.follows.empty": "Ky përdorues ende s’ndjek kënd.",
-  "account.follows_you": "Ju ndjek",
   "account.go_to_profile": "Kalo te profili",
   "account.hide_reblogs": "Fshih përforcime nga @{name}",
   "account.in_memoriam": "In Memoriam.",
diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json
index 35bb8f99297..59ad0ae84ea 100644
--- a/app/javascript/mastodon/locales/sr-Latn.json
+++ b/app/javascript/mastodon/locales/sr-Latn.json
@@ -38,7 +38,6 @@
   "account.following": "Prati",
   "account.following_counter": "{count, plural, one {{counter} prati} few {{counter} prati} other {{counter} prati}}",
   "account.follows.empty": "Ovaj korisnik još uvek nikog ne prati.",
-  "account.follows_you": "Prati vas",
   "account.go_to_profile": "Idi na profil",
   "account.hide_reblogs": "Sakrij podržavanja @{name}",
   "account.in_memoriam": "U znak sećanja na.",
diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json
index 552c04d13f6..79786b8d442 100644
--- a/app/javascript/mastodon/locales/sr.json
+++ b/app/javascript/mastodon/locales/sr.json
@@ -38,7 +38,6 @@
   "account.following": "Прати",
   "account.following_counter": "{count, plural, one {{counter} прати} few {{counter} прати} other {{counter} прати}}",
   "account.follows.empty": "Овај корисник још увек никог не прати.",
-  "account.follows_you": "Прати вас",
   "account.go_to_profile": "Иди на профил",
   "account.hide_reblogs": "Сакриј подржавања од @{name}",
   "account.in_memoriam": "У знак сећања на.",
@@ -53,6 +52,7 @@
   "account.mute_notifications_short": "Искључи обавештења",
   "account.mute_short": "Искључи",
   "account.muted": "Игнорисан",
+  "account.mutual": "Заједнички",
   "account.no_bio": "Нема описа.",
   "account.open_original_page": "Отвори оригиналну страницу",
   "account.posts": "Објаве",
diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json
index 8a07da72d8e..d3ca776bd9e 100644
--- a/app/javascript/mastodon/locales/sv.json
+++ b/app/javascript/mastodon/locales/sv.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "Inga inlägg",
   "account.featured_tags.title": "{name}s utvalda hashtaggar",
   "account.follow": "Följ",
+  "account.follow_back": "Följ tillbaka",
   "account.followers": "Följare",
   "account.followers.empty": "Ingen följer denna användare än.",
   "account.followers_counter": "{count, plural, one {{counter} följare} other {{counter} följare}}",
   "account.following": "Följer",
   "account.following_counter": "{count, plural, one {{counter} följd} other {{counter} följda}}",
   "account.follows.empty": "Denna användare följer inte någon än.",
-  "account.follows_you": "Följer dig",
   "account.go_to_profile": "Gå till profilen",
   "account.hide_reblogs": "Dölj boostar från @{name}",
   "account.in_memoriam": "Till minne av.",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Stäng av aviseringsljud",
   "account.mute_short": "Tysta",
   "account.muted": "Tystad",
+  "account.mutual": "Ömsesidig",
   "account.no_bio": "Ingen beskrivning angiven.",
   "account.open_original_page": "Öppna den ursprungliga sidan",
   "account.posts": "Inlägg",
diff --git a/app/javascript/mastodon/locales/ta.json b/app/javascript/mastodon/locales/ta.json
index ce9042e62b9..6b2332d5b8a 100644
--- a/app/javascript/mastodon/locales/ta.json
+++ b/app/javascript/mastodon/locales/ta.json
@@ -25,7 +25,6 @@
   "account.following": "பின்தொடரும்",
   "account.following_counter": "{count, plural,one {{counter} சந்தா} other {{counter} சந்தாக்கள்}}",
   "account.follows.empty": "இந்த பயனர் இதுவரை யாரையும் பின்தொடரவில்லை.",
-  "account.follows_you": "உங்களைப் பின்தொடர்கிறார்",
   "account.hide_reblogs": "இருந்து ஊக்கியாக மறை @{name}",
   "account.link_verified_on": "இந்த இணைப்பை உரிமையாளர் சரிபார்க்கப்பட்டது {date}",
   "account.locked_info": "இந்தக் கணக்கு தனியுரிமை நிலை பூட்டப்பட்டுள்ளது. அவர்களைப் பின்தொடர்பவர் யார் என்பதை உரிமையாளர் கைமுறையாக மதிப்பாய்வு செய்கிறார்.",
diff --git a/app/javascript/mastodon/locales/te.json b/app/javascript/mastodon/locales/te.json
index f21c0ef57ad..3c231871fa7 100644
--- a/app/javascript/mastodon/locales/te.json
+++ b/app/javascript/mastodon/locales/te.json
@@ -12,7 +12,6 @@
   "account.followers": "అనుచరులు",
   "account.followers.empty": "ఈ వినియోగదారుడిని ఇంకా ఎవరూ అనుసరించడంలేదు.",
   "account.follows.empty": "ఈ వినియోగదారి ఇంకా ఎవరినీ అనుసరించడంలేదు.",
-  "account.follows_you": "మిమ్మల్ని అనుసరిస్తున్నారు",
   "account.hide_reblogs": "@{name} నుంచి బూస్ట్ లను దాచిపెట్టు",
   "account.link_verified_on": "ఈ లంకె యొక్క యాజమాన్యం {date}న పరీక్షించబడింది",
   "account.locked_info": "ఈ ఖాతా యొక్క గోప్యత స్థితి లాక్ చేయబడి వుంది. ఈ ఖాతాను ఎవరు అనుసరించవచ్చో యజమానే నిర్ణయం తీసుకుంటారు.",
diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json
index 67b920393a3..d14e37517d5 100644
--- a/app/javascript/mastodon/locales/th.json
+++ b/app/javascript/mastodon/locales/th.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "ไม่มีโพสต์",
   "account.featured_tags.title": "แฮชแท็กที่น่าสนใจของ {name}",
   "account.follow": "ติดตาม",
+  "account.follow_back": "ติดตามกลับ",
   "account.followers": "ผู้ติดตาม",
   "account.followers.empty": "ยังไม่มีใครติดตามผู้ใช้นี้",
   "account.followers_counter": "{count, plural, other {{counter} ผู้ติดตาม}}",
   "account.following": "กำลังติดตาม",
   "account.following_counter": "{count, plural, other {{counter} กำลังติดตาม}}",
   "account.follows.empty": "ผู้ใช้นี้ยังไม่ได้ติดตามใคร",
-  "account.follows_you": "ติดตามคุณ",
   "account.go_to_profile": "ไปยังโปรไฟล์",
   "account.hide_reblogs": "ซ่อนการดันจาก @{name}",
   "account.in_memoriam": "เพื่อระลึกถึง",
diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json
index 8eb09bb7cbd..e85db817b9f 100644
--- a/app/javascript/mastodon/locales/tr.json
+++ b/app/javascript/mastodon/locales/tr.json
@@ -32,20 +32,20 @@
   "account.featured_tags.last_status_never": "Gönderi yok",
   "account.featured_tags.title": "{name} kişisinin öne çıkan etiketleri",
   "account.follow": "Takip et",
+  "account.follow_back": "Geri takip et",
   "account.followers": "Takipçi",
   "account.followers.empty": "Henüz kimse bu kullanıcıyı takip etmiyor.",
   "account.followers_counter": "{count, plural, one {{counter} Takipçi} other {{counter} Takipçi}}",
   "account.following": "Takip Ediliyor",
   "account.following_counter": "{count, plural, one {{counter} Takip Edilen} other {{counter} Takip Edilen}}",
   "account.follows.empty": "Bu kullanıcı henüz kimseyi takip etmiyor.",
-  "account.follows_you": "Seni takip ediyor",
   "account.go_to_profile": "Profile git",
   "account.hide_reblogs": "@{name} kişisinin boostlarını gizle",
   "account.in_memoriam": "Hatırasına.",
   "account.joined_short": "Katıldı",
   "account.languages": "Abone olunan dilleri değiştir",
   "account.link_verified_on": "Bu bağlantının sahipliği {date} tarihinde denetlendi",
-  "account.locked_info": "Bu hesabın gizlilik durumu gizli olarak ayarlanmış. Sahibi, onu kimin takip edebileceğini manuel olarak onaylıyor.",
+  "account.locked_info": "Bu hesabın gizlilik durumu gizli olarak ayarlanmış. Sahibi, onu kimin takip edebileceğini elle onaylıyor.",
   "account.media": "Medya",
   "account.mention": "@{name} kişisinden bahset",
   "account.moved_to": "{name} yeni hesabının artık şu olduğunu belirtti:",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "Bildirimleri sessize al",
   "account.mute_short": "Sessize al",
   "account.muted": "Susturuldu",
+  "account.mutual": "Karşılıklı",
   "account.no_bio": "Herhangi bir açıklama belirtilmedi.",
   "account.open_original_page": "Asıl sayfayı aç",
   "account.posts": "Gönderiler",
@@ -345,7 +346,7 @@
   "keyboard_shortcuts.down": "Listede aşağıya inmek için",
   "keyboard_shortcuts.enter": "gönderiyi aç",
   "keyboard_shortcuts.favourite": "Gönderiyi favorilerine ekle",
-  "keyboard_shortcuts.favourites": "Favoriler listeni aç",
+  "keyboard_shortcuts.favourites": "Gözde listeni aç",
   "keyboard_shortcuts.federated": "Federe akışı aç",
   "keyboard_shortcuts.heading": "Klavye kısayolları",
   "keyboard_shortcuts.home": "Ana akışı aç",
diff --git a/app/javascript/mastodon/locales/tt.json b/app/javascript/mastodon/locales/tt.json
index 6727f3e59ab..47fe60bd252 100644
--- a/app/javascript/mastodon/locales/tt.json
+++ b/app/javascript/mastodon/locales/tt.json
@@ -35,7 +35,6 @@
   "account.following": "Язылулар",
   "account.following_counter": "{count, plural, one {{counter} язылу} other {{counter} язылу}}",
   "account.follows.empty": "Беркемгә дә язылмаган әле.",
-  "account.follows_you": "Сезгә язылган",
   "account.go_to_profile": "Профильгә күчү",
   "account.hide_reblogs": "Скрывать көчен нче @{name}",
   "account.in_memoriam": "Истәлегенә.",
diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json
index 58a14c0ed22..92eacaad144 100644
--- a/app/javascript/mastodon/locales/uk.json
+++ b/app/javascript/mastodon/locales/uk.json
@@ -38,7 +38,6 @@
   "account.following": "Ви стежите",
   "account.following_counter": "{count, plural, one {{counter} підписка} few {{counter} підписки} many {{counter} підписок} other {{counter} підписки}}",
   "account.follows.empty": "Цей користувач ще ні на кого не підписався.",
-  "account.follows_you": "Підписується на вас",
   "account.go_to_profile": "Перейти до профілю",
   "account.hide_reblogs": "Сховати поширення від @{name}",
   "account.in_memoriam": "Пам'ятник.",
@@ -53,6 +52,7 @@
   "account.mute_notifications_short": "Не сповіщати",
   "account.mute_short": "Ігнорувати",
   "account.muted": "Приховується",
+  "account.mutual": "Взаємно",
   "account.no_bio": "Немає опису.",
   "account.open_original_page": "Відкрити оригінальну сторінку",
   "account.posts": "Дописи",
diff --git a/app/javascript/mastodon/locales/ur.json b/app/javascript/mastodon/locales/ur.json
index 8fc3aff030e..563b2dedf8e 100644
--- a/app/javascript/mastodon/locales/ur.json
+++ b/app/javascript/mastodon/locales/ur.json
@@ -32,7 +32,6 @@
   "account.following": "فالو کر رہے ہیں",
   "account.following_counter": "{count, plural, one {{counter} پیروی کر رہے ہیں} other {{counter} پیروی کر رہے ہیں}}",
   "account.follows.empty": "\"یہ صارف ہنوز کسی کی پیروی نہیں کرتا ہے\".",
-  "account.follows_you": "آپ کا پیروکار ہے",
   "account.go_to_profile": "پروفائل پر جائیں",
   "account.hide_reblogs": "@{name} سے فروغ چھپائیں",
   "account.in_memoriam": "یادگار میں۔",
diff --git a/app/javascript/mastodon/locales/uz.json b/app/javascript/mastodon/locales/uz.json
index 026cc115c1c..8eeee42a5e6 100644
--- a/app/javascript/mastodon/locales/uz.json
+++ b/app/javascript/mastodon/locales/uz.json
@@ -35,7 +35,6 @@
   "account.following": "Kuzatish",
   "account.following_counter": "{count, plural, one {{counter} ga Muxlis} other {{counter} larga muxlis}}",
   "account.follows.empty": "Bu foydalanuvchi hali hech kimni kuzatmagan.",
-  "account.follows_you": "Sizga obuna",
   "account.go_to_profile": "Profilga o'tish",
   "account.hide_reblogs": "@{name} dan boostlarni yashirish",
   "account.joined_short": "Qo'shilgan",
diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json
index 9ac90b407e1..721c7cd4ad1 100644
--- a/app/javascript/mastodon/locales/vi.json
+++ b/app/javascript/mastodon/locales/vi.json
@@ -38,7 +38,6 @@
   "account.following": "Đang theo dõi",
   "account.following_counter": "{count, plural, one {{counter} Theo dõi} other {{counter} Theo dõi}}",
   "account.follows.empty": "Người này chưa theo dõi ai.",
-  "account.follows_you": "Đang theo dõi bạn",
   "account.go_to_profile": "Xem hồ sơ",
   "account.hide_reblogs": "Ẩn tút @{name} đăng lại",
   "account.in_memoriam": "Tưởng Niệm.",
diff --git a/app/javascript/mastodon/locales/zgh.json b/app/javascript/mastodon/locales/zgh.json
index 5896a25b022..008a9636db4 100644
--- a/app/javascript/mastodon/locales/zgh.json
+++ b/app/javascript/mastodon/locales/zgh.json
@@ -12,7 +12,6 @@
   "account.edit_profile": "ⵙⵏⴼⵍ ⵉⴼⵔⵙ",
   "account.follow": "ⴹⴼⵕ",
   "account.followers": "ⵉⵎⴹⴼⴰⵕⵏ",
-  "account.follows_you": "ⴹⴼⵕⵏ ⴽⵯⵏ",
   "account.media": "ⴰⵙⵏⵖⵎⵉⵙ",
   "account.mute": "ⵥⵥⵉⵥⵏ @{name}",
   "account.muted": "ⵉⵜⵜⵓⵥⵉⵥⵏ",
diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json
index 1678fd72937..575e0c7aebe 100644
--- a/app/javascript/mastodon/locales/zh-CN.json
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "暂无嘟文",
   "account.featured_tags.title": "{name} 的精选标签",
   "account.follow": "关注",
+  "account.follow_back": "回关",
   "account.followers": "关注者",
   "account.followers.empty": "目前无人关注此用户。",
   "account.followers_counter": "被 {counter} 人关注",
   "account.following": "正在关注",
   "account.following_counter": "正在关注 {counter} 人",
   "account.follows.empty": "此用户目前未关注任何人。",
-  "account.follows_you": "关注了你",
   "account.go_to_profile": "前往个人资料页",
   "account.hide_reblogs": "隐藏来自 @{name} 的转嘟",
   "account.in_memoriam": "谨此悼念。",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "关闭通知",
   "account.mute_short": "隐藏",
   "account.muted": "已隐藏",
+  "account.mutual": "互相关注",
   "account.no_bio": "未提供描述。",
   "account.open_original_page": "打开原始页面",
   "account.posts": "嘟文",
diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json
index 263c707993e..cd0845b6e2e 100644
--- a/app/javascript/mastodon/locales/zh-HK.json
+++ b/app/javascript/mastodon/locales/zh-HK.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "暫無文章",
   "account.featured_tags.title": "{name} 的精選標籤",
   "account.follow": "關注",
+  "account.follow_back": "追蹤對方",
   "account.followers": "追蹤者",
   "account.followers.empty": "尚未有人追蹤這位使用者。",
   "account.followers_counter": "有 {count, plural,one {{counter} 個} other {{counter} 個}}追蹤者",
   "account.following": "正在追蹤",
   "account.following_counter": "正在追蹤 {count, plural,one {{counter}}other {{counter} 人}}",
   "account.follows.empty": "這位使用者尚未追蹤任何人。",
-  "account.follows_you": "追蹤你",
   "account.go_to_profile": "前往個人檔案",
   "account.hide_reblogs": "隱藏 @{name} 的轉推",
   "account.in_memoriam": "謹此悼念。",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "靜音通知",
   "account.mute_short": "靜音",
   "account.muted": "靜音",
+  "account.mutual": "互相追蹤",
   "account.no_bio": "未提供描述。",
   "account.open_original_page": "打開原始頁面",
   "account.posts": "帖文",
diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json
index 8484e00c830..99839369535 100644
--- a/app/javascript/mastodon/locales/zh-TW.json
+++ b/app/javascript/mastodon/locales/zh-TW.json
@@ -32,13 +32,13 @@
   "account.featured_tags.last_status_never": "沒有嘟文",
   "account.featured_tags.title": "{name} 的推薦主題標籤",
   "account.follow": "跟隨",
+  "account.follow_back": "跟隨回去",
   "account.followers": "跟隨者",
   "account.followers.empty": "尚未有人跟隨這位使用者。",
   "account.followers_counter": "被 {count, plural, other {{counter} 人}}跟隨",
   "account.following": "跟隨中",
   "account.following_counter": "正在跟隨 {count,plural,other {{counter} 人}}",
   "account.follows.empty": "這位使用者尚未跟隨任何人。",
-  "account.follows_you": "跟隨了您",
   "account.go_to_profile": "前往個人檔案",
   "account.hide_reblogs": "隱藏來自 @{name} 的轉嘟",
   "account.in_memoriam": "謹此悼念。",
@@ -53,6 +53,7 @@
   "account.mute_notifications_short": "靜音推播通知",
   "account.mute_short": "靜音",
   "account.muted": "已靜音",
+  "account.mutual": "互相跟隨",
   "account.no_bio": "無個人檔案描述",
   "account.open_original_page": "檢視原始頁面",
   "account.posts": "嘟文",
diff --git a/app/lib/potential_friendship_tracker.rb b/app/lib/potential_friendship_tracker.rb
deleted file mode 100644
index f5bc2034659..00000000000
--- a/app/lib/potential_friendship_tracker.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-# frozen_string_literal: true
-
-class PotentialFriendshipTracker
-  EXPIRE_AFTER = 90.days.seconds
-  MAX_ITEMS    = 80
-
-  WEIGHTS = {
-    reply: 1,
-    favourite: 10,
-    reblog: 20,
-  }.freeze
-
-  class << self
-    include Redisable
-
-    def record(account_id, target_account_id, action)
-      return if account_id == target_account_id
-
-      key    = "interactions:#{account_id}"
-      weight = WEIGHTS[action]
-
-      redis.zincrby(key, weight, target_account_id)
-      redis.zremrangebyrank(key, 0, -MAX_ITEMS)
-      redis.expire(key, EXPIRE_AFTER)
-    end
-
-    def remove(account_id, target_account_id)
-      redis.zrem("interactions:#{account_id}", target_account_id)
-    end
-  end
-end
diff --git a/app/models/account_domain_block.rb b/app/models/account_domain_block.rb
index db2e37184f1..753935d6af6 100644
--- a/app/models/account_domain_block.rb
+++ b/app/models/account_domain_block.rb
@@ -19,6 +19,7 @@ class AccountDomainBlock < ApplicationRecord
   validates :domain, presence: true, uniqueness: { scope: :account_id }, domain: true
 
   after_commit :invalidate_domain_blocking_cache
+  after_commit :invalidate_follow_recommendations_cache
 
   private
 
@@ -26,4 +27,8 @@ class AccountDomainBlock < ApplicationRecord
     Rails.cache.delete("exclude_domains_for:#{account_id}")
     Rails.cache.delete(['exclude_domains', account_id, domain])
   end
+
+  def invalidate_follow_recommendations_cache
+    Rails.cache.delete("follow_recommendations/#{account_id}")
+  end
 end
diff --git a/app/models/account_suggestions.rb b/app/models/account_suggestions.rb
index d1774e62fae..d62176c7ca3 100644
--- a/app/models/account_suggestions.rb
+++ b/app/models/account_suggestions.rb
@@ -1,28 +1,48 @@
 # frozen_string_literal: true
 
 class AccountSuggestions
+  include DatabaseHelper
+
   SOURCES = [
     AccountSuggestions::SettingSource,
-    AccountSuggestions::PastInteractionsSource,
+    AccountSuggestions::FriendsOfFriendsSource,
+    AccountSuggestions::SimilarProfilesSource,
     AccountSuggestions::GlobalSource,
   ].freeze
 
-  def self.get(account, limit)
-    SOURCES.each_with_object([]) do |source_class, suggestions|
-      source_suggestions = source_class.new.get(
-        account,
-        skip_account_ids: suggestions.map(&:account_id),
-        limit: limit - suggestions.size
-      )
+  BATCH_SIZE = 40
 
-      suggestions.concat(source_suggestions)
+  def initialize(account)
+    @account = account
+  end
+
+  def get(limit, offset = 0)
+    with_read_replica do
+      account_ids_with_sources = Rails.cache.fetch("follow_recommendations/#{@account.id}", expires_in: 15.minutes) do
+        SOURCES.flat_map { |klass| klass.new.get(@account, limit: BATCH_SIZE) }.each_with_object({}) do |(account_id, source), h|
+          (h[account_id] ||= []).concat(Array(source).map(&:to_sym))
+        end.to_a.shuffle
+      end
+
+      # The sources deliver accounts that haven't yet been followed, are not blocked,
+      # and so on. Since we reset the cache on follows, blocks, and so on, we don't need
+      # a complicated query on this end.
+
+      account_ids  = account_ids_with_sources[offset, limit]
+      accounts_map = Account.where(id: account_ids.map(&:first)).includes(:account_stat).index_by(&:id)
+
+      account_ids.filter_map do |(account_id, source)|
+        next unless accounts_map.key?(account_id)
+
+        AccountSuggestions::Suggestion.new(
+          account: accounts_map[account_id],
+          source: source
+        )
+      end
     end
   end
 
-  def self.remove(account, target_account_id)
-    SOURCES.each do |source_class|
-      source = source_class.new
-      source.remove(account, target_account_id)
-    end
+  def remove(target_account_id)
+    FollowRecommendationMute.create(account_id: @account.id, target_account_id: target_account_id)
   end
 end
diff --git a/app/models/account_suggestions/friends_of_friends_source.rb b/app/models/account_suggestions/friends_of_friends_source.rb
new file mode 100644
index 00000000000..28d0ab99b3b
--- /dev/null
+++ b/app/models/account_suggestions/friends_of_friends_source.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+class AccountSuggestions::FriendsOfFriendsSource < AccountSuggestions::Source
+  def get(account, limit: 10)
+    Account.find_by_sql([<<~SQL.squish, { id: account.id, limit: limit }]).map { |row| [row.id, key] }
+      WITH first_degree AS (
+          SELECT target_account_id
+          FROM follows
+          JOIN accounts AS target_accounts ON follows.target_account_id = target_accounts.id
+          WHERE account_id = :id
+            AND NOT target_accounts.hide_collections
+      )
+      SELECT accounts.id, COUNT(*) AS frequency
+      FROM accounts
+      JOIN follows ON follows.target_account_id = accounts.id
+      JOIN account_stats ON account_stats.account_id = accounts.id
+      LEFT OUTER JOIN follow_recommendation_mutes ON follow_recommendation_mutes.target_account_id = accounts.id AND follow_recommendation_mutes.account_id = :id
+      WHERE follows.account_id IN (SELECT * FROM first_degree)
+        AND NOT EXISTS (SELECT 1 FROM follows f WHERE f.target_account_id = follows.target_account_id AND f.account_id = :id)
+        AND follows.target_account_id <> :id
+        AND accounts.discoverable
+        AND accounts.suspended_at IS NULL
+        AND accounts.silenced_at IS NULL
+        AND accounts.moved_to_account_id IS NULL
+        AND follow_recommendation_mutes.target_account_id IS NULL
+      GROUP BY accounts.id, account_stats.id
+      ORDER BY frequency DESC, account_stats.followers_count ASC
+      LIMIT :limit
+    SQL
+  end
+
+  private
+
+  def key
+    :friends_of_friends
+  end
+end
diff --git a/app/models/account_suggestions/global_source.rb b/app/models/account_suggestions/global_source.rb
index 651041d6751..d68f285e4f4 100644
--- a/app/models/account_suggestions/global_source.rb
+++ b/app/models/account_suggestions/global_source.rb
@@ -1,39 +1,13 @@
 # frozen_string_literal: true
 
 class AccountSuggestions::GlobalSource < AccountSuggestions::Source
-  include Redisable
-
-  def key
-    :global
-  end
-
-  def get(account, skip_account_ids: [], limit: 40)
-    account_ids = account_ids_for_locale(I18n.locale.to_s.split(/[_-]/).first) - [account.id] - skip_account_ids
-
-    as_ordered_suggestions(
-      scope(account).where(id: account_ids),
-      account_ids
-    ).take(limit)
-  end
-
-  def remove(_account, _target_account_id)
-    nil
+  def get(account, limit: 10)
+    FollowRecommendation.localized(content_locale).joins(:account).merge(base_account_scope(account)).order(rank: :desc).limit(limit).pluck(:account_id, :reason)
   end
 
   private
 
-  def scope(account)
-    Account.searchable
-           .followable_by(account)
-           .not_excluded_by_account(account)
-           .not_domain_blocked_by_account(account)
-  end
-
-  def account_ids_for_locale(locale)
-    redis.zrevrange("follow_recommendations:#{locale}", 0, -1).map(&:to_i)
-  end
-
-  def to_ordered_list_key(account)
-    account.id
+  def content_locale
+    I18n.locale.to_s.split(/[_-]/).first
   end
 end
diff --git a/app/models/account_suggestions/past_interactions_source.rb b/app/models/account_suggestions/past_interactions_source.rb
deleted file mode 100644
index d169394f11a..00000000000
--- a/app/models/account_suggestions/past_interactions_source.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: true
-
-class AccountSuggestions::PastInteractionsSource < AccountSuggestions::Source
-  include Redisable
-
-  def key
-    :past_interactions
-  end
-
-  def get(account, skip_account_ids: [], limit: 40)
-    account_ids = account_ids_for_account(account.id, limit + skip_account_ids.size) - skip_account_ids
-
-    as_ordered_suggestions(
-      scope.where(id: account_ids),
-      account_ids
-    ).take(limit)
-  end
-
-  def remove(account, target_account_id)
-    redis.zrem("interactions:#{account.id}", target_account_id)
-  end
-
-  private
-
-  def scope
-    Account.searchable
-  end
-
-  def account_ids_for_account(account_id, limit)
-    redis.zrevrange("interactions:#{account_id}", 0, limit).map(&:to_i)
-  end
-
-  def to_ordered_list_key(account)
-    account.id
-  end
-end
diff --git a/app/models/account_suggestions/setting_source.rb b/app/models/account_suggestions/setting_source.rb
index 6185732b4bc..4b7275bf7ad 100644
--- a/app/models/account_suggestions/setting_source.rb
+++ b/app/models/account_suggestions/setting_source.rb
@@ -1,32 +1,18 @@
 # frozen_string_literal: true
 
 class AccountSuggestions::SettingSource < AccountSuggestions::Source
-  def key
-    :staff
-  end
-
-  def get(account, skip_account_ids: [], limit: 40)
-    return [] unless setting_enabled?
-
-    as_ordered_suggestions(
-      scope(account).where(setting_to_where_condition).where.not(id: skip_account_ids),
-      usernames_and_domains
-    ).take(limit)
-  end
-
-  def remove(_account, _target_account_id)
-    nil
+  def get(account, limit: 10)
+    if setting_enabled?
+      base_account_scope(account).where(setting_to_where_condition).limit(limit).pluck(:id).zip([key].cycle)
+    else
+      []
+    end
   end
 
   private
 
-  def scope(account)
-    Account.searchable
-           .followable_by(account)
-           .not_excluded_by_account(account)
-           .not_domain_blocked_by_account(account)
-           .where(locked: false)
-           .where.not(id: account.id)
+  def key
+    :featured
   end
 
   def usernames_and_domains
@@ -61,8 +47,4 @@ class AccountSuggestions::SettingSource < AccountSuggestions::Source
   def setting
     Setting.bootstrap_timeline_accounts
   end
-
-  def to_ordered_list_key(account)
-    [account.username.downcase, account.domain&.downcase]
-  end
 end
diff --git a/app/models/account_suggestions/similar_profiles_source.rb b/app/models/account_suggestions/similar_profiles_source.rb
new file mode 100644
index 00000000000..733c5f0bbcd
--- /dev/null
+++ b/app/models/account_suggestions/similar_profiles_source.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+class AccountSuggestions::SimilarProfilesSource < AccountSuggestions::Source
+  class QueryBuilder < AccountSearchService::QueryBuilder
+    def must_clauses
+      [
+        {
+          more_like_this: {
+            fields: %w(text text.stemmed),
+            like: @query.map { |id| { _index: 'accounts', _id: id } },
+          },
+        },
+
+        {
+          term: {
+            properties: 'discoverable',
+          },
+        },
+      ]
+    end
+
+    def must_not_clauses
+      [
+        {
+          terms: {
+            id: following_ids,
+          },
+        },
+
+        {
+          term: {
+            properties: 'bot',
+          },
+        },
+      ]
+    end
+
+    def should_clauses
+      {
+        term: {
+          properties: {
+            value: 'verified',
+            boost: 2,
+          },
+        },
+      }
+    end
+  end
+
+  def get(account, limit: 10)
+    recently_followed_account_ids = account.active_relationships.recent.limit(5).pluck(:target_account_id)
+
+    if Chewy.enabled? && !recently_followed_account_ids.empty?
+      QueryBuilder.new(recently_followed_account_ids, account).build.limit(limit).hits.pluck('_id').map(&:to_i).zip([key].cycle)
+    else
+      []
+    end
+  rescue Faraday::ConnectionFailed
+    []
+  end
+
+  private
+
+  def key
+    :similar_to_recently_followed
+  end
+end
diff --git a/app/models/account_suggestions/source.rb b/app/models/account_suggestions/source.rb
index 504d26a8bd6..ee93a1342fc 100644
--- a/app/models/account_suggestions/source.rb
+++ b/app/models/account_suggestions/source.rb
@@ -1,34 +1,18 @@
 # frozen_string_literal: true
 
 class AccountSuggestions::Source
-  def key
-    raise NotImplementedError
-  end
-
   def get(_account, **kwargs)
     raise NotImplementedError
   end
 
-  def remove(_account, target_account_id)
-    raise NotImplementedError
-  end
-
   protected
 
-  def as_ordered_suggestions(scope, ordered_list)
-    return [] if ordered_list.empty?
-
-    map = scope.index_by { |account| to_ordered_list_key(account) }
-
-    ordered_list.filter_map { |ordered_list_key| map[ordered_list_key] }.map do |account|
-      AccountSuggestions::Suggestion.new(
-        account: account,
-        source: key
-      )
-    end
-  end
-
-  def to_ordered_list_key(_account)
-    raise NotImplementedError
+  def base_account_scope(account)
+    Account.searchable
+           .followable_by(account)
+           .not_excluded_by_account(account)
+           .not_domain_blocked_by_account(account)
+           .where.not(id: account.id)
+           .joins("LEFT OUTER JOIN follow_recommendation_mutes ON follow_recommendation_mutes.target_account_id = accounts.id AND follow_recommendation_mutes.account_id = #{account.id}").where(follow_recommendation_mutes: { target_account_id: nil })
   end
 end
diff --git a/app/models/block.rb b/app/models/block.rb
index 11156ebab31..5476542a5ab 100644
--- a/app/models/block.rb
+++ b/app/models/block.rb
@@ -26,15 +26,20 @@ class Block < ApplicationRecord
   end
 
   before_validation :set_uri, only: :create
-  after_commit :remove_blocking_cache
+  after_commit :invalidate_blocking_cache
+  after_commit :invalidate_follow_recommendations_cache
 
   private
 
-  def remove_blocking_cache
+  def invalidate_blocking_cache
     Rails.cache.delete("exclude_account_ids_for:#{account_id}")
     Rails.cache.delete("exclude_account_ids_for:#{target_account_id}")
   end
 
+  def invalidate_follow_recommendations_cache
+    Rails.cache.delete("follow_recommendations/#{account_id}")
+  end
+
   def set_uri
     self.uri = ActivityPub::TagManager.instance.generate_uri_for(self) if uri.nil?
   end
diff --git a/app/models/concerns/account/associations.rb b/app/models/concerns/account/associations.rb
index 31902ae21a8..2bb6fed5ad0 100644
--- a/app/models/concerns/account/associations.rb
+++ b/app/models/concerns/account/associations.rb
@@ -64,6 +64,7 @@ module Account::Associations
     has_one :deletion_request, class_name: 'AccountDeletionRequest', inverse_of: :account, dependent: :destroy
 
     # Follow recommendations
+    has_one :follow_recommendation, inverse_of: :account, dependent: nil
     has_one :follow_recommendation_suppression, inverse_of: :account, dependent: :destroy
 
     # Account statuses cleanup policy
diff --git a/app/models/concerns/account/interactions.rb b/app/models/concerns/account/interactions.rb
index 4ddec9bf49b..351530c2f06 100644
--- a/app/models/concerns/account/interactions.rb
+++ b/app/models/concerns/account/interactions.rb
@@ -116,8 +116,6 @@ module Account::Interactions
 
     rel.save! if rel.changed?
 
-    remove_potential_friendship(other_account)
-
     rel
   end
 
@@ -131,13 +129,10 @@ module Account::Interactions
 
     rel.save! if rel.changed?
 
-    remove_potential_friendship(other_account)
-
     rel
   end
 
   def block!(other_account, uri: nil)
-    remove_potential_friendship(other_account)
     block_relationships.create_with(uri: uri)
                        .find_or_create_by!(target_account: other_account)
   end
@@ -148,8 +143,6 @@ module Account::Interactions
     mute.expires_in = duration.zero? ? nil : duration
     mute.save!
 
-    remove_potential_friendship(other_account)
-
     # When toggling a mute between hiding and allowing notifications, the mute will already exist, so the find_or_create_by! call will return the existing Mute without updating the hide_notifications attribute. Therefore, we check that hide_notifications? is what we want and set it if it isn't.
     mute.update!(hide_notifications: notifications) if mute.hide_notifications? != notifications
 
@@ -307,10 +300,4 @@ module Account::Interactions
       domain_blocking_by_domain: Account.domain_blocking_map_by_domain(domains, id),
     })
   end
-
-  private
-
-  def remove_potential_friendship(other_account)
-    PotentialFriendshipTracker.remove(id, other_account.id)
-  end
 end
diff --git a/app/models/concerns/account/search.rb b/app/models/concerns/account/search.rb
index 40d87aaaa19..077e5d57b18 100644
--- a/app/models/concerns/account/search.rb
+++ b/app/models/concerns/account/search.rb
@@ -116,6 +116,7 @@ module Account::Search
     [].tap do |properties|
       properties << 'bot' if bot?
       properties << 'verified' if fields.any?(&:verified?)
+      properties << 'discoverable' if discoverable?
     end
   end
 
diff --git a/app/models/follow.rb b/app/models/follow.rb
index 108f5c5d515..4d1598dcad6 100644
--- a/app/models/follow.rb
+++ b/app/models/follow.rb
@@ -44,10 +44,10 @@ class Follow < ApplicationRecord
 
   before_validation :set_uri, only: :create
   after_create :increment_cache_counters
-  after_create :invalidate_hash_cache
   after_destroy :remove_endorsements
   after_destroy :decrement_cache_counters
-  after_destroy :invalidate_hash_cache
+  after_commit :invalidate_follow_recommendations_cache
+  after_commit :invalidate_hash_cache
 
   private
 
@@ -74,4 +74,8 @@ class Follow < ApplicationRecord
 
     Rails.cache.delete("followers_hash:#{target_account_id}:#{account.synchronization_uri_prefix}")
   end
+
+  def invalidate_follow_recommendations_cache
+    Rails.cache.delete("follow_recommendations/#{account_id}")
+  end
 end
diff --git a/app/models/follow_recommendation_filter.rb b/app/models/follow_recommendation_filter.rb
index 2fab9756988..62a02eba5ae 100644
--- a/app/models/follow_recommendation_filter.rb
+++ b/app/models/follow_recommendation_filter.rb
@@ -17,12 +17,9 @@ class FollowRecommendationFilter
 
   def results
     if params['status'] == 'suppressed'
-      Account.joins(:follow_recommendation_suppression).order(FollowRecommendationSuppression.arel_table[:id].desc).to_a
+      Account.includes(:account_stat).joins(:follow_recommendation_suppression).order(FollowRecommendationSuppression.arel_table[:id].desc)
     else
-      account_ids = redis.zrevrange("follow_recommendations:#{@language}", 0, -1).map(&:to_i)
-      accounts    = Account.where(id: account_ids).index_by(&:id)
-
-      account_ids.filter_map { |id| accounts[id] }
+      Account.includes(:account_stat).joins(:follow_recommendation).merge(FollowRecommendation.localized(@language).order(rank: :desc))
     end
   end
 end
diff --git a/app/models/follow_recommendation_mute.rb b/app/models/follow_recommendation_mute.rb
new file mode 100644
index 00000000000..d166d0a6206
--- /dev/null
+++ b/app/models/follow_recommendation_mute.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+# == Schema Information
+#
+# Table name: follow_recommendation_mutes
+#
+#  id                :bigint(8)        not null, primary key
+#  account_id        :bigint(8)        not null
+#  target_account_id :bigint(8)        not null
+#  created_at        :datetime         not null
+#  updated_at        :datetime         not null
+#
+class FollowRecommendationMute < ApplicationRecord
+  belongs_to :account
+  belongs_to :target_account, class_name: 'Account'
+
+  validates :target_account, uniqueness: { scope: :account_id }
+
+  after_commit :invalidate_follow_recommendations_cache
+
+  private
+
+  def invalidate_follow_recommendations_cache
+    Rails.cache.delete("follow_recommendations/#{account_id}")
+  end
+end
diff --git a/app/models/follow_recommendation_suppression.rb b/app/models/follow_recommendation_suppression.rb
index e261a2fe359..59e94dc6b3a 100644
--- a/app/models/follow_recommendation_suppression.rb
+++ b/app/models/follow_recommendation_suppression.rb
@@ -11,19 +11,5 @@
 #
 
 class FollowRecommendationSuppression < ApplicationRecord
-  include Redisable
-
   belongs_to :account
-
-  after_commit :remove_follow_recommendations, on: :create
-
-  private
-
-  def remove_follow_recommendations
-    redis.pipelined do |pipeline|
-      I18n.available_locales.each do |locale|
-        pipeline.zrem("follow_recommendations:#{locale}", account_id)
-      end
-    end
-  end
 end
diff --git a/app/models/follow_request.rb b/app/models/follow_request.rb
index a5c23e09d47..3c5e8f96f0d 100644
--- a/app/models/follow_request.rb
+++ b/app/models/follow_request.rb
@@ -45,10 +45,15 @@ class FollowRequest < ApplicationRecord
   end
 
   before_validation :set_uri, only: :create
+  after_commit :invalidate_follow_recommendations_cache
 
   private
 
   def set_uri
     self.uri = ActivityPub::TagManager.instance.generate_uri_for(self) if uri.nil?
   end
+
+  def invalidate_follow_recommendations_cache
+    Rails.cache.delete("follow_recommendations/#{account_id}")
+  end
 end
diff --git a/app/models/mute.rb b/app/models/mute.rb
index 8fc5422624a..1d18b30eea9 100644
--- a/app/models/mute.rb
+++ b/app/models/mute.rb
@@ -23,11 +23,16 @@ class Mute < ApplicationRecord
 
   validates :account_id, uniqueness: { scope: :target_account_id }
 
-  after_commit :remove_blocking_cache
+  after_commit :invalidate_blocking_cache
+  after_commit :invalidate_follow_recommendations_cache
 
   private
 
-  def remove_blocking_cache
+  def invalidate_blocking_cache
     Rails.cache.delete("exclude_account_ids_for:#{account_id}")
   end
+
+  def invalidate_follow_recommendations_cache
+    Rails.cache.delete("follow_recommendations/#{account_id}")
+  end
 end
diff --git a/app/models/preview_cards_status.rb b/app/models/preview_cards_status.rb
index 214eec22e5c..5ff63520554 100644
--- a/app/models/preview_cards_status.rb
+++ b/app/models/preview_cards_status.rb
@@ -4,8 +4,8 @@
 #
 # Table name: preview_cards_statuses
 #
-#  preview_card_id :bigint(8)        not null
-#  status_id       :bigint(8)        not null
+#  preview_card_id :bigint(8)        not null, primary key
+#  status_id       :bigint(8)        not null, primary key
 #  url             :string
 #
 class PreviewCardsStatus < ApplicationRecord
diff --git a/app/services/account_search_service.rb b/app/services/account_search_service.rb
index 7b85b09d8f0..571a0fa57d6 100644
--- a/app/services/account_search_service.rb
+++ b/app/services/account_search_service.rb
@@ -23,6 +23,7 @@ class AccountSearchService < BaseService
               query: {
                 bool: {
                   must: must_clauses,
+                  must_not: must_not_clauses,
                 },
               },
 
@@ -49,6 +50,10 @@ class AccountSearchService < BaseService
       end
     end
 
+    def must_not_clauses
+      []
+    end
+
     def should_clauses
       if @account && !@options[:following]
         [boost_following_query]
diff --git a/app/services/favourite_service.rb b/app/services/favourite_service.rb
index 6fdc92a1731..ded50187f77 100644
--- a/app/services/favourite_service.rb
+++ b/app/services/favourite_service.rb
@@ -20,7 +20,7 @@ class FavouriteService < BaseService
     Trends.statuses.register(status)
 
     create_notification(favourite)
-    bump_potential_friendship(account, status)
+    increment_statistics
 
     favourite
   end
@@ -37,11 +37,8 @@ class FavouriteService < BaseService
     end
   end
 
-  def bump_potential_friendship(account, status)
+  def increment_statistics
     ActivityTracker.increment('activity:interactions')
-    return if account.following?(status.account_id)
-
-    PotentialFriendshipTracker.record(account.id, status.account_id, :favourite)
   end
 
   def build_json(favourite)
diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb
index 8f5f91ec787..964ca91a67e 100644
--- a/app/services/post_status_service.rb
+++ b/app/services/post_status_service.rb
@@ -194,9 +194,6 @@ class PostStatusService < BaseService
     return if !@status.reply? || @account.id == @status.in_reply_to_account_id
 
     ActivityTracker.increment('activity:interactions')
-    return if @account.following?(@status.in_reply_to_account_id)
-
-    PotentialFriendshipTracker.record(@account.id, @status.in_reply_to_account_id, :reply)
   end
 
   def status_attributes
diff --git a/app/services/reblog_service.rb b/app/services/reblog_service.rb
index b73669f9d84..18e1748cea0 100644
--- a/app/services/reblog_service.rb
+++ b/app/services/reblog_service.rb
@@ -33,7 +33,7 @@ class ReblogService < BaseService
     ActivityPub::DistributionWorker.perform_async(reblog.id) unless reblogged_status.local_only?
 
     create_notification(reblog)
-    bump_potential_friendship(account, reblog)
+    increment_statistics
 
     reblog
   end
@@ -50,12 +50,8 @@ class ReblogService < BaseService
     end
   end
 
-  def bump_potential_friendship(account, reblog)
+  def increment_statistics
     ActivityTracker.increment('activity:interactions')
-
-    return if account.following?(reblog.reblog.account_id)
-
-    PotentialFriendshipTracker.record(account.id, reblog.reblog.account_id, :reblog)
   end
 
   def build_json(reblog)
diff --git a/app/views/admin/follow_recommendations/show.html.haml b/app/views/admin/follow_recommendations/show.html.haml
index dc65a72135b..9c2063d3c57 100644
--- a/app/views/admin/follow_recommendations/show.html.haml
+++ b/app/views/admin/follow_recommendations/show.html.haml
@@ -38,3 +38,5 @@
         = nothing_here 'nothing-here--under-tabs'
       - else
         = render partial: 'account', collection: @accounts, locals: { f: f }
+
+= paginate @accounts
diff --git a/app/workers/scheduler/follow_recommendations_scheduler.rb b/app/workers/scheduler/follow_recommendations_scheduler.rb
index ea59ad986b9..ea5aa8b5396 100644
--- a/app/workers/scheduler/follow_recommendations_scheduler.rb
+++ b/app/workers/scheduler/follow_recommendations_scheduler.rb
@@ -2,61 +2,11 @@
 
 class Scheduler::FollowRecommendationsScheduler
   include Sidekiq::Worker
-  include Redisable
 
   sidekiq_options retry: 0, lock: :until_executed, lock_ttl: 1.day.to_i
 
-  # The maximum number of accounts that can be requested in one page from the
-  # API is 80, and the suggestions API does not allow pagination. This number
-  # leaves some room for accounts being filtered during live access
-  SET_SIZE = 100
-
   def perform
-    # Maintaining a materialized view speeds-up subsequent queries significantly
     AccountSummary.refresh
     FollowRecommendation.refresh
-
-    fallback_recommendations = FollowRecommendation.order(rank: :desc).limit(SET_SIZE)
-
-    Trends.available_locales.each do |locale|
-      recommendations = if AccountSummary.safe.filtered.localized(locale).exists? # We can skip the work if no accounts with that language exist
-                          FollowRecommendation.localized(locale).order(rank: :desc).limit(SET_SIZE).map { |recommendation| [recommendation.rank, recommendation.account_id] }
-                        else
-                          []
-                        end
-
-      # Use language-agnostic results if there are not enough language-specific ones
-      missing = SET_SIZE - recommendations.size
-
-      if missing.positive? && fallback_recommendations.size.positive?
-        max_fallback_rank = fallback_recommendations.first.rank || 0
-
-        # Language-specific results should be above language-agnostic ones,
-        # otherwise language-agnostic ones will always overshadow them
-        recommendations.map! { |(rank, account_id)| [rank + max_fallback_rank, account_id] }
-
-        added = 0
-
-        fallback_recommendations.each do |recommendation|
-          next if recommendations.any? { |(_, account_id)| account_id == recommendation.account_id }
-
-          recommendations << [recommendation.rank, recommendation.account_id]
-          added += 1
-
-          break if added >= missing
-        end
-      end
-
-      redis.multi do |multi|
-        multi.del(key(locale))
-        multi.zadd(key(locale), recommendations)
-      end
-    end
-  end
-
-  private
-
-  def key(locale)
-    "follow_recommendations:#{locale}"
   end
 end
diff --git a/config/locales/activerecord.lad.yml b/config/locales/activerecord.lad.yml
index 625d897d0b5..71a63d1cd19 100644
--- a/config/locales/activerecord.lad.yml
+++ b/config/locales/activerecord.lad.yml
@@ -33,6 +33,10 @@ lad:
           attributes:
             data:
               malformed: tiene formato yerrado
+        status:
+          attributes:
+            reblog:
+              taken: de publikasyon ya existe
         user:
           attributes:
             email:
@@ -49,3 +53,7 @@ lad:
             position:
               elevated: no puede ser mas alto ke tu rolo aktual
               own_role: no se puede trokar kon tu rolo aktual
+        webhook:
+          attributes:
+            events:
+              invalid_permissions: no puedes inkluir evenimientos a los kualos no estas autorizado
diff --git a/config/locales/devise.ie.yml b/config/locales/devise.ie.yml
index 16d51dba168..0cf0fbe1fe2 100644
--- a/config/locales/devise.ie.yml
+++ b/config/locales/devise.ie.yml
@@ -2,8 +2,16 @@
 ie:
   devise:
     failure:
+      already_authenticated: Tu ha ja intrat.
+      inactive: Tui conto ancor ne ha esset activat.
       invalid: Ínvalid %{authentication_keys} o passa-parol.
+      last_attempt: Hay solmen un prova ante que tui conto deveni serrat.
+      locked: Tui conto es serrat.
       not_found_in_database: Ínvalid %{authentication_keys} o passa-parol.
+      pending: Tui conto es ancor sub revision.
+      timeout: Tui session ha expirat. Ples reintrar denov por continuar.
+      unauthenticated: Tu deve intrar o registrar te ante continuar.
+      unconfirmed: Tu deve confirmar tui e-posta ante continuar.
     mailer:
       email_changed:
         extra: Si tu ne changeat tui email-adresse, it es probabil que alqui ha ganiat accesse a tui conto. Ples changear tui passa-parol strax o contacter li administrator del servitor si tu ne posse intrar tui conto.
@@ -20,9 +28,13 @@ ie:
         title: Reiniciar passa-parol
       two_factor_disabled:
         explanation: 2-factor autentication por tui conto ha esset desactivisat. Aperter session nu es possibil solmen per email-adresse e passa-parol.
+    omniauth_callbacks:
+      failure: Ne posset autenticar te de %{kind} pro "%{reason}".
     passwords:
       no_token: Tu ne posse accessar ti-ci págine sin venir de un email pri reiniciar li passa-parol. Si tu ha venit de un email pri reiniciar li passa-parol, ples far cert que tu usat li complet URL providet.
       send_instructions: Si tui email-adresse existe in nor database, tu va reciver un ligament por recuperar li passa-parol a tui email-adresse in quelc minutes. Ples vider tui spam-emails si tu ne recivet ti email.
       send_paranoid_instructions: Si tui email-adresse existe in nor database, tu va reciver un ligament por recuperar li passa-parol a tui email-adresse in quelc minutes. Ples vider tui spam-emails si tu ne recivet ti email.
       updated: Tui passa-parol ha esset changeat successosimen. Tu nu ha apertet session.
       updated_not_active: Tui passa-parol ha esset changeat successosimen.
+    registrations:
+      signed_up: Benevenit! Tu ha successat registrar te.
diff --git a/config/locales/ie.yml b/config/locales/ie.yml
index 3a85602ba51..6013923e3fe 100644
--- a/config/locales/ie.yml
+++ b/config/locales/ie.yml
@@ -119,6 +119,7 @@ ie:
       remote_suspension_reversible_hint_html: Li conto ha esset suspendet che su servitor, e li data va esser completmen removet ye %{date}. Til tande, li lontan servitor posse restaurar ti conto sin quelcunc mal efectes. Si tu vole remover omni data del conto ínmediatmen, tu posse far it in infra.
       remove_avatar: Remover li avatar
       removed_avatar_msg: Successosimen removet li avatar-image de %{username}
+      removed_header_msg: Successosimen removet li cap-image de %{username}
       resend_confirmation:
         already_confirmed: Ti usator ja esset confirmat
         send: Inviar li confirmation denov
@@ -280,6 +281,7 @@ ie:
         update_announcement_html: "%{name} actualisat li proclamation %{target}"
         update_custom_emoji_html: "%{name} actualisat emoji %{target}"
         update_domain_block_html: "%{name} actualisat li dominia-blocca de %{target}"
+        update_ip_block_html: "%{name} changeat regul por IP %{target}"
         update_status_html: "%{name} actualisat posta de %{target}"
         update_user_role_html: "%{name} changeat li rol %{target}"
       deleted_account: deletet conto
@@ -327,6 +329,7 @@ ie:
         title: Adjunter nov customisat emoji
       no_emoji_selected: Null emoji esset changeat pro que null esset selectet
       not_permitted: Tu ne es permisset far ti action
+      overwrite: Remplazzar
       shortcode_hint: Adminim 2 carácteres, solmen lítteres, ciffres e sublineas
       title: Customisat emoji
       uncategorized: Íncategorisat
@@ -431,6 +434,7 @@ ie:
         title: Bloccar nov email-dominia
       no_email_domain_block_selected: Null email-dominia-bloccas esset changeat pro que null esset selectet
       not_permitted: Ne permisset
+      resolved_through_html: Resoluet per %{domain}
       title: Bloccat email-dominias
     export_domain_allows:
       new:
@@ -448,6 +452,7 @@ ie:
         title: Importar dominia-bloccas
       no_file: Null file selectet
     follow_recommendations:
+      description_html: "<strong>Seque-recomandationes auxilia nov usatores trovar interessant contenete</strong>. Quande un usator ne ha interactet con altres suficentmen por generar personalisat seque-recomandationes, ti contos es recomandat vicemen. Ili es recalculat dialmen de un mixtura de contos con li max mult recent interactiones e li max alt númeres de local sequitores por un specificat lingue."
       language: Por li lingue
       status: Statu
       suppress: Supresser seque-recomandation
@@ -456,6 +461,7 @@ ie:
       unsuppress: Restaurar seque-recomandation
     instances:
       availability:
+        no_failures_recorded: Null fallimentes registrat.
         title: Disponibilitá
         warning: Li ultim prova conexer a ti servitor ha esset ínsuccessosi
       back_to_all: Omni
@@ -465,11 +471,30 @@ ie:
       confirm_purge: Vole tu vermen permanentmen deleter data de ti dominia?
       content_policies:
         comment: Internal nota
+        policies:
+          reject_media: Rejecter medie
+          reject_reports: Rejecter raportes
+          silence: Limitar
+          suspend: Suspender
         policy: Politica
         reason: Visibil rason
         title: Politicas pri contenete
+      dashboard:
+        instance_accounts_dimension: Max sequet contos
+        instance_accounts_measure: inmagasinat contos
+        instance_followers_measure: nor sequitores ta
+        instance_follows_measure: lor sequitores ci
+        instance_languages_dimension: Max grand lingues
+        instance_statuses_measure: salvat postas
       delivery:
+        all: Omni
+        clear: Aclarar errores de liveration
+        failing: Fallint
+        restart: Recomensar liveration
+        stop: Haltar liveration
         unavailable: Índisponibil
+      delivery_available: Liveration es disponibil
+      delivery_error_days: Dies de errores de liveration
       delivery_error_hint: Si liveration ne es possibil durant %{count} dies, it va esser marcat automaticmen quam ínliverabil.
       destroyed_msg: Data de %{domain} es nu in li linea por iminent deletion.
       empty: Null dominias trovat.
@@ -547,6 +572,9 @@ ie:
         other_description_html: Vider plu optiones por controlar li conduida del conto e customisar comunication al raportat conto.
         resolve_description_html: Null action va esser fat contra li raportat conto, null admoniment registrat, e li raporte va esser cludet.
         silence_description_html: Li conto va esser visibil nur a tis qui ja seque it o qui sercha it manualmen, limitante severimen su atingement. Ti sempre posse esser revertet. Ti clude omni raportes contra ti conto.
+        suspend_description_html: Ti-ci conto e omni contenete va esser ínaccessibil e finalmen deletet, e interacter con it va esser ínpossibil. Reversibil til que 30 dies ha passat. Clude omni raportes contra ti conto.
+      actions_description_html: Decider quel action a far por soluer ti raporte. Si tu fa un punitiv action contra li raportat conto, li usator va reciver un notification per email, except in li casu que li <strong>Spam</strong> categorie es selectet.
+      actions_description_remote_html: Decider quel action a far por soluer ti raporte. It va afecter solmen qualmen <strong>vor</strong> servitor comunica con ti lontan conto e tractar su contenete.
       add_to_report: Adjunter plu al raporte
       are_you_sure: Es tu cert?
       assign_to_self: Assignar it a me
@@ -562,6 +590,9 @@ ie:
       confirm_action: Confirmar moderatori action contra @%{acct}
       created_at: Raportat
       delete_and_resolve: Deleter postas
+      forwarded: Reinviat
+      forwarded_replies_explanation: Ti-ci raporte es de un lontan usator e pri lontan contenete. It ha esset reinviat a vos pro que li raportat contenete es un response a un de tui usatores.
+      forwarded_to: Misset anc a %{domain}
       mark_as_resolved: Marcar quam soluet
       mark_as_sensitive: Marcar quam sensitiv
       mark_as_unresolved: Marcar quam ínsoluet
@@ -586,72 +617,383 @@ ie:
       skip_to_actions: Ear rect al actiones
       status: Statu
       statuses: Contenete raportat
+      statuses_description_html: Ofensiv contenete va esser citat in comunication con li raportat conto
+      summary:
+        action_preambles:
+          delete_html: 'Tu va <strong>remover</strong> alcun postas de <strong>@%{acct}</strong>''. To va:'
+          mark_as_sensitive_html: 'Tu va <strong>marcar</strong> alcun postas de <strong>@%{acct}</strong> quam <strong>sensitiv</strong>. To va:'
+          silence_html: 'Tu va <strong>limitar</strong> li conto de <strong>@%{acct}</strong>. To va:'
+          suspend_html: 'Tu va <strong>suspender</strong> li conto de <strong>@%{acct}</strong>. To va:'
+        actions:
+          delete_html: Remover li ofendent postas
+          mark_as_sensitive_html: Marcar li medie del ofendent postas quam sensitiv
+          silence_html: Severimen limitar li atingement de <strong>@%{acct}</strong> per far su profil e contenete visibil solmen a persones qui ja seque ilu o qui sercha su profil manualmen
+          suspend_html: Suspender <strong>@%{acct}</strong>, fante su profil e contenete ínaccessibil e ínpossibil con quel interacter
+        close_report: 'Marcar raporte #%{id} quam resoluet'
+        close_reports_html: Marcar <strong>omni</strong> raportes contra <strong>@%{acct}</strong> quam soluet
+        delete_data_html: Deleter li profil e contenete de <strong>@%{acct}</strong> 30 dies pos nu, except si ilu es dessuspendet ante tande
+        preview_preamble_html: "<strong>@%{acct}</strong> va reciver un admoniment con li sequent contenete:"
+        record_strike_html: Registrar un admoniment contra <strong>@%{acct}</strong> por auxiliar vos tractar futur violationes de ti-ci conto
+        send_email_html: Misser un admoniment-email a <strong>@%{acct}</strong>
+        warning_placeholder: Ínobligatori additional rason por li moderatori action.
+      target_origin: Orígine del conto raportat
       title: Raportes
+      unassign: Ínassignar
+      unknown_action_msg: 'Ínconosset action: %{action}'
+      unresolved: Ínresoluet
+      updated_at: Actualisat
+      view_profile: Vider profil
     roles:
+      add_new: Adjunter un rol
+      assigned_users:
+        one: "%{count} usator"
+        other: "%{count} usatores"
       categories:
+        administration: Administration
+        devops: DevOps
+        invites: Invitationes
         moderation: Moderation
+        special: Special
+      delete: Deleter
+      description_html: Con <strong>roles por usatores</strong>, tu posse customisar li functiones e locs de Mastodon in queles tui usatores posse accesser.
+      edit: Modificar rol '%{name}'
+      everyone: Permissiones predefinit
+      permissions_count:
+        one: "%{count} permission"
+        other: "%{count} permissiones"
       privileges:
         administrator: Administrator
+        delete_user_data: Deleter Data de Usator
+        delete_user_data_description: Possibilisa que usatores mey deleter li data de altri usatores strax
+        invite_users: Invitar Usatores
+        invite_users_description: Permisse que usatores invita novones al servitor
         manage_announcements: Tractar proclamationes
         manage_announcements_description: Permisse usatores tractar proclamationes sur li servitor
+        manage_appeals: Gerer Apelles
+        manage_blocks: Gerer Bloccas
+        manage_blocks_description: Permisse que usatores blocca provisores de e-posta e IP-adresses
+        manage_custom_emojis: Gerer Customisat Emojis
+        manage_custom_emojis_description: Permisse que usatores gere customisat emojis sur li servitor
+        manage_federation: Gerer Federation
+        manage_federation_description: Permisse que usatores sive blocca sive permisse federation con altri domenes, e controla liverabilitá
+        manage_invites: Gerer Invitationes
+        manage_reports: Gerer Raportes
+        manage_roles: Gerer Roles
+        manage_rules: Gerer Regules
+        manage_rules_description: Permisse que usatores changea regules del servitor
+        manage_settings: Gerer Parametres
+        manage_settings_description: Permisse que usatores changea parametres del situ
+        manage_taxonomies: Gerer Taxonomies
+        manage_user_access: Gerer Usator-Accesse
         manage_user_access_description: Permisse usatores desactivisar li 2-factor autentication de altri usatores, changear lor email-adresses, e reiniciar lor passa-paroles
         manage_users: Gerer usatores
+        manage_webhooks: Gerer Web-crocs
+        view_audit_log_description: Permisse que usatores vide li historie de administrativ actiones sur li servitor
+        view_devops: DevOps
       title: Roles
     rules:
       add_new: Adjunter un regule
+      delete: Deleter
+      edit: Redacter regul
+      empty: Ancor null regules de servitor ha esset definit.
+      title: Regules del servitor
     settings:
       about:
+        manage_rules: Gerer regules de servitor
         title: Pri
       appearance:
+        preamble: Customisar li interfacie web de Mastodon.
         title: Aspecte
+      discovery:
+        follow_recommendations: Seque-recomandationes
+        preamble: Exposir interessant contenete es importantissim por incorporar nov usatores qui fórsan conosse nequi che Mastodon. Decider qualmen diferent utensiles de decovrition functiona che vor servitor.
+        profile_directory: Profilarium
+        public_timelines: Public témpor-lineas
+        publish_discovered_servers: Publicar decovrit servitores
+        publish_statistics: Publicar statisticas
+        title: Decovriment
+        trends: Tendenties
       domain_blocks:
+        all: Ad omnes
+        disabled: A necun
         users: A local usatores qui ha initiat session
+      registrations:
+        preamble: Decider qui posse crear un conto che vor servitor.
+        title: Registrationes
+      registrations_mode:
+        modes:
+          approved: Aprobation besonat por adhesion
+          none: Nequi posse registrar se
+          open: Quicunc posse registrar se
+      security:
+        authorized_fetch: Postular autentication de federat servitores
+        authorized_fetch_hint: Postular autentication de federat servitores possibilisa plu strict infortiament de ambi usatori e servitori bloccas. Támen, ti fórsan va limitar li potentie de vor servitor, reducter li atingement de vor responses, e possibilmen introducter problemas de compatibilitá con quelc federat servicies. Additionalmen, ti ne va preventer dedicat actores de accesser vor public postas e contos.
+        authorized_fetch_overridden_hint: Tu actualmen ne posse changear ti parametre pro que it es controlat de un environmental variabile.
+        federation_authentication: Infortiation de federational autentication
+      title: Parametres del servitor
     site_uploads:
       delete: Deleter cargat file
       destroyed_msg: Cargat file successosimen deletet!
     software_updates:
-      type: Tip
+      critical_update: Critic — ples actualisar rapidmen
+      description: On recomanda que vu actualisa vor Mastodon-servitor regularimen por profiter del max recent fixes e facultates. In plu, quelcvez it es critic actualisar Mastodon promptmen por evitar problemas de securitá. Pro ti rasones, Mastodon questiona chascun 30 minutes ca hay actualisationes, e va notificar vos secun vor parametres pri email-notificationes.
+      documentation_link: Aprender plu
+      title: Actualisationes disponibil
+      type: Specie
       version: Version
     statuses:
       account: Autor
+      application: Aplication
+      back_to_account: Retornar al págine del conto
+      back_to_report: Retornar al págine del raporte
+      batch:
+        remove_from_report: Remover de raporte
+        report: Raportar
+      deleted: Deletet
+      favourites: Favorites
+      history: Historie de versiones
+      in_reply_to: Respondent a
       language: Lingue
+      media:
+        title: Medie
       metadata: Metadata
+      no_status_selected: Null postas esset changeat pro que null esset selectet
+      open: Aperter posta
+      original_status: Original posta
+      reblogs: Boosts
+      status_changed: Posta modificat
+      title: Postas del conto
+      trending: Populari
       visibility: Visibilitá
+      with_media: Con medie
+    strikes:
+      actions:
+        delete_statuses: "%{name} deletet li postas de %{target}"
+        disable: "%{name} gelat li conto de %{target}"
+        mark_statuses_as_sensitive: "%{name} marcat li postas de %{target} quam sensitiv"
+        none: "%{name} misset un admoniment a %{target}"
+        sensitive: "%{name} marcat li conto de %{target} quam sensitiv"
+        silence: "%{name} limitat li conto de %{target}"
+        suspend: "%{name} suspendet li conto de %{target}"
+      appeal_approved: Apellat
+      appeal_pending: Apelle pendent
+      appeal_rejected: Apelle rejectet
+    system_checks:
+      database_schema_check:
+        message_html: Hay pendent migrationes de database. Ples far les por far cert que li aplication functiona quam expectat
+      elasticsearch_preset:
+        action: Vider li documentation
+      elasticsearch_preset_single_node:
+        action: Vider li documentation
+      rules_check:
+        action: Gerer regules de servitor
+      software_version_critical_check:
+        message_html: Un critical actualisation por Mastodon es disposibil, ples actualisar tam rapidmen possibil.
+      software_version_patch_check:
+        action: Vider actualisationes disponibil
     title: Administration
     trends:
+      allow: Permisser
       approved: Aprobat
+      disallow: Despermisser
+      links:
+        allow: Permisser ligament
+        allow_provider: Permisser editor
+        description_html: Ci hay ligamentes actualmen partit per mult contos sur queles tui servitor vide postas. It posse auxiliar tui usatores saver to quo eveni in li munde. Null ligamentes es monstrat publicmen ante que tu aproba li publicator. Tu posse anc aprobar o rejecter índividual ligamentes.
+        disallow: Despermisser ligament
+        disallow_provider: Despermisser editor
+        no_link_selected: Null ligamentes esset changeat pro que null esset selectet
+        publishers:
+          no_publisher_selected: Null editores esset changeat pro que necun esset selectet
+        title: Ligamentes in tendentie
+        usage_comparison: Partit %{today} vezes hodie, in comparation a %{yesterday} yer
+      not_allowed_to_trend: Ne permisset esser in tendentie
+      pending_review: Sub inspection
+      preview_card_providers:
+        allowed: Ligamentes de ti-ci editor posse esser in tendentie
+        description_html: Tis es dominias de queles ligamentes es sovente distribuet che vor servitor. Ligamentes ne va intrar li tendenties publicmen except si li dominia del ligament es aprobat. Vor aprobation (o rejection) aplica anc a subdominias.
+        rejected: Ligamentes de ti publicator ne va intrar li tendenties
+        title: Editores
+      rejected: Rejectet
       statuses:
+        allow: Permisser posta
+        allow_account: Permisser autor
+        description_html: Tis es postas queles vor servitor conosse queles on actualmen distribue e favoritisa mult. It posse auxiliar vor nov e retornant usatores por trovar plu persones por sequer. Null postas es monstrat publicmen til que vu aproba li autor, e li autor permisse que su conto es suggestet a altres. Vu anc posse permisser o rejecter individual postas.
+        disallow: Despermisser posta
+        disallow_account: Despermisser autor
+        no_status_selected: Null populari postas esset changeat pro que null esset selectet
+        not_discoverable: Autor ne ha consentit esser decovribil
         shared_by:
           one: Partit o favoritisat un vez
           other: Partit e favoritisat %{friendly_count} vezes
+        title: Populari postas
       tags:
+        current_score: Actual puntes %{score}
+        dashboard:
+          tag_accounts_measure: unic usationes
+          tag_languages_dimension: Max usat lingues
+          tag_servers_dimension: Max populari servitores
+          tag_servers_measure: diferent servitores
+          tag_uses_measure: total usationes
+        description_html: Vi hashtags actualmen aparient in mult postas queles vor servitor vide. It posse auxiliar vor usatores decovrir pri quel gente parla nu. Null hashtags es monstrat til que vu aproba les.
         listable: Suggestibil
+        no_tag_selected: Null hashtags esset changeat pro que null esset selectet
         not_listable: Ne suggestibil
+        not_trendable: Ne va aparir in tendenties
         not_usable: Prohibit
+        peaked_on_and_decaying: Atinget su cime ye %{date}, nu deperient
+        title: Populari hashtags
+        trendable: Posse aparir in tendenties
+        trending_rank: 'Tendentie #%{rank}'
         usable: Permisset
         usage_comparison: 'Usat hodie: %{today} vez(es), yer: %{yesterday}'
+        used_by_over_week:
+          one: Usat de un person durant li ultim semane
+          other: Usat de %{count} persones durant li ultim semane
+      title: Tendenties
+      trending: Populari
+    warning_presets:
+      add_new: Adjunter un nov
+      delete: Deleter
+      edit_preset: Modificar prefiguration de avise
+      empty: Vu ancor ha definit null prefigurationes de avise.
+      title: Modificar prefigurationes de avise
     webhooks:
+      enabled: Activ
       events: Evenimentes
       status: Statu
+  admin_mailer:
+    new_trends:
+      new_trending_links:
+        title: Populari ligamentes
+      new_trending_statuses:
+        title: Populari postas
+      new_trending_tags:
+        title: Populari hashtags
+      subject: Nov tendenties a inspecter sur %{instance}
+  aliases:
+    add_new: Crear alias
+    created_msg: Successosimen creat un nov alias. Tu nu posse initiar li movement del antiqui conto.
+    deleted_msg: Successosimen removet li alias. Mover de ti-ta conto a ti-ci ne plu va esser possibil.
+    empty: Tu have null aliases.
+    hint_html: Si tu vole mover de un altri conto a ti-ci, ci tu posse crear un alias, quel es besonat ante que tu posse proceder a mover sequitores del antiqui conto a ti-ci. Ti-ci action, sol, es <strong>ínnociv e reversibil</strong>. <strong>Li conto-migration es initiat del antiqui conto</strong>.
+    remove: Desconexer alias
+  appearance:
+    advanced_web_interface: Avansat web-interfacie
+    advanced_web_interface_hint: 'Si tu vole usar li tot largore de tui ecran, li avansat web-interfacie permisse que tu mey configurar mult columnes diferent por vider tam mult information simultanmen quam tu vole: Hem, federat témpor-linea, quelcunc númere de listes e hashtags.'
+    animations_and_accessibility: Animationes e accessibilitá
+    confirmation_dialogs: Dialogs de confirmation
+    discovery: Decovriment
+    localization:
+      body: Mastodon es traductet de voluntarios.
+      guide_link: https://crowdin.com/project/mastodon
+      guide_link_text: Omnes posse contribuer.
+    sensitive_content: Sensitiv contenete
   application_mailer:
+    notification_preferences: Changear parametres pri email
     salutation: "%{name},"
+    settings: 'Changear parametres pri email: %{link}'
+    unsubscribe: Desabonnar
+    view: 'Vider:'
+    view_profile: Vider profil
+    view_status: Vider posta
+  applications:
+    created: Aplication successosimen creat
+    destroyed: Aplication successosimen deletet
+    logout: Exear
+    regenerate_token: Regenerar accesse-clave
+    token_regenerated: Accesse-clave successosimen regenerat
+    warning: Esse tre cuidosi pri ti-ci data. Nequande parti it a quicunc!
+    your_token: Tui accesse-clave
   auth:
+    apply_for_account: Solicitar un conto
+    captcha_confirmation:
+      help_html: Si tu have desfacilitá solver li CAPTCHA, tu posse contacter nos a %{email} e noi posse auxiliar te.
+      hint_html: Just un cose plu! Noi deve confirmar que tu es homan (por que noi posse impedir li spam!). Solue li CAPTCHA in-infra e clicca "Avansar".
+      title: Control de securitá
     confirmations:
+      awaiting_review: Tui email-adresse es confirmat! Li personale de %{domain} nu va tractar tui registration. Tu va reciver un email si ili aproba tui conto!
+      awaiting_review_title: On tracta tui registration
+      clicking_this_link: cliccar ti-ci ligament
+      login_link: intrar
+      proceed_to_login_html: Tu nu posse proceder a %{login_link}.
+      redirect_to_app_html: Tu deve har esset redirectet al aplication <strong>%{app_name}</strong>. Si to ne evenit, prova %{clicking_this_link} o retornar manualmen al aplication.
+      registration_complete: Tu ha completat tui registration che %{domain}!
       welcome_title: Benevenit, %{name}!
+      wrong_email_hint: Si ti email-adresse ne es corect, tu posse changear it in li parametres de conto.
     delete_account: Deleter li conto
+    delete_account_html: Si tu vole deleter tui conto, tu posse <a href="%{path}">proceder ci</a>. On va petir confirmation de te.
+    description:
+      prefix_invited_by_user: "@%{name} invita te adherer ti-ci servitor de Mastodon!"
+      prefix_sign_up: Adherer Mastodon hodie!
+      suffix: Med un conto, tu va posser sequer persones, postar actualisationes e exchangear missages con usatores de quelcunc Mastodon-servitor e plu!
+    didnt_get_confirmation: Ne recivet un confirmation-ligament?
+    dont_have_your_security_key: Ne have tui clave de securitá?
     forgot_password: Obliviat tu tui passa-parol?
     invalid_reset_password_token: Li clave por reiniciar li passa-parol es ínvalid o expirat. Ples demandar un nov.
+    link_to_otp: Introducte un 2-factor code de tui telefon o un code de recuperation
+    link_to_webauth: Usa tui aparate de clave de securitá
+    log_in_with: Intrar med
+    login: Intrar
+    logout: Exear
+    migrate_account: Mover te a un conto diferent
+    migrate_account_html: Si tu vole redirecter ti-ci conto a un altri, tu posse <a href="%{path}">configurar it ci</a>.
+    or_log_in_with: O intrar med
+    privacy_policy_agreement_html: Yo leet e consenti li <a href="%{privacy_policy_path}" target="_blank">politica pri privatie</a>
+    progress:
+      confirm: Confirmar email
+      details: Tui detallies
+      review: Nor revise
+      rules: Acceptar regules
+    providers:
+      cas: CAS
+      saml: SAML
+    register: Adherer
+    registration_closed: "%{instance} ne accepta nov membres"
+    resend_confirmation: Reinviar ligament de confirmation
     reset_password: Reiniciar passa-parol
     rules:
       accept: Acceptar
       back: Retro
+      invited_by: 'Tu posse adherer %{domain} mersí al invitation quel tu recivet de:'
+      preamble: Tis es etablisset e infortiat del moderatores de %{domain}.
+      preamble_invited: Ante que tu procede, ples considerar li regules establisset del moderatores de %{domain}.
+      title: Quelc regules basic.
+      title_invited: Tu ha esset invitat.
     security: Securitá
     set_new_password: Establisser nov passa-parol
+    setup:
+      email_below_hint_html: Vider tui spam-emails, o petir un altre. Tu posse corectar tui email-adresse si it es íncorect.
+      email_settings_hint_html: Clicca li ligament quel noi inviat a te por verificar %{email}. Noi va atender ci.
+      link_not_received: Recivet null ligament?
+      new_confirmation_instructions_sent: Tu va reciver un nov email con li ligament de confirmation in quelc minutes!
+      title: Vider tui inbuxe
+    sign_in:
+      preamble_html: Aperter session med tui <strong>%{domain}</strong> detallies. Si tui conto logia che un diferent servitor, tu ne va posser intrar ci.
+      title: Aperter session che %{domain}
+    sign_up:
+      manual_review: Adhesiones a %{domain} es tractat manualmen de nor moderatores. Por auxiliar nos tractar tui aplication, scri un poc pri te e pro quo tu vole un conto che %{domain}.
+      preamble: Med un conto che ti-ci Mastodon-servitor, tu va posser sequer quelcunc altri person in li retage, sin egarda a u logia su conto.
+      title: Crear un conto che %{domain}.
+    status:
+      account_status: Statu del conto
+      confirming: Atendent li confirmation del email-adresse.
+      functional: Tui conto es completmen functional.
+      pending: Tu conto atende tractation de nor personale. Ti fórsan va durar quelc témpor. Tu va reciver un email si tui aplication es aprobat.
+      redirecting_to: Tui conto es ínactiv pro que it actualmen redirecte a %{acct}.
+      self_destruct: Pro que %{domain} va cluder bentost, tu have solmen limitat accesse a tui conto.
+      view_strikes: Vide anteyan admonimentes contra tui conto
+    too_fast: Formul inviat tro rapid, prova denov.
+    use_security_key: Usar clave de securitá
   challenge:
-    confirm: Continuar
+    confirm: Avansar
     hint_html: "<strong>Nota</strong>: On ne va petir tui passa-parol denov por li venient hor."
     invalid_password: Ínvalid passa-parol
     prompt: Confirmar passa-parol por avansar
+  crypto:
+    errors:
+      invalid_key: ne es un valid clave Ed25519 o Curve25519
+      invalid_signature: ne es un valid signatura Ed25519
   date:
     formats:
       default: "%d.%m.%Y"
@@ -659,73 +1001,234 @@ ie:
   datetime:
     distance_in_words:
       about_x_hours: "%{count}h"
+      about_x_months: "%{count}me"
       about_x_years: "%{count}a"
       almost_x_years: "%{count}a"
+      half_a_minute: Just nu
+      less_than_x_minutes: "%{count}m"
+      less_than_x_seconds: Just nu
+      over_x_years: "%{count}a"
+      x_days: "%{count}d"
+      x_minutes: "%{count}m"
+      x_months: "%{count}me"
       x_seconds: "%{count}s"
   deletes:
+    challenge_not_passed: Li information quel tu dat ne esset corect
     confirm_password: Introducte tui actual passa-parol por verificar tui identitá
+    confirm_username: Scri tu usator-nómine por confirmar li procedura
     proceed: Deleter li conto
+    success_msg: Tui conto esset successosimen deletet
+    warning:
+      before: 'Ante proceder, ples leer ti notas cuidosimen:'
+      caches: Contenete quel ha esset inmagasinat de altri servitores fórsan va persister
+      data_removal: Tui postas e altri data va esser permanentmen removet
+      email_change_html: Tui posse <a href="%{path}">changear tui email-adresse</a> sin deleter tui conto
+      email_contact_html: Si ancor it ne ariva, tu posse inviar un email a <a href="mailto:%{email}">%{email}</a> por auxilie
+      email_reconfirmation_html: Si tu ne recive li confirmation-email, tu posse <a href="%{path}">solicita it denov</a>
+      irreversible: Tu ne va posser restaurar o reactivisar tui conto
+      more_details_html: Por plu mult detallies, vide li <a href="%{terms_path}">politica pri privatie</a>.
+      username_available: Tui usator-nómine va retornar a esser disponibil
+      username_unavailable: Tui usator-nómine va restar índisponibil
   disputes:
     strikes:
+      action_taken: Action fat
       appeal: Apellar
+      appeal_approved: Ti admoniment ha esset apellat successosimen e nu es ínvalid
+      appeal_rejected: Li apelle ha esset rejectet
+      appeal_submitted_at: Apelle inviat
+      appealed_msg: Tui apelle ha esset inviat. Si it es aprobat, tu va esser notificat.
+      appeals:
+        submit: Inviar apelle
+      approve_appeal: Aprobar apelle
+      associated_report: Associat raporte
+      created_at: Date
+      description_html: Tis es li actiones fat contra tui conto e admonimentes misset a te del personale de %{instance}.
+      recipient: Adressat a
+      reject_appeal: Rejecter apelle
+      status: 'Posta #%{id}'
+      status_removed: Posta ja removet del sistema
+      title: "%{action} de %{date}"
+      title_actions:
+        delete_statuses: Removement de posta
+        disable: Gelation de conto
+        mark_statuses_as_sensitive: Marcation de postas quam sensitiv
+        none: Admoniment
+        sensitive: Marcation de conto quam sensitiv
+        silence: Limitation de conto
+        suspend: Suspension de conto
+      your_appeal_approved: Tui apelle ha esset aprobat
+      your_appeal_pending: Tu ha fat un apelle
+      your_appeal_rejected: Tui apelle ha esset rejectet
+  domain_validator:
+    invalid_domain: ne es un valid dominia-nómine
+  edit_profile:
+    basic_information: Basic information
+    hint_html: "<strong>Customisa ti quel gente vide sur tui public profil e apu tui postas.</strong> Altri persones es plu probabil sequer te e interacter con te si tu have un detalliat profil e un profil-image."
+    other: Altri
+  errors:
+    '400': Li demande quel tu inviat esset ínvalid o misformat.
+    '403': Tu ne have permission vider ti-ci págine.
+    '404': Li págine quel tu sercha ne es ci.
+    '406': Ti-ci págine ne es disponibil in li formate petit.
+    '410': Li págine quel tu serchat ne plu existe ci.
+    '422':
+      content: Verification de securitá fallit. Esque tu blocca cookies?
+      title: Verification de securitá fallit
+    '429': Tro mult demandes
+    '500':
+      content: Pardona nos, alquo errat in nor servitor.
+      title: Ti-ci págine ne es corect
+    '503': On ne posset servir li págine pro un falliment temporari del servitor.
+    noscript_html: Por usar li web-aplication de Mastodon, ples activisar JavaScript. Alternativmen, prova un del <a href="%{apps_path}">nativ aplicationes</a> por Mastodon por tui platform.
+  existing_username_validator:
+    not_found: ne posset trovar un local usator con ti usator-nómine
+    not_found_multiple: ne posset trovar %{usernames}
   exports:
     archive_takeout:
       date: Date
+      download: Descargar tui archive
+      hint_html: Tu posse petir un archive de tui <strong>postas e cargat medie</strong>. Li exportat data va esser in li formate ActivityPub, leibil de quelcunc programmatura concordant. Tu posse petir un archive chascun 7 dies.
+      in_progress: Compilant tui archive...
+      request: Petir tui archive
       size: Grandore
     blocks: Tu ha bloccat
     bookmarks: Marcatores
     csv: CSV
+    domain_blocks: Dominia-bloccas
     lists: Listes
     mutes: Tu silentia
+    storage: Inmagasination de medie
   featured_tags:
     add_new: Adjunter un nov
+    errors:
+      limit: Tu ja ha pinglat li maxim númere de hashtags
     hint_html: "<strong>Pinglar tui max important hashtags sur tui profil.</strong> Un bonissim maniere de mantener un registre de tui ovres e projectes, pinglat hashtags es monstrat prominentmen sur tui profil e permisse rapid accesse a tui propri postas."
   filters:
     contexts:
       account: Profiles
+      home: Hem e listes
+      notifications: Notificationes
+      public: Public témpor-lineas
       thread: Conversationes
+    edit:
+      add_keyword: Adjunter término
+      keywords: Términos
+      statuses: Individual postas
+      statuses_hint_html: Ti filtre aplica a selectet postas individual sin egarda a ca ili contene li términos in-infra. <a href="%{path}">Reviser o remover postas del filtre</a>.
+      title: Modificar filtre
+    errors:
+      deprecated_api_multiple_keywords: On ne posse changear ti parametres per ti-ci aplication pro que ili aplica a plu quam un filtrat término. Usa un aplication plu recent o li web-interfacie.
+      invalid_context: Null o ínvalid contextu dat
     index:
+      contexts: Filtres in %{contexts}
+      delete: Deleter
+      empty: Tu have null filtres.
+      expires_in: Expira in %{distance}
+      expires_on: Expira ye %{date}
+      keywords:
+        one: "%{count} término"
+        other: "%{count} términos"
+      statuses:
+        one: "%{count} posta"
+        other: "%{count} postas"
+      statuses_long:
+        one: "%{count} individual posta celat"
+        other: "%{count} individual postas celat"
       title: Filtres
     new:
       save: Conservar nov filtre
+      title: Adjunter nov filtre
+    statuses:
+      back_to_filter: Retornar al filtre
+      batch:
+        remove: Remover de filtre
+      index:
+        hint: Ti filtre aplica a selectet postas individual sin egarda a altri criteries. Tu posse adjunter plu postas a ti-ci filtre del web-interfacie.
+        title: Filtrat postas
   generic:
     all: Omni
+    all_items_on_page_selected_html:
+      one: "<strong>%{count}</strong> element in ti-ci págine es selectet."
+      other: Omni <strong>%{count}</strong> elementes in ti-ci págine es selectet.
+    all_matching_items_selected_html:
+      one: "<strong>%{count}</strong> element concordant tui sercha es selectet."
+      other: Omni <strong>%{count}</strong> elementes concordant tui sercha es selectet.
     cancel: Anullar
     changes_saved_msg: Modificationes conservat successosimen!
     confirm: Confirmar
     copy: Copiar
+    delete: Deleter
+    deselect: Desselecter omnis
     none: 'Null'
+    order_by: Ordinar per
     save_changes: Conservar changes
+    select_all_matching_items:
+      one: Selecter %{count} element concordant tui sercha.
+      other: Selecter omni %{count} elementes concordant tui sercha.
     today: hodie
+    validation_errors:
+      one: Alquo ancor ne es bon! Ples controlar li errore in-infra
+      other: Alquo ancor ne es bon! Ples controlar li %{count} errores in-infra
   imports:
     errors:
       empty: li file es vacui
+      incompatible_type: Íncompatibil con li selectet specie de importation
       invalid_csv_file: 'Ínvalid file CSV. Errore: %{error}'
       over_rows_processing_limit: contene plu quam %{count} lineas
       too_large: li file es tro grand
+    failures: Fallites
     imported: Importat
+    mismatched_types_warning: It apari que tu fórsan selectet li íncorect specie por ti importation, ples controlar denov.
     modes:
       merge: Coalescer
+      merge_long: Conservar existent registres e adjunter li novis
+      overwrite: Remplazzar
+      overwrite_long: Remplazzar existent registres per li novis
     overwrite_preambles:
+      blocking_html: Tu va <strong>remplazzar tui liste de bloccat contos</strong> per til <strong>%{total_items} contos</strong> de <strong>%{filename}</strong>.
+      bookmarks_html: Tu va <strong>remplazzar tui marcatores</strong> per til <strong>%{total_items} postas</strong> de <strong>%{filename}</strong>.
+      domain_blocking_html: Tu va <strong>remplazzar tui liste de bloccat dominias</strong> per til <strong>%{total_items} dominias</strong> de <strong>%{filename}</strong>.
+      following_html: Tu va <strong>sequer</strong> til <strong>%{total_items} contos</strong> de <strong>%{filename}</strong> e <strong>dessequer omni altri contos</strong>.
+      lists_html: Tu va <strong>remplazzar tui listes</strong> per li contenete de <strong>%{filename}</strong>. Til <strong>%{total_items} contos</strong> va esser adjuntet a nov listes.
       muting_html: Tu va <strong>remplazzar tui liste de silentiat contos</strong> per til <strong>%{total_items} contos</strong> de <strong>%{filename}</strong>.
     preambles:
+      blocking_html: Tu va <strong>bloccar</strong> til <strong>%{total_items} contos</strong> de <strong>%{filename}</strong>.
+      bookmarks_html: Tu va adjunter til <strong>%{total_items} postas</strong> de <strong>%{filename}</strong> a tui <strong>marcatores</strong>.
+      domain_blocking_html: Tu va <strong>bloccar</strong> til <strong>%{total_items} dominias</strong> de <strong>%{filename}</strong>.
+      following_html: Tu va <strong>sequer</strong> til <strong>%{total_items} contos</strong> de <strong>%{filename}</strong>.
+      lists_html: Tu va adjunter til <strong>%{total_items} contos</strong> de <strong>%{filename}</strong> a tui <strong>listes</strong>. Nov listes va esser creat si ne hay un liste a quel adjunter.
       muting_html: Tu va <strong>silentiar</strong> til <strong>%{total_items} contos</strong> de <strong>%{filename}</strong>.
+    preface: Tu posse importar data quel tu ha exportat de un altri servitor, quam un liste del gente quem tu seque o blocca.
+    recent_imports: Recent importationes
     states:
       finished: Finit
+      in_progress: Progressent
+      scheduled: Planat
+      unconfirmed: Ínconfirmat
+    status: Statu
+    success: Tui data esset cargat successosimen e va esser tractat tam tost quam es possibil
+    time_started: Comensat ye
     titles:
-      lists: Importar listes
-      muting: Importation de silentiat contos
-    type: Tip de importation
+      blocking: Important bloccat contos
+      bookmarks: Important marcatores
+      domain_blocking: Important bloccat dominias
+      following: Important sequet contos
+      lists: Important listes
+      muting: Important silentiat contos
+    type: Specie de importation
     type_groups:
       constructive: Seques e marcatores
       destructive: Bloccas & silentias
     types:
       blocking: Liste de bloccas
       bookmarks: Marcatores
+      domain_blocking: Liste de dominia-bloccas
       following: Liste de sequetes
       lists: Listes
+      muting: Liste de silentiationes
     upload: Cargar
   invites:
+    delete: Desactivisar
     expired: Expirat
     expires_in:
       '1800': 30 minutes
@@ -735,14 +1238,52 @@ ie:
       '604800': 1 semane
       '86400': 1 die
     expires_in_prompt: Nequande
+    generate: Generar ligament de invitation
+    invalid: Ti-ci invitation ne es valid
+    invited_by: 'Tu esset invitat de:'
+    max_uses:
+      one: 1 use
+      other: "%{count} uses"
+    max_uses_prompt: Null límite
+    prompt: Generar e distribuer ligamentes a altres por dar accesse a ti-ci servitor
+    table:
+      expires_at: Expira
+      uses: Uses
+    title: Invitar gente
+  lists:
+    errors:
+      limit: Tu ha atinget li maxim númere de listes
   login_activities:
     authentication_methods:
+      otp: aplication de 2-factor autentication
       password: passa-parol
+      sign_in_token: code de securitá per email
+      webauthn: claves de securitá
     description_html: Si tu vide activitá quel tu ne conosse, considera changear tui passa-parol e activisar 2-factor autentication.
+    empty: Null historie de autentication disponibil
+    failed_sign_in_html: Fallit prova de apertion de session per %{method} de %{ip} (%{browser})
+    successful_sign_in_html: Successosi apertion de session per %{method} de %{ip} (%{browser})
+    title: Historie de autentication
   mail_subscriptions:
     unsubscribe:
+      action: Yes, desabonnar
+      complete: Desabonnat
+      confirmation_html: Esque tu vermen vole desabonnar de reciver %{type} por Mastodon che %{domain} a tui email-adresse %{email}? Tu sempre posse reabonnar per tui <a href="%{settings_path}">parametres pri email-notificationes</a>.
+      emails:
+        notification_emails:
+          favourite: email-notificationes pri favoritisationes
+          follow: email-notificationes pri seques
+          follow_request: email-notificationes pri seque-petitiones
+          mention: email-notificationes pri mentiones
+          reblog: email-notificationes pri boosts
+      resubscribe_html: Si tu ha desabonnat errarimen, tu posse reabonnar per tui <a href="%{settings_path}">parametres pri email-notificationes</a>.
+      success_html: Tu ne plu va reciver %{type} por Mastodon che %{domain} a tui email-adresse %{email}.
       title: Desabonnar
+  media_attachments:
+    validations:
+      images_and_video: On ne posse atachar un video a un posta quel ja contene images
   migrations:
+    acct: Translocat a
     set_redirect: Configurar un redirection
   move_handler:
     carry_mutes_over_text: Ti-ci usator movet se de %{acct}, quel tu hat silentiat.
@@ -754,13 +1295,41 @@ ie:
       title: Nov petition de sequer
     mention:
       action: Responder
+      title: Nov mention
     poll:
       subject: Un balotation de %{name} ha finit
+    reblog:
+      body: 'Tui posta esset boostat de %{name}:'
+      subject: "%{name} boostat tui posta"
+      title: Nov boost
+    status:
+      subject: "%{name} just postat"
+    update:
+      subject: "%{name} modificat un posta"
+  notifications:
+    administration_emails: Email-notificationes pri administration
+    email_events: Evenimentes por email-notificationes
+    email_events_hint: 'Selecte li evenimentes pri queles tu vole reciver notificationes:'
+    other_settings: Parametres pri altri notificationes
+  number:
+    human:
+      decimal_units:
+        units:
+          billion: B
+          million: M
+          thousand: m
   otp_authentication:
+    code_hint: Inmetter li code generat de tui aplication de autentication por confirmar
+    description_html: Si tu activisa <strong>2-factor autentication</strong> per un aplication de autentication, aperter un session va postular que tu have possession de tui telefon, quel va generar codes por que tu mey inmetter les.
     enable: Activar
+    instructions_html: "<strong>Scande ti-ci code QR ad-in Google Authenticator o un simil aplication TOTP in tu telefon</strong>. Pos nu, ti aplication va generar codes queles tu va dever inmetter quande tu aperte un session."
+    manual_instructions: 'Si tu ne posse scander li code QR e besona inmetter it manualmen, vi li crud-text secrete:'
     setup: Configurar
+    wrong_code: Li inmettet code esset ínvalid! Esque li témpor del servitor e del aparate corect?
   pagination:
+    newer: Plu nov
     next: Seq
+    older: Plu old
     prev: Prec
     truncate: "&hellip;"
   polls:
@@ -777,33 +1346,128 @@ ie:
       too_many_options: ne posse contener plu quam %{max} optiones
   preferences:
     other: Altri
+    public_timelines: Public témpor-lineas
   privacy:
+    hint_html: "<strong>Customisa qualmen tu vole que tui profil e tui postas posse esser trovat.</strong> Mastodon have un varietá de manieres por auxiliar te atinger un auditorie plu grand quande activisat. Prende un moment por reviser ti parametres por far cert que ili concorda tui casu de usation."
+    privacy: Privatie
+    privacy_hint_html: Decide quant mult tu vole revelar por li beneficie de altres. Gente decovri interessant profiles e frisc aplicationes per vider li seques de altres e vider de quel aplicationes ili posta, ma tu fórsan prefere celar ti.
+    reach: Atingement
+    reach_hint_html: Decide ca tu vole esser decovrit e sequet de nov persones. Vole tu que tui postas mey aparir sur li págine "Explorar"? Vole tu que altri gente posse vider te in lor seque-recomandationes? Vole tu acceptar omni nov sequitores automaticmen, o manualmen tractar chascun?
     search: Sercha
+    search_hint_html: Decide qualmen tu vole esser trovat. Vole tu que gente trova te per ti pri quel tu ha postat publicmen? Vole tu que gente éxter Mastodon trova tui profil quande ili sercha li web? Ples memorar que complet exclusion de omni serchatores ne posse esser garantit por public information.
+    title: Privatie e atingement
+  privacy_policy:
+    title: Politica pri Privatie
+  reactions:
+    errors:
+      limit_reached: Límite de diferent reactiones atinget
+      unrecognized_emoji: ne es un reconosset emoji
   relationships:
     activity: Activitá de conto
+    confirm_follow_selected_followers: Esque tu vermen vole sequer selectet sequitores?
+    confirm_remove_selected_followers: Esque tu vermen vole remover selectet sequitores?
+    confirm_remove_selected_follows: Esque tu vermen vole remover selectet sequetes?
     dormant: Dormient
+    follow_failure: Ne posset sequer quelc del contos selectet.
+    follow_selected_followers: Sequer selectet sequitores
+    followers: Sequitores
+    following: Sequetes
+    invited: Invitat
+    last_active: Ultimmen activ
+    most_recent: Max recent
+    moved: Movet
     mutual: Reciproc
+    primary: Primari
+    relationship: Relation
+    remove_selected_domains: Remover omni sequitores del selectet dominias
+    remove_selected_followers: Remover selectet sequitores
+    remove_selected_follows: Dessequer selectet usatores
     status: Statu del conto
+  rss:
+    content_warning: 'Avise pri li contenete:'
+    descriptions:
+      account: Public postas de @%{acct}
+      tag: 'Public postas con li hashtag #%{hashtag}'
+  scheduled_statuses:
+    over_daily_limit: Tu ha atinget li límite de %{limit} planat postas por hodie
+    over_total_limit: Tu ha atinget li límite de %{limit} planat postas
+    too_soon: Li planat date deve esser in li future
+  self_destruct:
+    lead_html: Ínfortunatmen, <strong>%{domain}</strong> va bentost permanentmen cluder. Si tu havet un conto ta, tu ne va posser continuar usar it, ma tu ancor posse demandar un archive de tui data.
+    title: Ti-ci servitor va cluder bentost
   sessions:
     activity: Ultim activitá
     browser: Navigator
     browsers:
+      alipay: Alipay
+      blackberry: BlackBerry
       chrome: Chrome
+      edge: Microsoft Edge
       electron: Electron
+      firefox: Firefox
       generic: Ínconosset navigator
+      huawei_browser: Huawei Browser
+      ie: Internet Explorer
+      micro_messenger: MicroMessenger
+      nokia: Nokia S40 Ovi Browser
+      opera: Opera
+      otter: Otter
+      phantom_js: PhantomJS
+      qq: QQ Browser
+      safari: Safari
+      uc_browser: UC Browser
       unknown_browser: Ínconosset navigator
+      weibo: Weibo
+    current_session: Actual session
     description: "%{browser} in %{platform}"
     explanation: Tis-ci es li navigatores queles actualmen ha initiat sessiones a tui Mastodon-conto.
     ip: IP
     platforms:
+      adobe_air: Adobe Air
+      android: Android
+      blackberry: BlackBerry
+      chrome_os: ChromeOS
+      firefox_os: Firefox OS
+      ios: iOS
+      kai_os: KaiOS
+      linux: Linux
+      mac: macOS
       unknown_platform: Ínconosset platform
       windows: Windows
+      windows_mobile: Windows Mobile
+      windows_phone: Windows Phone
     revoke: Revocar
+    revoke_success: Session successosimen revocat
+    title: Sessiones
+    view_authentication_history: Vider li historie de autentication por tui conto
   settings:
     account: Conto
+    account_settings: Parametres del conto
+    aliases: Aliases del conto
     appearance: Aspecte
+    authorized_apps: Autorisat aplicationes
+    back: Retornar a Mastodon
+    delete: Deletion de conto
+    development: Developation
+    edit_profile: Modificar profil
+    export: Exportation de data
+    featured_tags: Recomandat hashtags
+    import: Importar
+    import_and_export: Importation e exportation
+    migrate: Migration de conto
+    notifications: Notificationes
+    preferences: Preferenties
+    profile: Public profil
+    relationships: Sequetes e sequitores
+    statuses_cleanup: Automatisat deletion de postas
+    strikes: Admonimentes moderatori
+    two_factor_authentication: 2-factor autentication
+    webauthn_authentication: Claves de securitá
   statuses:
     attached:
+      audio:
+        one: "%{count} audio-file"
+        other: "%{count} audio-files"
       description: 'Atachat: %{attached}'
       image:
         one: "%{count} image"
@@ -811,8 +1475,10 @@ ie:
       video:
         one: "%{count} video"
         other: "%{count} videos"
+    boosted_from_html: Boostat de %{acct_link}
     default_language: Sam quam li lingue del interfacie
     edited_at_html: Modificat ye %{date}
+    open_in_web: Aperter in web
     pin_errors:
       direct: On ne posse pinglar postas queles es visibil solmen a mentionat usatores
       limit: Tu ja ha pinglat li maxim númere de postas
@@ -826,38 +1492,131 @@ ie:
         one: "%{count} vote"
         other: "%{count} votes"
       vote: Votar
+    show_more: Monstrar plu
+    show_newer: Monstrar plu nov
+    show_older: Monstrar plu old
     title: "%{name}: «%{quote}»"
     visibilities:
+      private_long: Monstrar solmen a sequitores
       public: Public
+      public_long: Omnes posse vider
       unlisted: Delistat
+      unlisted_long: Omnes posse vider, ma ne listat sur public témpor-lineas
   statuses_cleanup:
+    enabled: Automaticmen deleter old postas
+    enabled_hint: Deleter automaticmen tui postas quande ili atinge un cert etá, except si ili concorda con un del exceptiones ci infra
     exceptions: Exceptiones
+    explanation: Deletion de postas es un operation expensiv, e pro to es efectuat lentmen quande li servitor ne es ocupat. Pro to, on posse deleter tui postas un cert témpor pos atinger un cert etá.
+    ignore_favs: Ignorar favorites
+    interaction_exceptions: Exceptiones basat sur interactiones
+    keep_direct: Retener missages direct
+    keep_direct_hint: Ne delete quelcunc de tui direct missages
     keep_pinned: Conservar pinglat postas
     keep_pinned_hint: Delete null de tui pinglat postas
     keep_polls: Conservar balotationes
     keep_polls_hint: Delete null de tui balotationes
+    keep_self_fav: Retener postas favorit de te
     min_age:
       '1209600': 2 semanes
       '31556952': 1 annu
+  strikes:
+    errors:
+      too_late: It es tro tard por apellar ti admoniment
+  tags:
+    does_not_match_previous_name: ne concorda li anteyan nómine
+  themes:
+    contrast: Mastodon (Alt contraste)
+    default: Mastodon (Obscur)
+    mastodon-light: Mastodon (Luminosi)
   time:
     formats:
       default: "%d.%m.%Y ye %H:%M"
       month: "%b %Y"
       time: "%H:%M"
+      with_time_zone: "%d.%m.%Y ye %H:%M %Z"
+  translation:
+    errors:
+      quota_exceeded: Li servitor ha exhaustet nor quote por li servicie de traduction.
+      too_many_requests: Hay tro mult recent demandes al servicie de traduction.
   two_factor_authentication:
     add: Adjunter
+    disable: Desactivisar 2FA
+    disabled_success: 2-factor autentication successosimen desactivisat
+    edit: Modificar
+    enabled: 2-factor autentication es activisat
+    enabled_success: 2-factor autentication successosimen activisat
+    generate_recovery_codes: Generar codes de recuperation
+    lost_recovery_codes: Codes de recuperation lassa te recuperar accesse a tui conto si tu perdi tui telefon. Si tu ha perdit tui codes de recuperation, tu posse regenerar les ci. Tui antiqui codes de recuperation va esser ínvalidat.
+    methods: Metodes de 2-factor autentication
+    otp: Aplication de autentication
+    recovery_codes: Copiar tui codes de recuperation
+    recovery_codes_regenerated: Codes de recuperation successosimen regenerat
+    recovery_instructions_html: Si tu perdi accesse a tui telefon, tu posse usar un del codes de recuperation in-infra por reganiar accesse a tui conto. <strong>Mantener li securitá del codes de recuperation</strong>. Per exemple, tu fórsan va printar les e inmagasinar les con altri important documentes.
+    webauthn: Claves de securitá
   user_mailer:
+    appeal_approved:
+      action: Ear a tui conto
+      explanation: Li apelle del admoniment contra tui conto ye %{strike_date} quel tu fat ye %{appeal_date} ha esset aprobat. Tui conto retorna a esser in bon statu.
+      subject: Tui apelle de %{date} ha esset aprobat
+      title: Apelle aprobat
+    appeal_rejected:
+      explanation: Li apelle del admoniment contra tui conto ye %{strike_date} quel tu fat ye %{appeal_date} ha esset rejectet.
+      subject: Tui apelle de %{date} ha esset rejectet
+      title: Apelle rejectet
+    backup_ready:
+      explanation: Tu solicitat un complet archive de tui Mastodon-conto. Nu it es pret por descargar!
+      subject: Tui archive es pret por descargar
+      title: Descargar archive
     suspicious_sign_in:
       change_password: changear tui passa-parol
+      details: 'Vi li detallies del apertion de session:'
+      explanation: On detectet un intration a tui conto de un nov IP-adresse.
+      further_actions_html: Si to ne esset tu, on recomanda que tu %{action} strax e activisar 2-factor autentication por mantener tui conto secur.
+      subject: Tui conto ha esset accesset de un nov IP-adresse
+      title: Un nov intration
     warning:
+      appeal: Inviar un apelle
+      appeal_description: Si tu crede que ti es un error, tu posse inviar un apelle al personale de %{instance}.
       categories:
         spam: Spam
+        violation: Contenete viola li sequent regules del comunité
+      explanation:
+        delete_statuses: On judicat que quelc de tui postas viola un o plu del regules del comunité e pro to ha esset removet del moderatores del %{instance}.
+        disable: Tu ne plu posse usar tui conto, ma tui profil e altri data resta integri. Tu posse solicitar un archive de tui data, changear parametres del conto, o deleter tui conto.
+        mark_statuses_as_sensitive: Quelc de tui postas ha esset marcat quam sensitiv del moderatores de %{instance}. Ti significa que gente va dever cliccar li medie in li postas ante que un previse es monstrat. Tu self posse marcar medie quam sensitiv quande tu posta futurimen.
+        sensitive: Pos nu, omni tui cargat medie-files va esser marcat quam sensitiv e celat detra un avise que on deve cliccar por transpassar.
+        silence: Tu ancor posse usar tui conto, ma solmen gente qui ja seque te va posser vider tui postas in ti-ci servitor, e tu va esser excludet de utensiles de decovrition. Támen, altres ancor posse sequer te manualmen.
+        suspend: Tu ne plu posse usar tui conto, e tui profil e altri data nu es ínaccessibil. Tu ancor posse intrar por solicitar un archive de tui data til que li data es removet in circa 30 dies, ma noi va retener quelc basic data pri te por preventer te evitar li suspension.
       reason: 'Rason:'
+      statuses: 'Postas citat:'
       subject:
+        delete_statuses: Tui postas che %{acct} ha esset removet
+        disable: Tui conto %{acct} ha esset gelat
+        mark_statuses_as_sensitive: Tui postas che %{acct} ha esset marcat quam sensitiv
         none: Admoniment por %{acct}
+        sensitive: Tui postas che %{acct} ve esser marcat quam sensitiv pos nu
+      title:
+        delete_statuses: Postas efaciat
+        sensitive: Conto marcat quam sensitiv
+        silence: Conto limitat
+        suspend: Conto suspendet
     welcome:
+      edit_profile_action: Configuration de profil
+      explanation: Vi quelc suggestiones por que tu mey comensar
+      final_action: Comensar postar
       subject: Benevenit a Mastodon
+      title: Benevenit, %{name}!
   users:
     seamless_external_login: Tu ha intrat per un servicie external, dunc parametres pri tui passa-parol e email-adresse ne es disponibil.
   verification:
+    extra_instructions_html: '<strong>Nota</strong>: Li ligament in tui websitu posse esser ínvisibil. Li important parte es <code>rel="me"</code> quel prevente fals self-identification in websitus con contenete generat de usatores. Tu posse mem usar un <code>link</code> element in li cap-section del págine vice <code>a</code>, ma li HTML code deve esser accessibil sin executer JavaScript.'
+    here_is_how: Vide qualmen
     verification: Verification
+    verified_links: Tui verificat ligamentes
+  webauthn_credentials:
+    add: Adjunter nov clave de securitá
+    create:
+      error: Un problema evenit durant li adjuntion de tui clave de securitá. Ples provar denov.
+      success: Tui clave de securitá esset adjuntet con successe.
+    delete: Deleter
+    delete_confirmation: Vole tu vermen deleter ti-ci clave de securitá?
diff --git a/config/locales/lad.yml b/config/locales/lad.yml
index 626d2da7eca..471b830a3e7 100644
--- a/config/locales/lad.yml
+++ b/config/locales/lad.yml
@@ -226,6 +226,56 @@ lad:
         update_status: Aktualiza publikasyon
         update_user_role: Aktualiza rolo
       actions:
+        approve_appeal_html: "%{name} acheto la solisitasyon de moderasyon de %{target}"
+        approve_user_html: "%{name} acheto el enrejistramiento de %{target}"
+        assigned_to_self_report_html: "%{name} asinyo el raporto %{target} a si mezmo"
+        change_email_user_html: "%{name} troko el adreso de posta elektronika del utilizador %{target}"
+        change_role_user_html: "%{name} troko el rolo de %{target}"
+        confirm_user_html: "%{name} konfirmo el adreso de posta elektronika del utilizador %{target}"
+        create_account_warning_html: "%{name} embio una avertensya a %{target}"
+        create_announcement_html: "%{name} kriyo un muevo pregon %{target}"
+        create_canonical_email_block_html: "%{name} bloko la posta elektronika kon el hash %{target}"
+        create_custom_emoji_html: "%{name} kargo el muevo emoji %{target}"
+        create_domain_allow_html: "%{name} perimitio la federasyon kon el domeno %{target}"
+        create_domain_block_html: "%{name} bloko el domeno %{target}"
+        create_email_domain_block_html: "%{name} bloko el domeno de posta elektronika %{target}"
+        create_ip_block_html: "%{name} kriyo una regla para IP %{target}"
+        create_unavailable_domain_html: "%{name} detuvo las entregas al domeno %{target}"
+        create_user_role_html: "%{name} kriyo el rolo %{target}"
+        demote_user_html: "%{name} degrado a %{target}"
+        destroy_announcement_html: "%{name} supremio el pregon %{target}"
+        destroy_canonical_email_block_html: "%{name} dezbloko la posta elektronika kon el hash %{target}"
+        destroy_custom_emoji_html: "%{name} supremio el emoji %{target}"
+        destroy_domain_allow_html: "%{name} bloko la federasyon kon el domeno %{target}"
+        destroy_domain_block_html: "%{name} dezbloko el domeno %{target}"
+        destroy_email_domain_block_html: "%{name} dezbloko el domeno de posta elektronika %{target}"
+        destroy_instance_html: "\"%{name} purgo el domeno %{target}"
+        destroy_ip_block_html: "%{name} supremio una regla para la IP %{target}"
+        destroy_status_html: "%{name} supremio una publikasyon de %{target}"
+        destroy_unavailable_domain_html: "%{name} reinisyo las entregas al domeno %{target}"
+        destroy_user_role_html: "%{name} supremio el rolo %{target}"
+        disable_2fa_user_html: "%{name} dezaktivo el rekerimiento en dos pasos para el utilizador %{target}"
+        disable_custom_emoji_html: "%{name} dezaktivo el emoji %{target}"
+        disable_sign_in_token_auth_user_html: "%{name} inkapasito la autentifikasyon por token de posta elektronika para %{target}"
+        disable_user_html: "%{name} inkapasito la koneksion kon el kuento para el utilizador %{target}"
+        enable_custom_emoji_html: "%{name} aktivo el emoji %{target}"
+        enable_sign_in_token_auth_user_html: "%{name} tiene kapasitado la autentifikasyon por token de posta elektronika para %{target}"
+        enable_user_html: "%{name} kapasito koneksion kon kuento para el utilizador %{target}"
+        memorialize_account_html: "%{name} konvirtio el kuento de %{target} en una pajina de bendicha memoria"
+        promote_user_html: "%{name} promosyon al utilizador %{target}"
+        reject_appeal_html: "%{name} refuzo la solisitasyon de moderasyon de %{target}"
+        reject_user_html: "%{name} refuzo el enrejistramiento de %{target}"
+        remove_avatar_user_html: "%{name} supremio la imaje de profil de %{target}"
+        reopen_report_html: "%{name} reavrio el raporto %{target}"
+        resend_user_html: "%{name} tiene reembiado la posta de konfirmasyon para %{target}"
+        reset_password_user_html: "%{name} reinisyo el kod del utilizador %{target}"
+        resolve_report_html: "%{name} rezolvio el raporto %{target}"
+        sensitive_account_html: "%{name} marko la multimedia de %{target} komo sensivle"
+        silence_account_html: "%{name} silensyo el kuento de %{target}"
+        suspend_account_html: "%{name} suspendio el kuento de %{target}"
+        unassigned_report_html: "%{name} dezasinyo el raporto %{target}"
+        unblock_email_account_html: "%{name} tiene dezblokado el adreso de posta de %{target}"
+        unsensitive_account_html: "%{name} dezmarko la multimedia de %{target} komo sensivle"
         unsilence_account_html: "%{name} kito el limite del kuento de %{target}"
         unsuspend_account_html: "%{name} reaktivo el kuento de %{target}"
         update_announcement_html: "%{name} aktualizo el pregon %{target}"
@@ -256,6 +306,7 @@ lad:
       unpublish: Retirar publikasyon
       unpublished_msg: Pregon retirado kon sukseso!
       updated_msg: Pregon aktualizado kon sukseso!
+    critical_update_pending: Aktualizasyon kritika esta asperando
     custom_emojis:
       assign_category: Asinyar kategoria
       by_domain: Domeno
@@ -280,7 +331,63 @@ lad:
         title: Adjustar muevo emoji personalizado
       no_emoji_selected: No se troko dingun emoji porke no eskojites dinguno
       not_permitted: No tienes permiso para realizar esta aksyon
+      overwrite: Sobreskrive
+      shortcode: Kodiche kurto
+      title: Emojis personalizados
+      uncategorized: No kategorizado
+      unlist: No lista
+      unlisted: No listado
+      update_failed_msg: No se pudo aktualizar akel emoji
+      updated_msg: Emoji aktualizado kon sukseso!
+      upload: Karga
+    dashboard:
+      active_users: utilizadores aktivos
+      interactions: enteraksyones
+      media_storage: Magazinaje de multimedia
+      new_users: muevos utilizadores
+      opened_reports: raportos aviertos
+      pending_appeals_html:
+        one: "<strong>%{count}</strong> apelasyon esta asperando"
+        other: "<strong>%{count}</strong> apelasiones estan asperando"
+      pending_reports_html:
+        one: "<strong>%{count}</strong> raporto esta asperando"
+        other: "<strong>%{count}</strong> raportos estan asperando"
+      pending_tags_html:
+        one: "<strong>%{count}</strong> etiketa esta asperando"
+        other: "<strong>%{count}</strong> etiketas estan asperando"
+      pending_users_html:
+        one: "<strong>%{count}</strong> utilizador esta asperando"
+        other: "<strong>%{count}</strong> utilizadores estan asperando"
+      resolved_reports: raportos rezolvidos
+      software: Programario
+      sources: Manaderos de rejistro
+      space: Uzo de magazinaje
+      title: Pano
+      top_languages: Linguas mas aktivas
+      top_servers: Sirvidores mas aktivos
+      website: Sitio internetiko
+    disputes:
+      appeals:
+        empty: No se toparon apelasiones.
+        title: Apelasiones
+    domain_allows:
+      add_new: Perimite federasyon kon el domeno
+      created_msg: Federasyon kon el domeno permitida kon sukseso
+      destroyed_msg: Federasyon kon el domeno proibida kon sukseso
+      export: Eksporto
+      import: Importo
+      undo: Proibe federasyon kon el domeno
     domain_blocks:
+      add_new: Adjusta muevo bloko de domeno
+      confirm_suspension:
+        cancel: Anula
+        confirm: Suspende
+        title: Konfirma bloko de domeno para %{domain}
+      created_msg: El bloko de domeno esta siendo prosesado
+      destroyed_msg: El bloko de domeno se dezizo
+      domain: Domeno
+      edit: Edita muevo bloko de domeno
+      existing_domain_block: Ya impusites limitos mas estriktos a %{name}.
       existing_domain_block_html: Ya tienes forsado limitos mas estriktos a %{name}, kale <a href="%{unblock_url}">dezblokarlo primero</a>.
       export: Eksporto
       import: Importo
@@ -345,7 +452,69 @@ lad:
     follow_recommendations:
       description_html: "<strong>Las rekomendasyones de kuentos ayudan a los muevos utilizadores a topar presto kontenido enteresante</strong>. Kuando un utilizador no tiene enteraktuado kon otros lo sufisiente komo para djenerar rekomendasyones personalizadas de kuentos a las ke segir, en sus lugar se le rekomiendan estes kuentos. Se rekalkulan diariamente a partir de una mikstura de kuentos kon el mayor numero de enteraksyones rezientes i kon el mayor numero de suivantes lokales kon una lingua determinada."
       language: Para la lingua
+      status: Estado
+      suppress: Inkapasita rekomendasyon de kuentos
+      suppressed: Inkapasitada
+      title: Rekomendasyones de kuentos
+      unsuppress: Restora rekomendasyones de kuentos
     instances:
+      availability:
+        description_html:
+          one: Si el embio al domeno no reushe <strong>%{count} diya</strong>, no se aran mas provas de entrega a manko ke se risiva un embio <em>dizde</em> el domeno.
+          other: Si el embio al domeno no reushe <strong>%{count} diyas desferentes</strong>, no se aprovaran a entregar a manko ke se risiva un embio <em>dizde</em> el domeno.
+        failure_threshold_reached: Limito de provas no reushidas alkansado el %{date}.
+        failures_recorded:
+          one: Prova no reushida en %{count} diya.
+          other: Provas no reushidas en %{count} diyas desferentes.
+        no_failures_recorded: No ay provas no reushidas en el defter.
+        title: Disponivilita
+        warning: La ultima prova de koneksyon a este sirvidor no tuvo sukseso
+      back_to_all: Todos
+      back_to_limited: Limitados
+      back_to_warning: Avertensya
+      by_domain: Domeno
+      confirm_purge: Siguro ke keres supremir permanentemente los datos de este domeno?
+      content_policies:
+        comment: Nota interna
+        description_html: Puedes definir politikas de kontenido ke se aplikaran a todos los kuentos de este domeno i a kualkiera de sus subdomenos.
+        limited_federation_mode_description_html: Puedes eskojer si keres permitir federasyon kon este domeno.
+        policies:
+          reject_media: Refuza multimedia
+          reject_reports: Refuza raportos
+          silence: Limita
+          suspend: Suspende
+        policy: Politika
+        reason: Razon publika
+        title: Politikas de kontenido
+      dashboard:
+        instance_accounts_dimension: Kuentos mas segidos
+        instance_accounts_measure: kuentos magazinados
+        instance_followers_measure: muestros suivantes ayi
+        instance_follows_measure: sus suivantes aki
+        instance_languages_dimension: Linguas popularas
+        instance_media_attachments_measure: dosyas adjuntas guadradas
+        instance_reports_measure: raportos sovre eyos
+        instance_statuses_measure: mesajes magazinados
+      delivery:
+        all: Todos
+        clear: Alimpiar yerros de entrega
+        failing: Fayando
+        restart: Reinisyar entrega
+        stop: Detener entrega
+        unavailable: No desponivle
+      delivery_available: Entrega desponivle
+      delivery_error_days: Diyas de yerro de entrega
+      delivery_error_hint: Si la entrega no es posivle a lo longo de %{count} diyas, se markara otomatikamente komo no entregable.
+      destroyed_msg: Los datos de %{domain} estan agora en kola para sus iminente efasasyon.
+      empty: No se toparon domenos.
+      known_accounts:
+        one: "%{count} kuento konesido"
+        other: "%{count} kuentos konesidos"
+      moderation:
+        all: Todos
+        limited: Limitado
+        title: Moderasyon
+      private_comment: Komento privado
       public_comment: Komento publiko
       purge: Purga
       purge_description_html: Si kreyes ke este domeno esta deskonektado, puedes efasar todos los rejistros de kuentos i los datos asosyados de este domeno de tu magazinaje. Esto puede levar un tiempo.
@@ -528,8 +697,47 @@ lad:
         manage_rules: Administra reglas
         manage_rules_description: Permete a los utilizadores trokar las reglas del sirvidor
         manage_settings: Administra konfigurasyon
+        manage_settings_description: Permete a los utilizadores trokar la konfigurasyon del sitio
+        manage_taxonomies: Administra etiketas
+        manage_taxonomies_description: Permete a los utilizadores revizar el kontenido en trend i aktualizar la konfigurasyon de las etiketas
+        manage_user_access: Administra akseso de utilizadores
+        manage_user_access_description: Permete a los utilizadores dezaktivar la autentifikasyon en dos pasos de otros utilizadores, trokar sus adreso de posta elektronika i restableser sus kod
+        manage_users: Administra utilizadores
+        manage_users_description: Permete a los utilizadores ver los peratim de otros utilizadores i realizar aksyones de moderasyon kontra eyos
+        manage_webhooks: Administrar webhooks
+        view_dashboard: Ve pano
+        view_devops: DevOps
+      title: Rolos
+    rules:
+      add_new: Adjusta regla
+      delete: Efasa
+      edit: Edita regla
+      title: Reglas del sirvidor
     settings:
+      about:
+        manage_rules: Administra reglas del sirvidor
+        title: Sovre esto
+      appearance:
+        preamble: Personaliza la enterfaz web de Mastodon.
+        title: Aparensya
+      branding:
+        title: Marka
+      content_retention:
+        title: Retensyon de kontenido
+      discovery:
+        follow_recommendations: Rekomendasyones de kuentos
+        profile_directory: Katalogo de profiles
+        public_timelines: Linyas de tiempo publikas
+        publish_discovered_servers: Publika sirvidores diskuviertos
+        publish_statistics: Publika estatistikas
+        title: Diskuvrimiento
+        trends: Trendes
+      domain_blocks:
+        all: A todos
+        disabled: A dinguno
+        users: Para los utilizadores lokales ke entrado en su kuento
       registrations:
+        preamble: Kontrola ken puede kriyar un kuento en tu sirvidor.
         title: Enrejistramientos
       registrations_mode:
         modes:
@@ -541,8 +749,13 @@ lad:
       delete: Efasa dosya kargada
       destroyed_msg: Dosya supremida kon sukseso!
     software_updates:
+      critical_update: Kritiko – por favor aktualiza pishin
       documentation_link: Ambezate mas
+      release_notes: Notas sovre la versyon
+      title: Aktualizasyones desponivles
       type: Tipo
+      types:
+        major: Versyon prinsipala
       version: Versyon
     statuses:
       account: Autor
@@ -579,7 +792,7 @@ lad:
         silence: "%{name} limito el kuento de %{target}"
         suspend: "%{name} suspendio el kuento de %{target}"
       appeal_approved: Apelado
-      appeal_pending: Apelasyon pendiente
+      appeal_pending: Apelasyon asperando
       appeal_rejected: Apelasyon refuzada
     system_checks:
       database_schema_check:
@@ -598,6 +811,10 @@ lad:
         message_html: No tienes definido dinguna regla del sirvidor.
       sidekiq_process_check:
         message_html: No ay dingun prosedura Sidekiq en egzekusion para la(s) kola(s) %{value}. Por favor, reviza tu konfigurasyon de Sidekiq
+      software_version_critical_check:
+        action: Amostra aktualizasyones desponivles
+      software_version_patch_check:
+        action: Amostra aktualizasyones desponivles
       upload_check_privacy_error:
         message_html: "<strong>Tu sirvidor de web es mal konfigurado. La privasita de tus utilizadores esta en riziko.</strong>"
       upload_check_privacy_error_object_storage:
@@ -620,3 +837,845 @@ lad:
         no_link_selected: No se troko dingun atadijo porke no eskojites dinguno
         publishers:
           no_publisher_selected: No se troko dingun publikador porke no eskojites dinguno
+        title: Atadijos en trend
+        usage_comparison: Partajado %{today} vezes oy, komparado kon %{yesterday} ayer
+      not_allowed_to_trend: Sin permiso para estar en trendes
+      only_allowed: Solo las permetidas
+      pending_review: Revizion esta asperando
+      preview_card_providers:
+        allowed: Los atadijos de este medio pueden ser trend
+        description_html: Estos son domenos dizde los ke los atadijos akoruto se partajan en sus sirvidor. Los atadijos no seran trend publikamente a menos ke se achete el domeno del atadijo. Sus achetasion (o refuzo) se ekstende a los subdomenos.
+        rejected: Los atadijos de este medio no pueden ser trend
+        title: Medios
+      rejected: Refuzadas
+      statuses:
+        allow: Permete publikasyon
+        allow_account: Permete al autor
+        description_html: Estas son publikasyones ke tu sirvidor konese ke estan siendo partajadas muncho i plazen a munchas personas en este momento. Puedes ayudar a tus utilizadores muevos i retornantes a topar mas djente a la ke segir. No ay mesajes ke se amostren publikamente asta ke aproves el autor i el autor permeta ke su kuento sea sugjerado a otros. Tamyen puedes permeter o refuzar mesajes individuales.
+        disallow: No permete publikasyon
+        disallow_account: No permete al autor
+        no_status_selected: No se troko dinguna publikasyon en trend porke no seleksionates dingunas
+        not_discoverable: El autor no tiene optado por ser detektable
+        shared_by:
+          one: Repartajado por o plaze a alguno una vez
+          other: Repartajado por o plaze a alguno %{friendly_count} vezes
+        title: Publikasyones en trend
+      tags:
+        current_score: Puntuasion aktuala %{score}
+        dashboard:
+          tag_accounts_measure: uzos unikos
+          tag_languages_dimension: Linguas popularas
+          tag_servers_dimension: Sirvidores prinsipales
+          tag_servers_measure: desferentes sirvidores
+          tag_uses_measure: uzos totales
+        description_html: Estas son etiketas ke estan aparesiendo en munchas publikasyones ke tu sirvidor ve. Pueden ayudar a tus utilizadores a averiguar de ke avla mas djente en estos momentos. No ay etiketas ke se amostren publikamente asta ke las achetes.
+        listable: Pueden ser rekomendadas
+        no_tag_selected: No se troko dinguna etiketa al no eskojer dinguna
+        not_listable: No seran rekomendadas
+        not_trendable: No aperesera en trendes
+        not_usable: No se pueden uzar
+        title: Etiketas en trend
+        trendable: Pueden apareser en trendes
+        trending_rank: Trend n.º %{rank}
+        usable: Pueden uzarse
+        usage_comparison: Uzada %{today} vezes oy, komparado kon %{yesterday} ayer
+        used_by_over_week:
+          one: Uzada por una persona durante la ultima semana
+          other: Uzada por %{count} personas durante la ultima semana
+      title: Trendes
+      trending: En trend
+    warning_presets:
+      add_new: Adjusta muevo
+      delete: Efasa
+      edit_preset: Edita avizo predeterminado
+      empty: Ainda no tienes definido ningun avizo predeterminado.
+      title: Edita konfigurasyon predeterminada de avizos
+    webhooks:
+      add_new: Adjusta endpoint
+      delete: Efasa
+      description_html: Un <strong>webhook</strong> permete a Mastodon embiar <strong>avizos en tiempo real</strong> sovre los evenimientos eskojidos a tu propia aplikasyon, para ke tu aplikasyon pueda <strong>lanzar reaksyones otomatikamente</strong>.
+      disable: Inkapasita
+      disabled: Inkapasitado
+      edit: Edita endpoint
+      empty: Ainda no tienes ningun endpoint de webhook konfigurado.
+      enable: Kapasita
+      enabled: Aktivo
+      enabled_events:
+        one: 1 evenimiento kapasitado
+        other: "%{count} evenimientos kapasitados"
+      events: Evenimientos
+      new: Muevo webhook
+      rotate_secret: Rotar sekreto
+      secret: Firmando sekreto
+      status: Estado
+      title: Webhooks
+      webhook: Webhook
+  admin_mailer:
+    new_appeal:
+      actions:
+        delete_statuses: para supremir sus mesajes
+        disable: para konjelar su kuento
+        mark_statuses_as_sensitive: para markar sus mesajes komo sensivles
+        none: una avertensya
+        sensitive: para markar su kuento komo sensivle
+        silence: para limitar su kuento
+        suspend: para suspender su kuento
+      body: "%{target} esta apelando a una solisitasyon de moderasyon de %{action_taken_by} el %{date}, del tipo %{type}. Eyos eskrivieron:"
+      next_steps: Puedes achetar la apelasyon para dezazer la dechizyon de moderasyon, o ignorarla.
+      subject: "%{username} esta apelando a una dechizyon de moderasyon en %{instance}"
+    new_critical_software_updates:
+      subject: Ay aktualizasyones kritikas de Mastodon desponivles para %{instance}!
+    new_pending_account:
+      body: Los peratim del muevo kuento estan abashos. Puedes achetar o refuzar esta aplikasyon.
+      subject: Muevo kuento para revizion en %{instance} (%{username})
+    new_report:
+      body: "%{reporter} tiene raportado a %{target}"
+      body_remote: Alguno de %{domain} a raportado a %{target}
+      subject: Muevo raporto para la %{instance} (#%{id})
+    new_software_updates:
+      body: Ay mueva versyon de Mastodon, kizas keras aktualizar!
+      subject: Ay muevas versyones de Mastodon desponivles para %{instance}!
+    new_trends:
+      body: 'Los sigientes elementos nesesitan una revizion antes de ke se puedan amostrar publikamente:'
+      new_trending_links:
+        title: Atadijos en trend
+      new_trending_statuses:
+        title: Publikasyones en trend
+      new_trending_tags:
+        title: Etiketas en trend
+  aliases:
+    add_new: Kriya un alias
+    empty: No tienes aliases.
+    remove: Dezata alias
+  appearance:
+    advanced_web_interface: Enterfaz web avanzada
+    animations_and_accessibility: Animasyones i aksesivilita
+    confirmation_dialogs: Dialogos de konfirmasyon
+    discovery: Diskuvrimiento
+    localization:
+      guide_link_text: Todos pueden kontribuir.
+    sensitive_content: Kontenido sensivle
+  application_mailer:
+    notification_preferences: Troka preferensyas de posta
+    salutation: "%{name},"
+    settings: 'Troka preferensyas de posta: %{link}'
+    unsubscribe: Dezabona
+    view: 'Mira:'
+    view_profile: Ve profil
+    view_status: Ve publikasyon
+  applications:
+    created: Aplikasyon kriyada kon sukseso
+    destroyed: Aplikasyon supremida kon sukseso
+    logout: Sal
+    regenerate_token: Redjenera token de akseso
+    token_regenerated: Token de akseso redjenerado kon sukseso
+    warning: Ten muncho kudiado kon estos datos. No los partajes kon dinguno!
+    your_token: Tu token de akseso
+  auth:
+    apply_for_account: Solisita un kuento
+    captcha_confirmation:
+      title: Kontrolo de sigurita
+    confirmations:
+      awaiting_review_title: Estamos revizando tu enrejistramiento
+      clicking_this_link: klikando en este atadijo
+      login_link: konektate kon kuento
+      proceed_to_login_html: Agora puedes ir a %{login_link}.
+      welcome_title: Bienvenido, %{name}!
+      wrong_email_hint: Si este adreso de posta es inkorekto, puedes trokarlo en las preferensyas del kuento.
+    delete_account: Efasa kuento
+    delete_account_html: Si keres supremir tu kuento, puedes <a href="%{path}">ir aki</a>. Seras pedido de una konfirmasyon.
+    description:
+      prefix_invited_by_user: "@%{name} te envita a unirte a este sirvidor de Mastodon!"
+      prefix_sign_up: Adjuntate a Mastodon oy!
+      suffix: Kon un kuento podras segir a djente, publikar haberes e enterkambiar mesajes kon utilizadores de kualkier sirvidor de Mastodon i mas!
+    didnt_get_confirmation: No risivites el atadijo de konfirmasyon?
+    dont_have_your_security_key: No tienes tu yave de sigurita?
+    forgot_password: Neglijates tu kod?
+    invalid_reset_password_token: El token de reinisyo de kod es malato o ekspiro. Por favor pide uno muevo.
+    link_to_otp: Introduse un kodiche de autorizasyon en dos pasos dizde tu telefon o un kodiche de rekuperasyon
+    link_to_webauth: Utiliza tu aparato de yave de sigurita
+    log_in_with: Konektate kon kuento kon
+    login: Konektate kon kuento
+    logout: Sal
+    migrate_account: Transferate a otro kuento
+    migrate_account_html: Si keres readresar este kuento a otra distinta, puedes <a href="%{path}">konfigurarlo aki</a>.
+    or_log_in_with: O konektate kon tu kuento kon
+    privacy_policy_agreement_html: Tengo meldado i acheto la <a href="%{privacy_policy_path}" target="_blank">politika de privasita</a>
+    progress:
+      confirm: Konfirma posta
+      details: Tus detalyos
+      review: Muestra revizyon
+      rules: Acheta reglas
+    providers:
+      cas: CAS
+      saml: SAML
+    register: Enrejistrate
+    registration_closed: "%{instance} no esta achetando a muevos miembros"
+    resend_confirmation: Reembia posta de konfirmasyon
+    reset_password: Reinisya kod
+    rules:
+      accept: Acheta
+      back: Atras
+      preamble: Estas son establesidas i aplikadas por los moderadores de %{domain}.
+      title: Algunas reglas bazikas.
+      title_invited: Fuites envitado.
+    security: Sigurita
+    set_new_password: Establese muevo kod
+    setup:
+      email_below_hint_html: Mira en tu kuti de spam o solisita de muevo. Si el adreso de posta elektronika ke aparese aki es yerrado, puedes trokarlo aki.
+      link_not_received: No risivites un atadijo?
+    sign_in:
+      preamble_html: Konektate kon tus kredensiales de <strong>%{domain}</strong>. Si tu kuento esta balabayado en otruno servidor, no puedras konektarte aki.
+      title: Konektate kon %{domain}
+    sign_up:
+      preamble: Kon un kuento en este sirvidor de Mastodon, podras segir a kualkier otra persona en la red, endependientemente del sirvidor en el ke se tope.
+      title: Kriya kuento de Mastodon en %{domain}.
+    status:
+      account_status: Estado del kuento
+      confirming: Bekleando konfirmasyon de posta elektronika.
+      functional: Tu kuento esta kompletamente funksyonal.
+      pending: Tu solisitasyon esta asperando la revizion por muestros administradores. Esto puede tadrar algun tiempo. Arisiviras una posta elektronika si la solisitasyon sea achetada.
+      redirecting_to: Tu kuento se topa inaktivo porke esta siendo readresado a %{acct}.
+      view_strikes: Ve amonestamientos pasados kontra tu kuento
+    too_fast: Formulario enviado demaziado rapido, aprovalo de muevo.
+    use_security_key: Uza la yave de sigurita
+  challenge:
+    confirm: Kontinua
+    hint_html: "<strong>Tip:</strong> No retornaremos a demandarte por el kod durante la sigiente ora."
+    invalid_password: Kod inkorekto
+    prompt: Konfirma kod para segir
+  crypto:
+    errors:
+      invalid_key: no es una yave Ed25519 o Curve25519 valida
+      invalid_signature: no es una firma Ed25519 valida
+  date:
+    formats:
+      default: "%d %b %Y"
+      with_month_name: "%d %B %Y"
+  datetime:
+    distance_in_words:
+      about_x_hours: "%{count} o"
+      about_x_months: "%{count} me"
+      about_x_years: "%{count} a"
+      almost_x_years: "%{count} a"
+      half_a_minute: Agora
+      less_than_x_minutes: "%{count} m"
+      less_than_x_seconds: Agora
+      over_x_years: "%{count} a"
+      x_days: "%{count} d"
+      x_minutes: "%{count} m"
+      x_months: "%{count} me"
+      x_seconds: "%{count} s"
+  deletes:
+    proceed: Efasa kuento
+    success_msg: Tu kuento fue efasado kon reusho
+    warning:
+      before: 'Antes de kontinuar, por favor melda kon atensyon las sigientes notas:'
+      username_unavailable: Tu nombre de utilizador no estara desponivle
+  disputes:
+    strikes:
+      action_taken: Aksyon tomada
+      appeal: Apela
+      appeal_rejected: La apelasyon fue refuzada
+      appeal_submitted_at: Apelasyon embiada
+      appealed_msg: Tu apelasyon fue embiada. Si la achetamos, se te avizara.
+      appeals:
+        submit: Embia apelasyon
+      approve_appeal: Acheta apelasyon
+      associated_report: Raporto asosiado
+      created_at: Kon data
+      description_html: Estas son las aksyones emprendidas kontra tu kuento i las avertensyas ke te an sido enviadas por la taifa de %{instance}.
+      recipient: Dirijda a
+      reject_appeal: Refuza apelasyon
+      status: 'Publikasyon #%{id}'
+      status_removed: Publikasyon ya supremida del sistem
+      title: "%{action} del %{date}"
+      title_actions:
+        delete_statuses: Efasasyon de publikasyon
+        disable: Konjelasyon del kuento
+        mark_statuses_as_sensitive: Markamiento de los mesajes komo sensivles
+        none: Avertensya
+        sensitive: Markamiento del kuento komo sensivle
+        silence: Limitasyon de kuento
+        suspend: Suspensyon de kuento
+      your_appeal_approved: Tu apelasyon fue achetada
+      your_appeal_pending: Tienes enviado una apelasyon
+      your_appeal_rejected: Tu apelasyon fue refuzada
+  domain_validator:
+    invalid_domain: no es un nombre de domeno valido
+  edit_profile:
+    basic_information: Enformasyon bazika
+    other: Otros
+  errors:
+    '400': La solisitasyon ke enviates no fue valida o fue malformada.
+    '403': No tienes permiso para ver esta pajina.
+    '404': La pajina ke bushkas no egziste.
+    '406': Esta pajina no esta desponivle en el formato solisitado.
+    '410': La pajina ke estavas bushkando no egziste mas.
+    '422':
+      content: Verifikasyon de sigurita no reushida. Estas blokando algunas cookies?
+      title: Verifikasyon de sigurita no reushida
+    '429': Demaziadas solisitasyones
+    '500':
+      content: Pardon, algo tiene fonksionado mal por muestra parte.
+      title: Esta pajina no es djusta
+    '503': La pajina no se tiene podido desbarkar por un yerro temporal del sirvidor.
+    noscript_html: Para uzar la aplikasyon web de Mastodon, por favor aktiva Javascript. Alternativamente, aprova alguna de las <a href="%{apps_path}">aplikasyones nativas</a> para Mastodon para tu platforma.
+  existing_username_validator:
+    not_found: no se pudo topar un utilizador lokal kon akel nombre de utilizador
+    not_found_multiple: no se pudo topar %{usernames}
+  exports:
+    archive_takeout:
+      date: Data
+      download: Abasha tu dosya
+      hint_html: Puedes solisitar una dosya de tus <strong>publikasyones i dosyas multimedia kargadas</strong>. Los datos eksportados estaran en formato ActivityPub, meldavles por kualkier programario kompativle. Puedes solisitar una dosya kada 7 diyas.
+      in_progress: Rekopilando tu dosya...
+      request: Solisita tu dosya
+      size: Boy
+    blocks: Personas a las kualas tienes blokado
+    bookmarks: Markadores
+    csv: CSV
+    domain_blocks: Blokos de domenos
+    lists: Listas
+    mutes: Silensias
+    storage: Magazinaje de multimedia
+  featured_tags:
+    add_new: Adjusta muevo
+    errors:
+      limit: Tienes alkansado el karar maksimo de etiketas
+  filters:
+    contexts:
+      account: Profiles
+      home: Prinsipyo i listas
+      notifications: Avizos
+      public: Linyas de tiempo publikas
+      thread: Konversasyones
+    edit:
+      add_keyword: Adjusta biervo yave
+      keywords: Biervos yaves
+      statuses: Publikasyones individualas
+      title: Edita filtro
+    index:
+      contexts: Filtros en %{contexts}
+      delete: Efasa
+      empty: No tyenes filtros.
+      expires_in: Kaduka en %{distance}
+      expires_on: Kaduka en %{date}
+      keywords:
+        one: "%{count} biervo yave"
+        other: "%{count} biervos yave"
+      statuses:
+        one: "%{count} publikasyon"
+        other: "%{count} publikasyones"
+      statuses_long:
+        one: "%{count} publikasyon individuala eskondida"
+        other: "%{count} publikasyones individualas eskondidas"
+      title: Filtros
+    new:
+      save: Guadra muevo filtro
+      title: Adjusta muevo filtro
+    statuses:
+      back_to_filter: Retorna al filtro
+      batch:
+        remove: Kita del filtro
+      index:
+        title: Publikasyones filtradas
+  generic:
+    all: Todos
+    cancel: Anula
+    changes_saved_msg: Trokamientos guadrados kon reusho!
+    confirm: Konfirma
+    copy: Kopia
+    delete: Efasa
+    deselect: Deseleksyonar todo
+    none: Dinguno
+    order_by: Ordena por
+    save_changes: Guadra trokamientos
+    today: oy
+  imports:
+    errors:
+      empty: Dosya CSV vaziya
+      invalid_csv_file: 'Dosya CSV no valida. Yerro: %{error}'
+      over_rows_processing_limit: kontiene mas de %{count} filas
+      too_large: Dosya es mas grande
+    imported: Importado
+    modes:
+      merge: Une
+      merge_long: Manten rejistros egzistentes i adjusta muevos
+      overwrite: Sobreskrive
+      overwrite_long: Mete muevos rejistros en vez de los aktuales
+    preface: Puedes importar siertos datos, komo todas las personas a las kualas estas sigiendo o blokando en tu kuento en esta instansya, dizde dosyas eksportadas de otra instansya.
+    recent_imports: Importasyones resyentes
+    states:
+      finished: Finalizado
+      scheduled: Programado
+      unconfirmed: Sin konfirmasyon
+    status: Estado
+    success: Tus datos se tienen kargado djustamente i seran prosesados pishin
+    time_started: Ampesado el
+    titles:
+      blocking: Importando kuentos blokados
+      bookmarks: Importando markadores
+      domain_blocking: Importando domenos blokados
+      following: Importando kuentos segidos
+      lists: Importando listas
+      muting: Importando kuentos silensyados
+    type: Tipo de importasyon
+    type_groups:
+      constructive: Segidores i markadores
+      destructive: Blokos i silensyos
+    types:
+      blocking: Lista de blokos
+      bookmarks: Markadores
+      domain_blocking: Lista de domenos blokados
+      following: Lista de segidos
+      lists: Listas
+      muting: Lista de silensyados
+    upload: Karga
+  invites:
+    delete: Dezaktiva
+    expired: Kadukado
+    expires_in:
+      '1800': 30 minutos
+      '21600': 6 oras
+      '3600': 1 ora
+      '43200': 12 oras
+      '604800': 1 semana
+      '86400': 1 diya
+    expires_in_prompt: Nunkua
+    generate: Djenera un atadijo de envitasyon
+    invalid: Esta envitasyon no es valida
+    invited_by: 'Fuites envitado por:'
+    max_uses:
+      one: 1 uzo
+      other: "%{count} uzos"
+    max_uses_prompt: Sin limito
+    table:
+      expires_at: Kaduka
+      uses: Uzos
+    title: Envita a djente
+  lists:
+    errors:
+      limit: Tienes alkansado el karar maksimo de listas
+  login_activities:
+    authentication_methods:
+      otp: aplikasyon de autentifikasyon en dos pasos
+      password: kod
+      sign_in_token: kodiche de sigurita por posta elektronika
+      webauthn: yaves de sigurita
+    description_html: Si ves una aktivita ke no rekoneses, konsidera trokar tu kod i kapasitar la autentifikasyon en dos pasos.
+    empty: No ay estoria de autentifikasyon desponivle
+    failed_sign_in_html: Prova de inisiasyon de sesion no reushida kon %{method} de %{ip} (%{browser})
+    successful_sign_in_html: Prova de sesion reushida kon %{method} dizde %{ip} (%{browser})
+    title: Estoria de autentifikasyon
+  mail_subscriptions:
+    unsubscribe:
+      action: Si, dezabona
+      complete: Dezabonado
+      emails:
+        notification_emails:
+          favourite: avizos de favoritos por posta
+          follow: avizos de segidores por posta
+          follow_request: avizos de solisitasyones de segimyento por posta
+          mention: avizos de enmentaduras por posta
+          reblog: avizos de repartajasyones por posta
+      title: Dezabona
+  media_attachments:
+    validations:
+      images_and_video: No se puede adjuntar un video a un estado ke ya kontenga imajes
+      not_ready: No se pueden adjuntar dosyas ke no se tienen eskapado de prosesar. Aprovalo de muevo en un momento!
+      too_many: No se pueden adjuntar mas de 4 dosyas
+  migrations:
+    acct: Migrado a
+    cancel: Anula readreso
+    cancel_explanation: Al anular el readreso se reaktivara tu kuento aktual, ama no rekuperaras los suivantes ke ayan sido trasladados al otro kuento.
+    cancelled_msg: Tienes anulado el readreso djustamente.
+    errors:
+      already_moved: es el mezmo kuento al ke ya tienes migrado
+      missing_also_known_as: no es un alias de este kuento
+      move_to_self: no puede ser el kuento aktual
+      not_found: no se pudo topar
+      on_cooldown: Estas en tiempo de reutilizasyon
+    followers_count: Suivantes al momento de migrar
+    incoming_migrations: Migra de un kuento desferente
+    incoming_migrations_html: Para migrar de otro kuento a este, primero kale <a href="%{path}">kriyar un alias del kuento</a>.
+    moved_msg: Tu kuento agora se esta redirigiendo a %{acct} i tus suivantes se estan migrando.
+    not_redirecting: Tu kuento no se esta readresando a dinguna otro kuento aktualmente.
+    on_cooldown: Tienes migrado tu kuento rezientemente. Esta fonksyon estara desponivle de muevo en %{count} diyas.
+    past_migrations: Migrasyones pasadas
+    proceed_with_move: Migra suivantes
+    redirected_msg: Tu kuento agora readresa a %{acct}.
+    redirecting_to: Tu kuento se esta readresando a %{acct}.
+    set_redirect: Establese readreso
+    warning:
+      backreference_required: El muevo kuento deve ser konfigurado primero para fazer referensya a este
+      before: 'Antes de kontinuar, por favor melda kon atensyon las sigientes notas:'
+      cooldown: Dempues de migrar ay un periodo de aspera durante el kual no podras retornar a migrar
+      disabled_account: Tu kuento aktual no sera kompletamente utilizable dempues. Portanto, tendras akseso a la eksportasyon de datos ansi komo a la reaktivasyon.
+      followers: Esta aksion migrara a todos los suivantes del kuento aktual al muevo kuento
+      only_redirect_html: Alternativamente, solo puedes <a href="%{path}">poner un readreso en tu profil</a>.
+  moderation:
+    title: Moderasyon
+  notification_mailer:
+    admin:
+      report:
+        subject: "%{name} embio un raporto"
+      sign_up:
+        subject: "%{name} se enrejistro"
+    favourite:
+      body: 'Tu publikasyon fue favoritada por %{name}:'
+      subject: A %{name} le plaze tu publikasyon
+      title: Muevo favorito
+    follow:
+      body: "%{name} te esta sigiendo!"
+      subject: "%{name} te esta sigiendo"
+      title: Muevo suivante
+    follow_request:
+      action: Administra solisitudes de segimiento
+      body: "%{name} tiene solisitado segirte"
+      subject: 'Segidor esta asperando: %{name}'
+      title: Mueva solisitud de segimiento
+    mention:
+      action: Arisponde
+      body: 'Fuites enmentado por %{name} en:'
+      subject: Fuites enmentado por %{name}
+      title: Mueva enmentadura
+    poll:
+      subject: Una anketa de %{name} eskapo
+    reblog:
+      body: 'Tu publikasyon fue repartajada por %{name}:'
+      subject: "%{name} repartajo tu publikasyon"
+      title: Mueva repartajasyon
+    status:
+      subject: "%{name} publiko algo"
+    update:
+      subject: "%{name} edito una publikasyon"
+  notifications:
+    administration_emails: Avizos de administrasyon por posta
+    email_events: Evenimyentos para avizos por posta
+    other_settings: Otras preferensyas de avizos
+  number:
+    human:
+      decimal_units:
+        format: "%n%u"
+        units:
+          billion: MM
+          million: M
+          quadrillion: Ku
+          thousand: K
+          trillion: T
+  otp_authentication:
+    enable: Kapasita
+    instructions_html: "<strong>Eskanea este kodiche QR dizde Google Authenticator o una aplikasyon similar en tu telefon</strong>. A partir de agora, esta aplikasyon djenerara kodiches ke tendras ke ingresar kuando keras konektarte kon tu kuento."
+    manual_instructions: 'Si no puedes eskanear el kodiche QR i nesesitas introdusirlo manualmente, este es el sekreto en teksto plano:'
+    setup: Konfigura
+    wrong_code: El kodiche ingresado es malato! Es djusta la ora del aparato i el sirvidor?
+  pagination:
+    newer: Mas muevo
+    next: Sigiente
+    older: Mas viejo
+    prev: Anterior
+    truncate: "&hellip;"
+  polls:
+    errors:
+      already_voted: Ya tienes votado en esta anketa
+      duplicate_options: kontiene elementos duplikados
+      duration_too_long: esta demaziado leshos en el avenir
+      duration_too_short: es demaziado pronto
+      expired: La anketa ya tiene eskapado
+      invalid_choice: La opsyon de voto eskojida no egziste
+      over_character_limit: no puede trespasar %{max} karakteres kada uno
+      self_vote: No puedes votar en tus propias anketas
+      too_few_options: deve tener mas de un elemento
+      too_many_options: no puede kontener mas de %{max} elementos
+  preferences:
+    other: Otros
+    posting_defaults: Konfigurasyon predeterminada de publikasyones
+    public_timelines: Linyas de tiempo publikas
+  privacy:
+    privacy: Privasita
+    search: Bushkeda
+  privacy_policy:
+    title: Politika de privasita
+  reactions:
+    errors:
+      limit_reached: Limito de reaksyones desferentes alkansado
+      unrecognized_emoji: no es un emoji konesido
+  relationships:
+    activity: Aktivita del kuento
+    confirm_follow_selected_followers: Estas siguro ke keres segir a los suivantes eskojidos?
+    confirm_remove_selected_followers: Estas siguro ke keres kitar a los suivantes eskojidos?
+    confirm_remove_selected_follows: Estas siguro ke keres kitar los kuentos segidos a los kualos tienes eskojido?
+    dormant: Inaktivo
+    follow_failure: No se pudo segir algunos de los kuentos eskojidos.
+    follow_selected_followers: Sige a los suivantes eskojidos
+    followers: Suivantes
+    following: Segidos
+    invited: Envitados
+    last_active: Ultima aktivita
+    most_recent: Mas reziente
+    moved: Migrados
+    mutual: Mutual
+    primary: Prinsipal
+    relationship: Relasyon
+    remove_selected_followers: Kita a los suivantes eskojidos
+    remove_selected_follows: Desige a los utilizadores eskojidos
+    status: Estado del kuento
+  rss:
+    content_warning: 'Avertensya de kontenido:'
+    descriptions:
+      account: Publikasyones publikas de @%{acct}
+      tag: 'Publikasyones publikas etiketadas kon #%{hashtag}'
+  self_destruct:
+    title: Este sirvidor esta serrando
+  sessions:
+    activity: Ultima aktivita
+    browser: Navigador
+    browsers:
+      alipay: Alipay
+      blackberry: BlackBerry
+      chrome: Chrome
+      edge: Microsoft Edge
+      electron: Electron
+      firefox: Firefox
+      generic: Navigador deskonosido
+      huawei_browser: Huawei Browser
+      ie: Internet Explorer
+      micro_messenger: MicroMessenger
+      nokia: Nokia S40 Ovi Browser
+      opera: Opera
+      otter: Otter
+      phantom_js: PhantomJS
+      qq: QQ Browser
+      safari: Safari
+      uc_browser: UC Browser
+      unknown_browser: Navigador deskonosido
+      weibo: Weibo
+    current_session: Sesyon aktuala
+    description: "%{browser} en %{platform}"
+    ip: IP
+    platforms:
+      adobe_air: Adobe Air
+      android: Android
+      blackberry: BlackBerry
+      chrome_os: ChromeOS
+      firefox_os: Firefox OS
+      ios: iOS
+      kai_os: KaiOS
+      linux: Linux
+      mac: macOS
+      unknown_platform: Platforma deskonesida
+      windows: Windows
+      windows_mobile: Windows Mobile
+      windows_phone: Windows Phone
+    revoke: Revoka
+    revoke_success: Sesion revokada kon sukseso
+    title: Sesiones
+    view_authentication_history: Ve estoria de autentifikasyon de tu kuento
+  settings:
+    account: Kuento
+    account_settings: Preferensyas de kuento
+    aliases: Aliases del kuento
+    appearance: Aparensya
+    authorized_apps: Aplikasyones autorizadas
+    back: Retorna a Mastodon
+    delete: Efasa kuento
+    development: Dezvelopamiento
+    edit_profile: Edita profil
+    export: Eksporta enformasyon
+    featured_tags: Etiketas avaliadas
+    import: Importo
+    import_and_export: Importo i eksporto
+    migrate: Migrasyon de kuento
+    notifications: Avizos
+    preferences: Preferensyas
+    profile: Profil publiko
+    relationships: Segidos i suivantes
+    statuses_cleanup: Efasasyon otomatika de publikasyones
+    strikes: Amonestamientos de moderasyon
+    two_factor_authentication: Autentifikasyon en dos pasos
+    webauthn_authentication: Yaves de sigurita
+  statuses:
+    attached:
+      audio:
+        one: "%{count} audio"
+        other: "%{count} audios"
+      description: 'Atamiento: %{attached}'
+      image:
+        one: "%{count} imaje"
+        other: "%{count} imajes"
+      video:
+        one: "%{count} video"
+        other: "%{count} videos"
+    boosted_from_html: Repartajado dizde %{acct_link}
+    content_warning: 'Avertensya de kontenido: %{warning}'
+    default_language: La mezma ke la lingua de la enterfaz
+    disallowed_hashtags:
+      one: 'kontenia una etiketa no permetida: %{tags}'
+      other: 'kontenia las etiketas no permetidas: %{tags}'
+    edited_at_html: Editado %{date}
+    errors:
+      in_reply_not_found: La publikasion a la ke aprovas arispondir no egziste.
+    open_in_web: Avre en web
+    over_character_limit: limito de karakteres de %{max} superado
+    pin_errors:
+      direct: Las publikasyones ke son vizivles solo para los utilizadores enmentados no pueden fiksarse
+      limit: Ya tienes fiksado el numero maksimo de publikasyones
+      ownership: La publikasyon de otra persona no puede fiksarse
+      reblog: No se puede fixar una repartajasyon
+    poll:
+      total_people:
+        one: "%{count} persona"
+        other: "%{count} personas"
+      total_votes:
+        one: "%{count} voto"
+        other: "%{count} votos"
+      vote: Vota
+    show_more: Amostra mas
+    show_newer: Amostra mas muevos
+    show_older: Amostra mas viejos
+    show_thread: Amostra diskusyon
+    title: '%{name}: "%{quote}"'
+    visibilities:
+      direct: Direkto
+      private: Solo suivantes
+      private_long: Solo amostra a tus segidores
+      public: Publiko
+      public_long: Todos pueden ver
+      unlisted: No listado
+  statuses_cleanup:
+    enabled: Otomatikamente efasa publikasyones viejas
+    exceptions: Eksepsiones
+    ignore_favs: Ignora favoritos
+    ignore_reblogs: Ignora repartajasyones
+    keep_direct: Manten enmentaduras privadas
+    keep_direct_hint: No efasa dingunos mesajes privados
+    keep_media: Manten publikasyones kon atamientos
+    keep_media_hint: No efasa tus publikasyones kon atamientos de multimedia
+    keep_pinned: Manten publikasyones fiksadas
+    keep_pinned_hint: No efasa tus publikasyones fiksadas
+    keep_polls: Manten anketas
+    keep_polls_hint: No efasa tus anketas
+    keep_self_bookmark: Manten publikasyones kon markadores
+    keep_self_bookmark_hint: No efasa tus publikasyones kon tus markadores
+    keep_self_fav: Manten publikasyones ke te plazen
+    keep_self_fav_hint: No efasa las tus publikasyones kualas markates komo favoritas
+    min_age:
+      '1209600': 2 semanas
+      '15778476': 6 mezes
+      '2629746': 1 mez
+      '31556952': 1 anyo
+      '5259492': 2 mezes
+      '604800': 1 semana
+      '63113904': 2 anyos
+      '7889238': 3 mezes
+    min_favs: Manten publikasyones favoritadas a lo manko
+    min_reblogs: Manten publikasyones repartajadas a lo manko
+  stream_entries:
+    sensitive_content: Kontenido sensivle
+  strikes:
+    errors:
+      too_late: Es demaziado tadre para apelar este amonestamiento
+  tags:
+    does_not_match_previous_name: no koensida kon el nombre anterior
+  themes:
+    contrast: Mastodon (alto kontraste)
+    default: Mastodon (eskuro)
+    mastodon-light: Mastodon (klaro)
+  time:
+    formats:
+      default: "%d de %b del %Y, %H:%M"
+      month: "%b %Y"
+      time: "%H:%M"
+      with_time_zone: "%d de %b del %Y, %H:%M %Z"
+  two_factor_authentication:
+    add: Adjusta
+    disable: Inkapasita autentifikasyon en dos pasos
+    disabled_success: Autentifikasyon de dos pasos inkapasitada djustamente
+    edit: Edita
+    enabled: Autentifikasyon de dos pasos esta kapasitada
+    enabled_success: Autentifikasyon de dos pasos kapasitada djustamente
+    generate_recovery_codes: Jenera kodiches de rekuperasyon
+    lost_recovery_codes: Los kodiches de rekuperasyon te permeten obtener akseso a tu kuento si piedres tu telefon. Si tienes pedrido tus kodiches de rekuperasyon, puedes redjenerarlos aki. Tus viejos kodiches de rekuperasyon se aran malatos.
+    methods: Metodos de autentifikasyon de dos pasos
+    otp: Aplikasyon de autentifikasyon
+    recovery_codes: Fazer kopias de sigurita de tus kodiches de rekuperasyon
+    recovery_codes_regenerated: Kodiches de rekupersayon redjenerados kon sukseso
+    recovery_instructions_html: Si piedres akseso a tu telefon, puedes uzar uno de los sigientes kodiches de rekuperasyon para obtener akseso a tu kuento. <strong>Mantenlos a salvo</strong>. Por enshemplo, puedes imprimirlos i guadrarlos kon otros dokumentos emportantes.
+    webauthn: Yaves de sigurita
+  user_mailer:
+    appeal_approved:
+      action: Va a tu kuento
+      explanation: La apelasyon del amonestamiento kontra tu kuento del %{strike_date} ke mandates el %{appeal_date} fue achetada. Tu kuento se topa de muevo en dobro estado.
+      subject: Tu apelasyon del %{date} fue achetada
+      title: Apelasyon achetada
+    appeal_rejected:
+      explanation: La apelasyon del amonestamiento kontra tu kuento del %{strike_date} ke mandates el %{appeal_date} fue refuzada.
+      subject: Tu apelasyon del %{date} fue refuzada
+      title: Apelasyon refuzada
+    backup_ready:
+      explanation: Tienes solisitado una kopia kompleta de tu kuento de Mastodon. Ya esta pronta para abashar!
+      subject: Tu dosya esta pronta para abashar
+      title: Abasha dosya
+    suspicious_sign_in:
+      change_password: troka tu kod
+      details: 'Aki estan los peratim de la koneksyon kon tu kuento:'
+      explanation: Topimos una koneksyon kon tu kuento dizde un muevo adreso IP.
+      further_actions_html: Si no fuites tu, te rekomendamos ke %{action} pishin i kapasites la autentifikasyon en dos pasos para mantener tu kuento siguro.
+      subject: Tu kuento fue aksedido dizde un muevo adreso IP
+      title: Una mueva koneksyon kon tu kuento
+    warning:
+      appeal: Embia una apelasyon
+      appeal_description: Si kreyes ke esto es un yerro, puedes embiar una apelasyon a la taifa de %{instance}.
+      categories:
+        spam: Spam
+        violation: El kontenido viola las sigientes reglas de la komunita
+      explanation:
+        delete_statuses: Se tiene determinado ke algunos de tus mesajes violan una o mas reglas de la komunita i por tanto fueron supremidos por los moderadores de %{instance}.
+        disable: Ya no puedes uzar tu kuento, ama tu profil i el resto de datos permanesen intactos. Puedes solisitar una kopia de sigurita de tus datos, trokar la konfigurasyon de tu kuento o supremirlo.
+        mark_statuses_as_sensitive: Algunas de tus publikasyones an sido markados komo sensivles por los moderadores de %{instance}. Esto sinyifika ke la djente tendra ke pulsar los dosyas multimedia en las publikasyones antes de ke se amostre una vista previa. Puedes markar los dosyas multimedia komo sensivles tu mezmo kuando publikes en el avenir.
+        sensitive: A partir de agora todas los dosyas multimedia ke subas seran markados komo sensivles i eskondidos tras una avertensya.
+      reason: 'Razon:'
+      statuses: 'Publikasyones relevantes:'
+      subject:
+        delete_statuses: Tus publikasyones en %{acct} tienen sido efasadas
+        disable: Tu kuento %{acct} tiene sido konjelado
+        mark_statuses_as_sensitive: Tus publikasyones en %{acct} tienen sido markadas komo sensivles
+        none: Avertensya para %{acct}
+        sensitive: Tus publikasyones en %{acct} seran markadas komo sensivles dizde agora
+        silence: Tu kuento %{acct} tiene sido limitado
+        suspend: Tu kuento %{acct} tiene sido suspendido
+      title:
+        delete_statuses: Publikasyones efasadas
+        disable: Kuento konjelado
+        mark_statuses_as_sensitive: Publikasyones markadas komo sensivles
+        none: Avertensya
+        sensitive: Kuento markado komo sensivle
+        silence: Kuento limitado
+        suspend: Kuento suspendido
+    welcome:
+      edit_profile_action: Konfigurasyon de profil
+      final_action: Ampesa a publikar
+      subject: Bienvenido a Mastodon
+      title: Bienvenido, %{name}!
+  users:
+    follow_limit_reached: No puedes segir a mas de %{limit} personas
+    invalid_otp_token: Kodiche de dos pasos no valido
+    signed_in_as: 'Konektado komo:'
+  verification:
+    verification: Verifikasyon
+    verified_links: Tus atadijos verifikados
+  webauthn_credentials:
+    add: Adjusta mueva yave de sigurita
+    create:
+      success: Tu yave de sigurita fue adjustada kon sukseso.
+    delete: Efasa
+    delete_confirmation: Estas siguro ke keres efasar esta yave de sigurita?
+    destroy:
+      success: Tu yave de sigurita fue efasada kon sukseso.
+    invalid_credential: Yave de sigurita no valida
+    nickname_hint: Introduska el sovrenombre de tu mueva yave de sigurita
+    not_enabled: Ainda no tienes aktivado WebAuthn
+    not_supported: Este navigador no soporta yaves de sigurita
+    otp_required: Para uzar yaves de sigurita, por favor kapasite primero la autentifikasyon de dos pasos.
+    registered_on: Enrejistrado el %{date}
diff --git a/config/locales/simple_form.ie.yml b/config/locales/simple_form.ie.yml
index ada775b862a..672ff9d0039 100644
--- a/config/locales/simple_form.ie.yml
+++ b/config/locales/simple_form.ie.yml
@@ -4,30 +4,77 @@ ie:
     hints:
       account:
         unlocked: Persones va posser sequer te sin petir aprobation. Desselecte si tu vole manualmen tractar petitiones de sequer e decider ca acceptar o rejecter nov sequitores.
+      account_warning_preset:
+        text: Tu posse usar posta-sintaxe, quam URLs, hashtags e mentiones
+        title: Ínobligatori. Ne visibil al recipiente
+      admin_account_action:
+        send_email_notification: Li usator va reciver un explication de ti quel evenit con su conto
+        text_html: Ínobligatori. Tu posse usar posta-sintaxe. Tu posse <a href="%{path}">adjunter preconfigurationes de avises</a> por sparar témpor
+        type_html: Selecte quo far con <strong>%{acct}</strong>
+        types:
+          disable: Prevente li usator de usar su conto, ma ne delete o cela su contenete.
+          none: Usa ti por inviar un admoniment al usator, sin initiante quelcunc altri action.
+          sensitive: Fortiar que omni medie-atachamentes de ti usator essaye marcat quam sensitiv.
+          silence: Preventer li usator de posser postar con public visibilitá, e celar tui postas e notificationes de gente qui ne seque ti. Ti clude omni raportes contra ti conto.
+          suspend: Preventer omni interaction de o a ti-ci conto e deleter su contenete. Reversibil ante 30 dies ha passat. Ti clude omni raportes contra ti conto.
+        warning_preset_id: Ínobligatori. Tu ancor posse adjunter customisat textu al fine del preconfiguration
       announcement:
         all_day: Si ti-ci es marcat, solmen li dates del periode de témpor va esser monstrat
         ends_at: Ínobligatori. Li proclamation va esser despublicat automaticmen ye ti-ci témpor
         scheduled_at: Lassar vacui por publicar li proclamation strax
         starts_at: Ínobligatori. In li casu que tui proclamation es ligat a un specific periode de témpor
         text: Tu posse usar posta-sintaxe. Ples considerar li spacie quel li proclamation va plenar sur li ecran del usator
+      appeal:
+        text: Tu posse apellar un admoniment solmen un vez
       defaults:
+        autofollow: Persones qui adherer per li invitation va sequer te automaticmen
+        avatar: PNG, GIF o JPEG. Admaxim %{size}. Li grandore va esser contraet a %{dimensions}px
+        bot: Dir a altres que li conto primarimen far automatic actiones e que fórsan null homan vigila it
+        context: Un o multiplic contextus u li filtre deve aplicar
         current_password: Por securital rasones, ples introducter li passa-parol del actual conto
+        current_username: Por confirmar, ples inmetter li usator-nómine del actual conto
+        digest: Misset solmen pos un long periode de ínactivitá e solmen si tu ha recivet quelcunc missages personal in tui inbuxe
+        email: On va misser te un email de confirmation
+        header: PNG, GIF o JPEG. Admaxim %{size}. Li grandore va esser contraet a %{dimensions}px
+        inbox_url: Copiar li URL del initial págine del relé quel tu vole usar
+        irreversible: Filtrat postas va desaparir ínreversibilmen, mem si li filtre es removet plu tard
+        locale: Li lingue del usator-interfacie, emails e notificationes push
         password: Usa adminim 8 carácteres
+        phrase: Va aplicar sin egarda a ca li lítteres in li posta o su contenete-avise es majuscules o minuscules
+        scopes: Queles API li aplication va esser permisset accesser. Si tu selecte un capabilitá del max alt nivelle, tu ne deve selecter li individualis.
+        setting_aggregate_reblogs: Ne monstrar nov boosts por postas queles ha esset recentmen boostat (afecta solmen boostas recivet futurimen)
+        setting_always_send_emails: Generalmen notificationes per email ne va esser misset quande tu activmen usa Mastodon
+        setting_default_sensitive: Sensitiv medie es customarimen celat e posse esser revelat con un clicca
+        setting_display_media_default: Celar medie marcat quam sensitiv
+        setting_display_media_hide_all: Sempre celar medie
+        setting_display_media_show_all: Sempre monstrar medie
       form_admin_settings:
         bootstrap_timeline_accounts: Ti-ci contos va esser pinglat al parte superiori del recomandationes por nov usatores.
+        site_contact_username: Qualmen li gente posse atinger te sur Mastodon.
         theme: Li dessine quel ínregistrat visitantes e nov usatores vide.
         timeline_preview: Ínregistrat visitantes va posser vider li max recent public postas disponibil che li servitor.
         trends_as_landing_page: Monstrar populari contenete a ínregistrat visitantes vice un description del servitor. Besona que tendenties es activisat.
       form_challenge:
         current_password: Tu nu intra un area secur
+      ip_block:
+        severities:
+          sign_up_block: Nov registrationes ne va esser possibil
+          sign_up_requires_approval: Nov registrationes va besonar tui aprobation
+      webhook:
+        events: Selecter evenimentes a misser
+        url: Ad u misser evenimentes
     labels:
       account:
         fields:
           value: Contenete
+        indexable: Includer public postas in resultates de sercha
       account_warning_preset:
+        text: Textu prefigurat
         title: Titul
       admin_account_action:
+        send_email_notification: Notificar li usator per e-posta
         type: Action
+        warning_preset_id: Usar un prefiguration de avise
       announcement:
         all_day: Eveniment del tot die
         ends_at: Fine del eveniment
@@ -35,13 +82,37 @@ ie:
         starts_at: Comense del eveniment
         text: Proclamation
       defaults:
+        chosen_languages: Filtrar lingues
         confirm_new_password: Confirmar nov passa-parol
         confirm_password: Confirmar passa-parol
+        context: Filtrar contextus
         current_password: Actual passa-parol
+        data: Data
+        display_name: Nómine a monstrar
+        email: E-posta
+        expires_in: Expirar pos
         honeypot: "%{label} (ne plenar)"
         locale: Lingue del interfacie
         new_password: Nov passa-parol
         note: Biografie
         password: Passa-parol
+        setting_default_language: Lingue in quel postar
+        setting_default_privacy: Privatie de postada
+        setting_default_sensitive: Sempre marcar medie quam sensitiv
+        setting_display_media_default: Predefinitiones
+        setting_display_media_hide_all: Celar omno
+        setting_display_media_show_all: Monstrar omno
+        setting_theme: Tema de situ
+        setting_trends: Monstrar li hodial tendenties
+        setting_unfollow_modal: Monstrar dialog de confirmation ante dessequer alquem
+        setting_use_pending_items: Mode lent
+      form_admin_settings:
+        registrations_mode: Qui posse registrar se
+        theme: Predefenit tema
+        trends: Possibilisar tendenties
+        trends_as_landing_page: Usar tendenties quam frontispicie
       notification_emails:
         follow_request: Alqui petit sequer te
+        trending_tag: Nov tendentie besonant inspection
+      tag:
+        trendable: Permisse que ti-ci hashtag apari sub tendenties
diff --git a/config/locales/simple_form.it.yml b/config/locales/simple_form.it.yml
index 9ad43235047..731379835aa 100644
--- a/config/locales/simple_form.it.yml
+++ b/config/locales/simple_form.it.yml
@@ -6,7 +6,7 @@ it:
         discoverable: I tuoi post pubblici e il tuo profilo potrebbero essere presenti o consigliati in varie aree di Mastodon e il tuo profilo potrebbe essere suggerito ad altri utenti.
         display_name: Il tuo nome completo o il tuo soprannome.
         fields: La tua homepage, i pronomi, l'età, tutto quello che vuoi.
-        indexable: I tuoi post pubblici potrebbero apparire nei risultati di ricerca su Mastodon. Le persone che hanno interagito con i tuoi post potrebbero essere in grado di cercarli indipendentemente.
+        indexable: I tuoi post pubblici potrebbero apparire nei risultati di ricerca su Mastodon. Le persone che hanno interagito con i tuoi post potrebbero essere in grado di cercarli anche se non hai attivato questa impostazione.
         note: 'Puoi @menzionare altre persone o usare gli #hashtags.'
         show_collections: Le persone saranno in grado di navigare attraverso i tuoi seguaci e seguaci. Le persone che segui vedranno che li seguirai indipendentemente dalle tue impostazioni.
         unlocked: Le persone saranno in grado di seguirti senza richiedere l'approvazione. Deseleziona se vuoi controllare le richieste di seguirti e scegli se accettare o rifiutare nuovi follower.
@@ -122,7 +122,7 @@ it:
         webauthn: Se si tratta di una chiavetta USB assicurati di inserirla e, se necessario, toccarla.
       settings:
         indexable: La pagina del tuo profilo potrebbe apparire nei risultati di ricerca su Google, Bing e altri.
-        show_application: Sarai sempre in grado di vedere quale app ha pubblicato il tuo post indipendentemente.
+        show_application: Tu sarai sempre in grado di vedere quale app ha pubblicato il tuo post anche se hai attivato questa impostazione.
       tag:
         name: Puoi cambiare solo il minuscolo/maiuscolo delle lettere, ad esempio, per renderlo più leggibile
       user:
diff --git a/config/locales/simple_form.lad.yml b/config/locales/simple_form.lad.yml
index 42474876071..aeac5f1c586 100644
--- a/config/locales/simple_form.lad.yml
+++ b/config/locales/simple_form.lad.yml
@@ -4,6 +4,9 @@ lad:
     hints:
       account:
         display_name: Tu nombre para amostrar.
+        fields: Tu deskripsyon, pronombres, edad, todo lo ke keras.
+        note: 'Puedes @enmentar a otra djente o #etiketas.'
+        show_collections: Otra djente podra ver tus segidos i suivantes. Personas a las kualas siges siempre podran ver que las estas sigiendo.
       account_alias:
         acct: Espesifika tu nombre de utilizador@domeno del kuento de ande keres migrar
       account_migration:
@@ -55,6 +58,7 @@ lad:
         setting_display_media_show_all: Amostra siempre el kontenido de multimedia
         setting_use_blurhash: Los gradientes se bazan en los kolores de las imajes eskondidas pero faziendo velados los peratim
         setting_use_pending_items: Eskonde muevas publikasyones detras de un klik en lugar de desplazar otomatikamente la linya
+        username: Solo puedes uzar letras, shifras i sulinyados
         whole_word: Kuando el biervo yave o fraza es solo alfanumerika, solo sera aplikado si konkorda kon todo el biervo
       domain_allow:
         domain: Este domeno podra obtener datos de este sirvidor i los datos entrantes seran prosesados i archivados
@@ -113,6 +117,8 @@ lad:
       sessions:
         otp: 'Introduse el kodiche de autentifikasyon de dos pasos djenerado por tu aplikasyon de telefon o uza uno de tus kodiches de recuperasyon:'
         webauthn: Si es una yave USB, asigurete de insertarla y, si es necesario, pulsala.
+      settings:
+        show_application: Tu siempre podras ver dizde kuala aplikasyon publikates tu publikasyon.
       tag:
         name: Solo se puede trokar la kapitalizasyon de las letras, por enshemplo, para ke sea mas meldable
       user:
@@ -132,6 +138,9 @@ lad:
         fields:
           name: Etiketa
           value: Kontenido
+        indexable: Inkluye publikasyones publikas en los rezultados de bushkeda
+        show_collections: Amostra tus segidos i suivantes en el profil
+        unlocked: Otomatikamente acheta muevos suivantes
       account_alias:
         acct: Alias del kuento viejo
       account_migration:
@@ -168,6 +177,30 @@ lad:
         confirm_password: Konfirma kod
         context: Filtra kontekstos
         current_password: Kod aktual
+        data: Datos
+        display_name: Nombre amostrado
+        email: Adreso de posta elektronika
+        expires_in: Kaduka dempues de
+        fields: Datos adisyonales
+        header: Imaje de kavesera
+        locale: Lingua de enterfaz
+        max_uses: Maksimo numero de uzos
+        new_password: Muevo kod
+        note: Deskripsyon
+        otp_attempt: Kodiche de dos pasos
+        password: Kod
+        phrase: Biervo yave o fraza
+        setting_aggregate_reblogs: Agrupa repartajasyones en linyas
+        setting_always_send_emails: Siempre embia avizos por posta
+        setting_auto_play_gif: Siempre reproduse los GIFs animados
+        setting_default_language: Lingua de publikasyones
+        setting_default_privacy: Privasita de publikasyones
+        setting_display_media_default: Predeterminado
+        setting_display_media_hide_all: Eskonde todo
+        setting_display_media_show_all: Amostra todo
+        setting_reduce_motion: Reduse el movimyento en animasyones
+        setting_system_font_ui: Uza el font predeterminado del sistem
+        setting_trends: Amostra los trendes de oy
         setting_use_blurhash: Amostra gradientes koloridos para kontenido multimedia eskondido
         setting_use_pending_items: Modo lento
         severity: Severita
@@ -228,3 +261,40 @@ lad:
           no_access: Bloka akseso
           sign_up_block: Bloka enrejistrasyones
           sign_up_requires_approval: Limita enrejistrasyones
+        severity: Regla
+      notification_emails:
+        favourite: A alguno le plaze tu publikasyon
+        follow: Alguno te ampeso a segir
+        follow_request: Alguno tiene solisitado segirte
+        mention: Alguno te enmento
+        pending_account: Muevo kuento nesesita revizyon
+        reblog: Alguno repartajo tu publikasyon
+        report: Muevo raporto fue embiado
+        software_updates:
+          all: Avizame de kada aktualizasyon
+          critical: Avizame solo de aktualizasyones kritikas
+          label: Mueva version de Mastodon esta desponivle
+          none: Nunkua avizame de aktualizasyones (no rekomendado)
+          patch: Avizame de aktualizasyones de yerros
+      rule:
+        text: Regla
+      settings:
+        indexable: Inkluye la pajina de profil en los bushkadores
+      tag:
+        name: Etiketa
+      user:
+        role: Rolo
+      user_role:
+        name: Nombre
+        permissions_as_keys: Permisos
+        position: Priorita
+      webhook:
+        events: Evenimientos kapasitados
+        url: URL de Endpoint
+    'no': 'No'
+    not_recommended: No rekomendado
+    recommended: Rekomendado
+    required:
+      mark: "*"
+      text: rekerido
+    'yes': Si
diff --git a/config/locales/tr.yml b/config/locales/tr.yml
index 098719eb7a7..568607e70d2 100644
--- a/config/locales/tr.yml
+++ b/config/locales/tr.yml
@@ -1272,7 +1272,7 @@ tr:
         remove: Filtreden kaldır
       index:
         hint: Bu filtre diğer ölçütlerden bağımsız olarak tekil gönderileri seçmek için uygulanıyor. Web arayüzünü kullanarak bu filtreye daha fazla gönderi ekleyebilirsiniz.
-        title: Filtrelenmiş gönderiler
+        title: Süzgeçlenmiş gönderiler
   generic:
     all: Tümü
     all_items_on_page_selected_html:
diff --git a/config/routes/api.rb b/config/routes/api.rb
index 0ac9173e3e3..f4e4b204add 100644
--- a/config/routes/api.rb
+++ b/config/routes/api.rb
@@ -303,10 +303,6 @@ namespace :api, format: false do
       resources :statuses, only: [:show, :destroy]
     end
 
-    namespace :accounts do
-      resources :relationships, only: :index
-    end
-
     namespace :admin do
       resources :accounts, only: [:index]
     end
diff --git a/db/migrate/20231211234923_create_follow_recommendation_mutes.rb b/db/migrate/20231211234923_create_follow_recommendation_mutes.rb
new file mode 100644
index 00000000000..92cb16e17b5
--- /dev/null
+++ b/db/migrate/20231211234923_create_follow_recommendation_mutes.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class CreateFollowRecommendationMutes < ActiveRecord::Migration[7.1]
+  def change
+    create_table :follow_recommendation_mutes do |t|
+      t.references :account, null: false, foreign_key: { on_delete: :cascade }, index: false
+      t.references :target_account, null: false, foreign_key: { to_table: 'accounts', on_delete: :cascade }
+
+      t.timestamps
+    end
+
+    add_index :follow_recommendation_mutes, [:account_id, :target_account_id], unique: true
+  end
+end
diff --git a/db/migrate/20231212073317_add_languages_index_to_account_summaries.rb b/db/migrate/20231212073317_add_languages_index_to_account_summaries.rb
new file mode 100644
index 00000000000..8adaf792c31
--- /dev/null
+++ b/db/migrate/20231212073317_add_languages_index_to_account_summaries.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddLanguagesIndexToAccountSummaries < ActiveRecord::Migration[7.1]
+  disable_ddl_transaction!
+
+  def change
+    add_index :account_summaries, [:account_id, :language, :sensitive], algorithm: :concurrently
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 4d41b9ff8c4..2bc6ca50d41 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema[7.0].define(version: 2023_10_06_183200) do
+ActiveRecord::Schema[7.1].define(version: 2023_12_12_073317) do
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
 
@@ -474,6 +474,15 @@ ActiveRecord::Schema[7.0].define(version: 2023_10_06_183200) do
     t.index ["tag_id"], name: "index_featured_tags_on_tag_id"
   end
 
+  create_table "follow_recommendation_mutes", force: :cascade do |t|
+    t.bigint "account_id", null: false
+    t.bigint "target_account_id", null: false
+    t.datetime "created_at", null: false
+    t.datetime "updated_at", null: false
+    t.index ["account_id", "target_account_id"], name: "idx_on_account_id_target_account_id_a8c8ddf44e", unique: true
+    t.index ["target_account_id"], name: "index_follow_recommendation_mutes_on_target_account_id"
+  end
+
   create_table "follow_recommendation_suppressions", force: :cascade do |t|
     t.bigint "account_id", null: false
     t.datetime "created_at", null: false
@@ -1212,6 +1221,8 @@ ActiveRecord::Schema[7.0].define(version: 2023_10_06_183200) do
   add_foreign_key "favourites", "statuses", name: "fk_b0e856845e", on_delete: :cascade
   add_foreign_key "featured_tags", "accounts", on_delete: :cascade
   add_foreign_key "featured_tags", "tags", on_delete: :cascade
+  add_foreign_key "follow_recommendation_mutes", "accounts", column: "target_account_id", on_delete: :cascade
+  add_foreign_key "follow_recommendation_mutes", "accounts", on_delete: :cascade
   add_foreign_key "follow_recommendation_suppressions", "accounts", on_delete: :cascade
   add_foreign_key "follow_requests", "accounts", column: "target_account_id", name: "fk_9291ec025d", on_delete: :cascade
   add_foreign_key "follow_requests", "accounts", name: "fk_76d644b0e7", on_delete: :cascade
@@ -1344,6 +1355,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_10_06_183200) do
     WHERE ((accounts.suspended_at IS NULL) AND (accounts.silenced_at IS NULL) AND (accounts.moved_to_account_id IS NULL) AND (accounts.discoverable = true) AND (accounts.locked = false))
     GROUP BY accounts.id;
   SQL
+  add_index "account_summaries", ["account_id", "language", "sensitive"], name: "idx_on_account_id_language_sensitive_250461e1eb"
   add_index "account_summaries", ["account_id"], name: "index_account_summaries_on_account_id", unique: true
 
   create_view "global_follow_recommendations", materialized: true, sql_definition: <<-SQL
diff --git a/lib/mastodon/cli/maintenance.rb b/lib/mastodon/cli/maintenance.rb
index 2e5243468bb..7b3a9852a6d 100644
--- a/lib/mastodon/cli/maintenance.rb
+++ b/lib/mastodon/cli/maintenance.rb
@@ -40,6 +40,10 @@ module Mastodon::CLI
     class BulkImport < ApplicationRecord; end
     class SoftwareUpdate < ApplicationRecord; end
 
+    class DomainBlock < ApplicationRecord
+      scope :by_severity, -> { order(Arel.sql('(CASE severity WHEN 0 THEN 1 WHEN 1 THEN 2 WHEN 2 THEN 0 END), domain')) }
+    end
+
     class PreviewCard < ApplicationRecord
       self.inheritance_column = false
     end
@@ -249,19 +253,7 @@ module Mastodon::CLI
 
       say 'Deduplicating user records…'
 
-      # Deduplicating email
-      ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM users GROUP BY email HAVING count(*) > 1").each do |row|
-        users = User.where(id: row['ids'].split(',')).sort_by(&:updated_at).reverse
-        ref_user = users.shift
-        say "Multiple users registered with e-mail address #{ref_user.email}.", :yellow
-        say "e-mail will be disabled for the following accounts: #{users.map { |user| user.account.acct }.join(', ')}", :yellow
-        say 'Please reach out to them and set another address with `tootctl account modify` or delete them.', :yellow
-
-        users.each_with_index do |user, index|
-          user.update!(email: "#{index} " + user.email)
-        end
-      end
-
+      deduplicate_users_process_email
       deduplicate_users_process_confirmation_token
       deduplicate_users_process_remember_token
       deduplicate_users_process_password_token
@@ -280,6 +272,20 @@ module Mastodon::CLI
       ActiveRecord::Base.connection.execute('REINDEX INDEX index_users_on_unconfirmed_email;') if migrator_version >= 2023_07_02_151753
     end
 
+    def deduplicate_users_process_email
+      ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM users GROUP BY email HAVING count(*) > 1").each do |row|
+        users = User.where(id: row['ids'].split(',')).sort_by(&:updated_at).reverse
+        ref_user = users.shift
+        say "Multiple users registered with e-mail address #{ref_user.email}.", :yellow
+        say "e-mail will be disabled for the following accounts: #{users.map { |user| user.account.acct }.join(', ')}", :yellow
+        say 'Please reach out to them and set another address with `tootctl account modify` or delete them.', :yellow
+
+        users.each_with_index do |user, index|
+          user.update!(email: "#{index} " + user.email)
+        end
+      end
+    end
+
     def deduplicate_users_process_confirmation_token
       ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM users WHERE confirmation_token IS NOT NULL GROUP BY confirmation_token HAVING count(*) > 1").each do |row|
         users = User.where(id: row['ids'].split(',')).sort_by(&:created_at).reverse.drop(1)
@@ -546,7 +552,7 @@ module Mastodon::CLI
       if migrator_version < 2021_04_21_121431
         ActiveRecord::Base.connection.add_index :tags, 'lower((name)::text)', name: 'index_tags_on_name_lower', unique: true
       else
-        ActiveRecord::Base.connection.execute 'CREATE UNIQUE INDEX CONCURRENTLY index_tags_on_name_lower_btree ON tags (lower(name) text_pattern_ops)'
+        ActiveRecord::Base.connection.execute 'CREATE UNIQUE INDEX index_tags_on_name_lower_btree ON tags (lower(name) text_pattern_ops)'
       end
     end
 
@@ -571,7 +577,7 @@ module Mastodon::CLI
 
       say 'Deduplicating webhooks…'
       ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM webhooks GROUP BY url HAVING count(*) > 1").each do |row|
-        Webhooks.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy)
+        Webhook.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy)
       end
 
       say 'Restoring webhooks indexes…'
@@ -604,11 +610,7 @@ module Mastodon::CLI
 
       say 'Please chose the one to keep unchanged, other ones will be automatically renamed.'
 
-      ref_id = ask('Account to keep unchanged:') do |q|
-        q.required true
-        q.default 0
-        q.convert :int
-      end
+      ref_id = ask('Account to keep unchanged:', required: true, default: 0).to_i
 
       accounts.delete_at(ref_id)
 
diff --git a/spec/controllers/admin/settings/branding_controller_spec.rb b/spec/controllers/admin/settings/branding_controller_spec.rb
index 435616022aa..e30300b4e4f 100644
--- a/spec/controllers/admin/settings/branding_controller_spec.rb
+++ b/spec/controllers/admin/settings/branding_controller_spec.rb
@@ -19,14 +19,6 @@ RSpec.describe Admin::Settings::BrandingController do
     end
 
     describe 'PUT #update' do
-      around do |example|
-        before = Setting.site_short_description
-        Setting.site_short_description = nil
-        example.run
-        Setting.site_short_description = before
-        Setting.new_setting_key = nil
-      end
-
       it 'cannot create a setting value for a non-admin key' do
         expect(Setting.new_setting_key).to be_blank
 
diff --git a/spec/controllers/api/v1/accounts/statuses_controller_spec.rb b/spec/controllers/api/v1/accounts/statuses_controller_spec.rb
index 0e4fa930177..df71e94ace7 100644
--- a/spec/controllers/api/v1/accounts/statuses_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/statuses_controller_spec.rb
@@ -10,11 +10,11 @@ describe Api::V1::Accounts::StatusesController do
 
   before do
     allow(controller).to receive(:doorkeeper_token) { token }
-    Fabricate(:status, account: user.account)
   end
 
   describe 'GET #index' do
     it 'returns expected headers', :aggregate_failures do
+      Fabricate(:status, account: user.account)
       get :index, params: { account_id: user.account.id, limit: 1 }
 
       expect(response).to have_http_status(200)
@@ -30,7 +30,6 @@ describe Api::V1::Accounts::StatusesController do
     end
 
     context 'with exclude replies' do
-      let!(:older_statuses) { user.account.statuses.destroy_all }
       let!(:status) { Fabricate(:status, account: user.account) }
       let!(:status_self_reply) { Fabricate(:status, account: user.account, thread: status) }
 
diff --git a/spec/controllers/api/v1/filters_controller_spec.rb b/spec/controllers/api/v1/filters_controller_spec.rb
index 8d5408cf548..b0f64ccf418 100644
--- a/spec/controllers/api/v1/filters_controller_spec.rb
+++ b/spec/controllers/api/v1/filters_controller_spec.rb
@@ -15,10 +15,15 @@ RSpec.describe Api::V1::FiltersController do
   describe 'GET #index' do
     let(:scopes) { 'read:filters' }
     let!(:filter) { Fabricate(:custom_filter, account: user.account) }
+    let!(:custom_filter_keyword) { Fabricate(:custom_filter_keyword, custom_filter: filter) }
 
     it 'returns http success' do
       get :index
       expect(response).to have_http_status(200)
+      expect(body_as_json)
+        .to contain_exactly(
+          include(id: custom_filter_keyword.id.to_s)
+        )
     end
   end
 
diff --git a/spec/controllers/api/v1/trends/links_controller_spec.rb b/spec/controllers/api/v1/trends/links_controller_spec.rb
index 12d4198aaf8..bcaf066f160 100644
--- a/spec/controllers/api/v1/trends/links_controller_spec.rb
+++ b/spec/controllers/api/v1/trends/links_controller_spec.rb
@@ -6,12 +6,6 @@ RSpec.describe Api::V1::Trends::LinksController do
   render_views
 
   describe 'GET #index' do
-    around do |example|
-      previous = Setting.trends
-      example.run
-      Setting.trends = previous
-    end
-
     context 'when trends are disabled' do
       before { Setting.trends = false }
 
diff --git a/spec/controllers/api/v1/trends/statuses_controller_spec.rb b/spec/controllers/api/v1/trends/statuses_controller_spec.rb
index 69fdb270d57..25ab5f228a0 100644
--- a/spec/controllers/api/v1/trends/statuses_controller_spec.rb
+++ b/spec/controllers/api/v1/trends/statuses_controller_spec.rb
@@ -6,12 +6,6 @@ RSpec.describe Api::V1::Trends::StatusesController do
   render_views
 
   describe 'GET #index' do
-    around do |example|
-      previous = Setting.trends
-      example.run
-      Setting.trends = previous
-    end
-
     context 'when trends are disabled' do
       before { Setting.trends = false }
 
diff --git a/spec/controllers/api/v1/trends/tags_controller_spec.rb b/spec/controllers/api/v1/trends/tags_controller_spec.rb
index 9311392cd9d..c889f1c5b9e 100644
--- a/spec/controllers/api/v1/trends/tags_controller_spec.rb
+++ b/spec/controllers/api/v1/trends/tags_controller_spec.rb
@@ -6,12 +6,6 @@ RSpec.describe Api::V1::Trends::TagsController do
   render_views
 
   describe 'GET #index' do
-    around do |example|
-      previous = Setting.trends
-      example.run
-      Setting.trends = previous
-    end
-
     context 'when trends are disabled' do
       before { Setting.trends = false }
 
diff --git a/spec/controllers/api/v2/admin/accounts_controller_spec.rb b/spec/controllers/api/v2/admin/accounts_controller_spec.rb
index 18b3950140e..349f4d2528b 100644
--- a/spec/controllers/api/v2/admin/accounts_controller_spec.rb
+++ b/spec/controllers/api/v2/admin/accounts_controller_spec.rb
@@ -34,28 +34,56 @@ RSpec.describe Api::V2::Admin::AccountsController do
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
     it_behaves_like 'forbidden for wrong role', ''
 
-    [
-      [{ status: 'active', origin: 'local', permissions: 'staff' }, [:admin_account]],
-      [{ by_domain: 'example.org', origin: 'remote' }, [:remote_account]],
-      [{ status: 'suspended' }, [:suspended_remote, :suspended_account]],
-      [{ status: 'disabled' }, [:disabled_account]],
-      [{ status: 'pending' }, [:pending_account]],
-    ].each do |params, expected_results|
-      context "when called with #{params.inspect}" do
-        let(:params) { params }
+    context 'when called with status active and origin local and permissions staff' do
+      let(:params) { { status: 'active', origin: 'local', permissions: 'staff' } }
 
-        it "returns the correct accounts (#{expected_results.inspect})" do
-          expect(response).to have_http_status(200)
-
-          expect(body_json_ids).to eq(expected_results.map { |symbol| send(symbol).id })
-        end
-
-        def body_json_ids
-          body_as_json.map { |a| a[:id].to_i }
-        end
+      it 'returns the correct accounts' do
+        expect(response).to have_http_status(200)
+        expect(body_json_ids).to eq([admin_account.id])
       end
     end
 
+    context 'when called with by_domain value and origin remote' do
+      let(:params) { { by_domain: 'example.org', origin: 'remote' } }
+
+      it 'returns the correct accounts' do
+        expect(response).to have_http_status(200)
+        expect(body_json_ids).to include(remote_account.id)
+        expect(body_json_ids).to_not include(other_remote_account.id)
+      end
+    end
+
+    context 'when called with status suspended' do
+      let(:params) { { status: 'suspended' } }
+
+      it 'returns the correct accounts' do
+        expect(response).to have_http_status(200)
+        expect(body_json_ids).to include(suspended_remote.id, suspended_account.id)
+      end
+    end
+
+    context 'when called with status disabled' do
+      let(:params) { { status: 'disabled' } }
+
+      it 'returns the correct accounts' do
+        expect(response).to have_http_status(200)
+        expect(body_json_ids).to include(disabled_account.id)
+      end
+    end
+
+    context 'when called with status pending' do
+      let(:params) { { status: 'pending' } }
+
+      it 'returns the correct accounts' do
+        expect(response).to have_http_status(200)
+        expect(body_json_ids).to include(pending_account.id)
+      end
+    end
+
+    def body_json_ids
+      body_as_json.map { |a| a[:id].to_i }
+    end
+
     context 'with limit param' do
       let(:params) { { limit: 1 } }
 
diff --git a/spec/controllers/api/v2/filters/keywords_controller_spec.rb b/spec/controllers/api/v2/filters/keywords_controller_spec.rb
index 5321f787a1a..9f25237bcf1 100644
--- a/spec/controllers/api/v2/filters/keywords_controller_spec.rb
+++ b/spec/controllers/api/v2/filters/keywords_controller_spec.rb
@@ -22,6 +22,10 @@ RSpec.describe Api::V2::Filters::KeywordsController do
     it 'returns http success' do
       get :index, params: { filter_id: filter.id }
       expect(response).to have_http_status(200)
+      expect(body_as_json)
+        .to contain_exactly(
+          include(id: keyword.id.to_s)
+        )
     end
 
     context "when trying to access another's user filters" do
diff --git a/spec/controllers/api/v2/filters/statuses_controller_spec.rb b/spec/controllers/api/v2/filters/statuses_controller_spec.rb
index 5c2a623954f..d9df803971b 100644
--- a/spec/controllers/api/v2/filters/statuses_controller_spec.rb
+++ b/spec/controllers/api/v2/filters/statuses_controller_spec.rb
@@ -22,6 +22,10 @@ RSpec.describe Api::V2::Filters::StatusesController do
     it 'returns http success' do
       get :index, params: { filter_id: filter.id }
       expect(response).to have_http_status(200)
+      expect(body_as_json)
+        .to contain_exactly(
+          include(id: status_filter.id.to_s)
+        )
     end
 
     context "when trying to access another's user filters" do
diff --git a/spec/controllers/auth/confirmations_controller_spec.rb b/spec/controllers/auth/confirmations_controller_spec.rb
index 58bc38f5481..15403e8ea13 100644
--- a/spec/controllers/auth/confirmations_controller_spec.rb
+++ b/spec/controllers/auth/confirmations_controller_spec.rb
@@ -41,8 +41,9 @@ describe Auth::ConfirmationsController do
         get :show, params: { confirmation_token: 'foobar' }
       end
 
-      it 'redirects to login' do
+      it 'redirects to login and confirms user' do
         expect(response).to redirect_to(new_user_session_path)
+        expect(user.reload.confirmed_at).to_not be_nil
       end
     end
 
@@ -87,8 +88,9 @@ describe Auth::ConfirmationsController do
         get :show, params: { confirmation_token: 'foobar' }
       end
 
-      it 'redirects to login' do
+      it 'redirects to login and confirms email' do
         expect(response).to redirect_to(new_user_session_path)
+        expect(user.reload.unconfirmed_email).to be_nil
       end
 
       it 'does not queue up bootstrapping of home timeline' do
diff --git a/spec/controllers/auth/passwords_controller_spec.rb b/spec/controllers/auth/passwords_controller_spec.rb
index e7f7ab4676c..d70490abcf1 100644
--- a/spec/controllers/auth/passwords_controller_spec.rb
+++ b/spec/controllers/auth/passwords_controller_spec.rb
@@ -70,6 +70,7 @@ describe Auth::PasswordsController do
 
       it 'deactivates all sessions' do
         expect(user.session_activations.count).to eq 0
+        expect { session_activation.reload }.to raise_error(ActiveRecord::RecordNotFound)
       end
 
       it 'revokes all access tokens' do
@@ -78,6 +79,7 @@ describe Auth::PasswordsController do
 
       it 'removes push subscriptions' do
         expect(Web::PushSubscription.where(user: user).or(Web::PushSubscription.where(access_token: access_token)).count).to eq 0
+        expect { web_push_subscription.reload }.to raise_error(ActiveRecord::RecordNotFound)
       end
     end
 
diff --git a/spec/controllers/auth/registrations_controller_spec.rb b/spec/controllers/auth/registrations_controller_spec.rb
index a9b24a10040..37172f8d244 100644
--- a/spec/controllers/auth/registrations_controller_spec.rb
+++ b/spec/controllers/auth/registrations_controller_spec.rb
@@ -6,12 +6,6 @@ RSpec.describe Auth::RegistrationsController do
   render_views
 
   shared_examples 'checks for enabled registrations' do |path|
-    around do |example|
-      registrations_mode = Setting.registrations_mode
-      example.run
-      Setting.registrations_mode = registrations_mode
-    end
-
     it 'redirects if it is in single user mode while it is open for registration' do
       Fabricate(:account)
       Setting.registrations_mode = 'open'
@@ -82,12 +76,6 @@ RSpec.describe Auth::RegistrationsController do
     end
 
     context 'with open registrations' do
-      around do |example|
-        registrations_mode = Setting.registrations_mode
-        example.run
-        Setting.registrations_mode = registrations_mode
-      end
-
       it 'returns http success' do
         Setting.registrations_mode = 'open'
         get :new
@@ -120,12 +108,6 @@ RSpec.describe Auth::RegistrationsController do
         post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', agreement: 'true' } }
       end
 
-      around do |example|
-        registrations_mode = Setting.registrations_mode
-        example.run
-        Setting.registrations_mode = registrations_mode
-      end
-
       it 'redirects to setup' do
         subject
         expect(response).to redirect_to auth_setup_path
@@ -146,12 +128,6 @@ RSpec.describe Auth::RegistrationsController do
         post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', agreement: 'false' } }
       end
 
-      around do |example|
-        registrations_mode = Setting.registrations_mode
-        example.run
-        Setting.registrations_mode = registrations_mode
-      end
-
       it 'does not create user' do
         subject
         user = User.find_by(email: 'test@example.com')
@@ -166,12 +142,6 @@ RSpec.describe Auth::RegistrationsController do
         post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', agreement: 'true' } }
       end
 
-      around do |example|
-        registrations_mode = Setting.registrations_mode
-        example.run
-        Setting.registrations_mode = registrations_mode
-      end
-
       it 'redirects to setup' do
         subject
         expect(response).to redirect_to auth_setup_path
@@ -194,12 +164,6 @@ RSpec.describe Auth::RegistrationsController do
         post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', invite_code: invite.code, agreement: 'true' } }
       end
 
-      around do |example|
-        registrations_mode = Setting.registrations_mode
-        example.run
-        Setting.registrations_mode = registrations_mode
-      end
-
       it 'redirects to setup' do
         subject
         expect(response).to redirect_to auth_setup_path
@@ -224,14 +188,6 @@ RSpec.describe Auth::RegistrationsController do
         post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', invite_code: invite.code, agreement: 'true' } }
       end
 
-      around do |example|
-        registrations_mode = Setting.registrations_mode
-        require_invite_text = Setting.require_invite_text
-        example.run
-        Setting.require_invite_text = require_invite_text
-        Setting.registrations_mode = registrations_mode
-      end
-
       it 'redirects to setup' do
         subject
         expect(response).to redirect_to auth_setup_path
diff --git a/spec/controllers/auth/sessions_controller_spec.rb b/spec/controllers/auth/sessions_controller_spec.rb
index f341d75b79d..212cc4d5e5a 100644
--- a/spec/controllers/auth/sessions_controller_spec.rb
+++ b/spec/controllers/auth/sessions_controller_spec.rb
@@ -123,9 +123,8 @@ RSpec.describe Auth::SessionsController do
         let(:previous_ip) { '1.2.3.4' }
         let(:current_ip)  { '4.3.2.1' }
 
-        let!(:previous_login) { Fabricate(:login_activity, user: user, ip: previous_ip) }
-
         before do
+          Fabricate(:login_activity, user: user, ip: previous_ip)
           allow(controller.request).to receive(:remote_ip).and_return(current_ip)
           user.update(current_sign_in_at: 1.month.ago)
           post :create, params: { user: { email: user.email, password: user.password } }
@@ -328,12 +327,6 @@ RSpec.describe Auth::SessionsController do
           Fabricate(:user, email: 'x@y.com', password: 'abcdefgh', otp_required_for_login: true, otp_secret: User.generate_otp_secret(32))
         end
 
-        let!(:recovery_codes) do
-          codes = user.generate_otp_backup_codes!
-          user.save
-          return codes
-        end
-
         let!(:webauthn_credential) do
           user.update(webauthn_id: WebAuthn.generate_user_id)
           public_key_credential = WebAuthn::Credential.from_create(fake_client.create)
@@ -356,6 +349,11 @@ RSpec.describe Auth::SessionsController do
 
         let(:fake_credential) { fake_client.get(challenge: challenge, sign_count: sign_count) }
 
+        before do
+          user.generate_otp_backup_codes!
+          user.save
+        end
+
         context 'when using email and password' do
           before do
             post :create, params: { user: { email: user.email, password: user.password } }
diff --git a/spec/controllers/concerns/account_controller_concern_spec.rb b/spec/controllers/concerns/account_controller_concern_spec.rb
index 56ffcfb047c..e3295204aaf 100644
--- a/spec/controllers/concerns/account_controller_concern_spec.rb
+++ b/spec/controllers/concerns/account_controller_concern_spec.rb
@@ -11,12 +11,6 @@ describe AccountControllerConcern do
     end
   end
 
-  around do |example|
-    registrations_mode = Setting.registrations_mode
-    example.run
-    Setting.registrations_mode = registrations_mode
-  end
-
   before do
     routes.draw { get 'success' => 'anonymous#success' }
   end
diff --git a/spec/controllers/concerns/signature_verification_spec.rb b/spec/controllers/concerns/signature_verification_spec.rb
deleted file mode 100644
index 650cd21eaf5..00000000000
--- a/spec/controllers/concerns/signature_verification_spec.rb
+++ /dev/null
@@ -1,305 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-describe SignatureVerification do
-  let(:wrapped_actor_class) do
-    Class.new do
-      attr_reader :wrapped_account
-
-      def initialize(wrapped_account)
-        @wrapped_account = wrapped_account
-      end
-
-      delegate :uri, :keypair, to: :wrapped_account
-    end
-  end
-
-  controller(ApplicationController) do
-    include SignatureVerification
-
-    before_action :require_actor_signature!, only: [:signature_required]
-
-    def success
-      head 200
-    end
-
-    def alternative_success
-      head 200
-    end
-
-    def signature_required
-      head 200
-    end
-  end
-
-  before do
-    routes.draw do
-      match :via => [:get, :post], 'success' => 'anonymous#success'
-      match :via => [:get, :post], 'signature_required' => 'anonymous#signature_required'
-    end
-  end
-
-  context 'without signature header' do
-    before do
-      get :success
-    end
-
-    describe '#signed_request?' do
-      it 'returns false' do
-        expect(controller.signed_request?).to be false
-      end
-    end
-
-    describe '#signed_request_account' do
-      it 'returns nil' do
-        expect(controller.signed_request_account).to be_nil
-      end
-    end
-  end
-
-  context 'with signature header' do
-    let!(:author) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/actor') }
-
-    context 'without body' do
-      before do
-        get :success
-
-        fake_request = Request.new(:get, request.url)
-        fake_request.on_behalf_of(author)
-
-        request.headers.merge!(fake_request.headers)
-      end
-
-      describe '#signed_request?' do
-        it 'returns true' do
-          expect(controller.signed_request?).to be true
-        end
-      end
-
-      describe '#signed_request_account' do
-        it 'returns an account' do
-          expect(controller.signed_request_account).to eq author
-        end
-
-        it 'returns nil when path does not match' do
-          request.path = '/alternative-path'
-          expect(controller.signed_request_account).to be_nil
-        end
-
-        it 'returns nil when method does not match' do
-          post :success
-          expect(controller.signed_request_account).to be_nil
-        end
-      end
-    end
-
-    context 'with a valid actor that is not an Account' do
-      let(:actor) { wrapped_actor_class.new(author) }
-
-      before do
-        get :success
-
-        fake_request = Request.new(:get, request.url)
-        fake_request.on_behalf_of(author)
-
-        request.headers.merge!(fake_request.headers)
-
-        allow(ActivityPub::TagManager.instance).to receive(:uri_to_actor).with(anything) do
-          actor
-        end
-      end
-
-      describe '#signed_request?' do
-        it 'returns true' do
-          expect(controller.signed_request?).to be true
-        end
-      end
-
-      describe '#signed_request_account' do
-        it 'returns nil' do
-          expect(controller.signed_request_account).to be_nil
-        end
-      end
-
-      describe '#signed_request_actor' do
-        it 'returns the expected actor' do
-          expect(controller.signed_request_actor).to eq actor
-        end
-      end
-    end
-
-    context 'with request with unparsable Date header' do
-      before do
-        get :success
-
-        fake_request = Request.new(:get, request.url)
-        fake_request.add_headers({ 'Date' => 'wrong date' })
-        fake_request.on_behalf_of(author)
-
-        request.headers.merge!(fake_request.headers)
-      end
-
-      describe '#signed_request?' do
-        it 'returns true' do
-          expect(controller.signed_request?).to be true
-        end
-      end
-
-      describe '#signed_request_account' do
-        it 'returns nil' do
-          expect(controller.signed_request_account).to be_nil
-        end
-      end
-
-      describe '#signature_verification_failure_reason' do
-        it 'contains an error description' do
-          controller.signed_request_account
-          expect(controller.signature_verification_failure_reason[:error]).to eq 'Invalid Date header: not RFC 2616 compliant date: "wrong date"'
-        end
-      end
-    end
-
-    context 'with request older than a day' do
-      before do
-        get :success
-
-        fake_request = Request.new(:get, request.url)
-        fake_request.add_headers({ 'Date' => 2.days.ago.utc.httpdate })
-        fake_request.on_behalf_of(author)
-
-        request.headers.merge!(fake_request.headers)
-      end
-
-      describe '#signed_request?' do
-        it 'returns true' do
-          expect(controller.signed_request?).to be true
-        end
-      end
-
-      describe '#signed_request_account' do
-        it 'returns nil' do
-          expect(controller.signed_request_account).to be_nil
-        end
-      end
-
-      describe '#signature_verification_failure_reason' do
-        it 'contains an error description' do
-          controller.signed_request_account
-          expect(controller.signature_verification_failure_reason[:error]).to eq 'Signed request date outside acceptable time window'
-        end
-      end
-    end
-
-    context 'with inaccessible key' do
-      before do
-        get :success
-
-        author = Fabricate(:account, domain: 'localhost:5000', uri: 'http://localhost:5000/actor')
-        fake_request = Request.new(:get, request.url)
-        fake_request.on_behalf_of(author)
-        author.destroy
-
-        request.headers.merge!(fake_request.headers)
-
-        stub_request(:get, 'http://localhost:5000/actor#main-key').to_raise(Mastodon::HostValidationError)
-      end
-
-      describe '#signed_request?' do
-        it 'returns true' do
-          expect(controller.signed_request?).to be true
-        end
-      end
-
-      describe '#signed_request_account' do
-        it 'returns nil' do
-          expect(controller.signed_request_account).to be_nil
-        end
-      end
-    end
-
-    context 'with body' do
-      before do
-        allow(controller).to receive(:actor_refresh_key!).and_return(author)
-        post :success, body: 'Hello world'
-
-        fake_request = Request.new(:post, request.url, body: 'Hello world')
-        fake_request.on_behalf_of(author)
-
-        request.headers.merge!(fake_request.headers)
-      end
-
-      describe '#signed_request?' do
-        it 'returns true' do
-          expect(controller.signed_request?).to be true
-        end
-      end
-
-      describe '#signed_request_account' do
-        it 'returns an account' do
-          expect(controller.signed_request_account).to eq author
-        end
-      end
-
-      context 'when path does not match' do
-        before do
-          request.path = '/alternative-path'
-        end
-
-        describe '#signed_request_account' do
-          it 'returns nil' do
-            expect(controller.signed_request_account).to be_nil
-          end
-        end
-
-        describe '#signature_verification_failure_reason' do
-          it 'contains an error description' do
-            controller.signed_request_account
-            expect(controller.signature_verification_failure_reason[:error]).to include('using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256)')
-            expect(controller.signature_verification_failure_reason[:signed_string]).to include("(request-target): post /alternative-path\n")
-          end
-        end
-      end
-
-      context 'when method does not match' do
-        before do
-          get :success
-        end
-
-        describe '#signed_request_account' do
-          it 'returns nil' do
-            expect(controller.signed_request_account).to be_nil
-          end
-        end
-      end
-
-      context 'when body has been tampered' do
-        before do
-          post :success, body: 'doo doo doo'
-        end
-
-        describe '#signed_request_account' do
-          it 'returns nil when body has been tampered' do
-            expect(controller.signed_request_account).to be_nil
-          end
-        end
-      end
-    end
-  end
-
-  context 'when a signature is required' do
-    before do
-      get :signature_required
-    end
-
-    context 'without signature header' do
-      it 'returns HTTP 401' do
-        expect(response).to have_http_status(401)
-      end
-
-      it 'returns an error' do
-        expect(Oj.load(response.body)['error']).to eq 'Request not signed'
-      end
-    end
-  end
-end
diff --git a/spec/controllers/follower_accounts_controller_spec.rb b/spec/controllers/follower_accounts_controller_spec.rb
index cb8c2a0e5b1..dd78c96c053 100644
--- a/spec/controllers/follower_accounts_controller_spec.rb
+++ b/spec/controllers/follower_accounts_controller_spec.rb
@@ -48,6 +48,13 @@ describe FollowerAccountsController do
 
         it 'returns followers' do
           expect(response).to have_http_status(200)
+          expect(body_as_json)
+            .to include(
+              orderedItems: contain_exactly(
+                include(follow_from_bob.account.username),
+                include(follow_from_chris.account.username)
+              )
+            )
           expect(body['totalItems']).to eq 2
           expect(body['partOf']).to be_present
         end
diff --git a/spec/controllers/following_accounts_controller_spec.rb b/spec/controllers/following_accounts_controller_spec.rb
index 095528ed07b..7bb78fb4204 100644
--- a/spec/controllers/following_accounts_controller_spec.rb
+++ b/spec/controllers/following_accounts_controller_spec.rb
@@ -48,6 +48,13 @@ describe FollowingAccountsController do
 
         it 'returns followers' do
           expect(response).to have_http_status(200)
+          expect(body_as_json)
+            .to include(
+              orderedItems: contain_exactly(
+                include(follow_of_bob.target_account.username),
+                include(follow_of_chris.target_account.username)
+              )
+            )
           expect(body['totalItems']).to eq 2
           expect(body['partOf']).to be_present
         end
diff --git a/spec/controllers/oauth/authorized_applications_controller_spec.rb b/spec/controllers/oauth/authorized_applications_controller_spec.rb
index b54610604c8..b46b944d0ea 100644
--- a/spec/controllers/oauth/authorized_applications_controller_spec.rb
+++ b/spec/controllers/oauth/authorized_applications_controller_spec.rb
@@ -63,5 +63,9 @@ describe Oauth::AuthorizedApplicationsController do
     it 'removes subscriptions for the application\'s access tokens' do
       expect(Web::PushSubscription.where(user: user).count).to eq 0
     end
+
+    it 'removes the web_push_subscription' do
+      expect { web_push_subscription.reload }.to raise_error(ActiveRecord::RecordNotFound)
+    end
   end
 end
diff --git a/spec/controllers/oauth/tokens_controller_spec.rb b/spec/controllers/oauth/tokens_controller_spec.rb
index 973393bcf2a..dd2d8ca70cb 100644
--- a/spec/controllers/oauth/tokens_controller_spec.rb
+++ b/spec/controllers/oauth/tokens_controller_spec.rb
@@ -20,5 +20,9 @@ RSpec.describe Oauth::TokensController do
     it 'removes web push subscription for token' do
       expect(Web::PushSubscription.where(access_token: access_token).count).to eq 0
     end
+
+    it 'removes the web_push_subscription' do
+      expect { web_push_subscription.reload }.to raise_error(ActiveRecord::RecordNotFound)
+    end
   end
 end
diff --git a/spec/controllers/settings/imports_controller_spec.rb b/spec/controllers/settings/imports_controller_spec.rb
index 1e7b7589315..89ec39e54d4 100644
--- a/spec/controllers/settings/imports_controller_spec.rb
+++ b/spec/controllers/settings/imports_controller_spec.rb
@@ -22,6 +22,7 @@ RSpec.describe Settings::ImportsController do
     it 'assigns the expected imports', :aggregate_failures do
       expect(response).to have_http_status(200)
       expect(assigns(:recent_imports)).to eq [import]
+      expect(assigns(:recent_imports)).to_not include(other_import)
       expect(response.headers['Cache-Control']).to include('private, no-store')
     end
   end
@@ -138,6 +139,7 @@ RSpec.describe Settings::ImportsController do
       let(:bulk_import) { Fabricate(:bulk_import, account: user.account, type: import_type, state: :finished) }
 
       before do
+        rows.each { |data| Fabricate(:bulk_import_row, bulk_import: bulk_import, data: data) }
         bulk_import.update(total_items: bulk_import.rows.count, processed_items: bulk_import.rows.count, imported_items: 0)
       end
 
@@ -152,11 +154,11 @@ RSpec.describe Settings::ImportsController do
     context 'with follows' do
       let(:import_type) { 'following' }
 
-      let!(:rows) do
+      let(:rows) do
         [
           { 'acct' => 'foo@bar' },
           { 'acct' => 'user@bar', 'show_reblogs' => false, 'notify' => true, 'languages' => %w(fr de) },
-        ].map { |data| Fabricate(:bulk_import_row, bulk_import: bulk_import, data: data) }
+        ]
       end
 
       include_examples 'export failed rows', "Account address,Show boosts,Notify on new posts,Languages\nfoo@bar,true,false,\nuser@bar,false,true,\"fr, de\"\n"
@@ -165,11 +167,11 @@ RSpec.describe Settings::ImportsController do
     context 'with blocks' do
       let(:import_type) { 'blocking' }
 
-      let!(:rows) do
+      let(:rows) do
         [
           { 'acct' => 'foo@bar' },
           { 'acct' => 'user@bar' },
-        ].map { |data| Fabricate(:bulk_import_row, bulk_import: bulk_import, data: data) }
+        ]
       end
 
       include_examples 'export failed rows', "foo@bar\nuser@bar\n"
@@ -178,11 +180,11 @@ RSpec.describe Settings::ImportsController do
     context 'with mutes' do
       let(:import_type) { 'muting' }
 
-      let!(:rows) do
+      let(:rows) do
         [
           { 'acct' => 'foo@bar' },
           { 'acct' => 'user@bar', 'hide_notifications' => false },
-        ].map { |data| Fabricate(:bulk_import_row, bulk_import: bulk_import, data: data) }
+        ]
       end
 
       include_examples 'export failed rows', "Account address,Hide notifications\nfoo@bar,true\nuser@bar,false\n"
@@ -191,11 +193,11 @@ RSpec.describe Settings::ImportsController do
     context 'with domain blocks' do
       let(:import_type) { 'domain_blocking' }
 
-      let!(:rows) do
+      let(:rows) do
         [
           { 'domain' => 'bad.domain' },
           { 'domain' => 'evil.domain' },
-        ].map { |data| Fabricate(:bulk_import_row, bulk_import: bulk_import, data: data) }
+        ]
       end
 
       include_examples 'export failed rows', "bad.domain\nevil.domain\n"
@@ -204,11 +206,11 @@ RSpec.describe Settings::ImportsController do
     context 'with bookmarks' do
       let(:import_type) { 'bookmarks' }
 
-      let!(:rows) do
+      let(:rows) do
         [
           { 'uri' => 'https://foo.com/1' },
           { 'uri' => 'https://foo.com/2' },
-        ].map { |data| Fabricate(:bulk_import_row, bulk_import: bulk_import, data: data) }
+        ]
       end
 
       include_examples 'export failed rows', "https://foo.com/1\nhttps://foo.com/2\n"
@@ -217,11 +219,11 @@ RSpec.describe Settings::ImportsController do
     context 'with lists' do
       let(:import_type) { 'lists' }
 
-      let!(:rows) do
+      let(:rows) do
         [
           { 'list_name' => 'Amigos', 'acct' => 'user@example.com' },
           { 'list_name' => 'Frenemies', 'acct' => 'user@org.org' },
-        ].map { |data| Fabricate(:bulk_import_row, bulk_import: bulk_import, data: data) }
+        ]
       end
 
       include_examples 'export failed rows', "Amigos,user@example.com\nFrenemies,user@org.org\n"
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 82272b717fc..ec9d908ee9f 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -294,12 +294,6 @@ describe ApplicationHelper do
   end
 
   describe 'title' do
-    around do |example|
-      site_title = Setting.site_title
-      example.run
-      Setting.site_title = site_title
-    end
-
     it 'returns site title on production environment' do
       Setting.site_title = 'site title'
       allow(Rails.env).to receive(:production?).and_return(true)
@@ -320,12 +314,6 @@ describe ApplicationHelper do
       allow(Rails.env).to receive(:production?).and_return(true)
     end
 
-    around do |example|
-      site_title = Setting.site_title
-      example.run
-      Setting.site_title = site_title
-    end
-
     context 'with a page_title content_for value' do
       it 'uses the value in the html title' do
         Setting.site_title = 'Site Title'
diff --git a/spec/helpers/instance_helper_spec.rb b/spec/helpers/instance_helper_spec.rb
index cdbce565984..7e39b7cb3d1 100644
--- a/spec/helpers/instance_helper_spec.rb
+++ b/spec/helpers/instance_helper_spec.rb
@@ -4,12 +4,6 @@ require 'rails_helper'
 
 describe InstanceHelper do
   describe 'site_title' do
-    around do |example|
-      site_title = Setting.site_title
-      example.run
-      Setting.site_title = site_title
-    end
-
     it 'Uses the Setting.site_title value when it exists' do
       Setting.site_title = 'New site title'
 
diff --git a/spec/lib/activitypub/activity/delete_spec.rb b/spec/lib/activitypub/activity/delete_spec.rb
index 3a73b3726c9..aec2c71e58a 100644
--- a/spec/lib/activitypub/activity/delete_spec.rb
+++ b/spec/lib/activitypub/activity/delete_spec.rb
@@ -50,6 +50,10 @@ RSpec.describe ActivityPub::Activity::Delete do
       it 'sends delete activity to followers of rebloggers' do
         expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once
       end
+
+      it 'deletes the reblog' do
+        expect { reblog.reload }.to raise_error(ActiveRecord::RecordNotFound)
+      end
     end
   end
 
diff --git a/spec/lib/mastodon/cli/maintenance_spec.rb b/spec/lib/mastodon/cli/maintenance_spec.rb
index 02169b7a42a..ca492bbf69c 100644
--- a/spec/lib/mastodon/cli/maintenance_spec.rb
+++ b/spec/lib/mastodon/cli/maintenance_spec.rb
@@ -52,5 +52,554 @@ describe Mastodon::CLI::Maintenance do
           .and raise_error(SystemExit)
       end
     end
+
+    context 'when requirements are met' do
+      before do
+        allow(ActiveRecord::Migrator).to receive(:current_version).and_return(2023_08_22_081029) # The latest migration before the cutoff
+        agree_to_backup_warning
+      end
+
+      context 'with duplicate accounts' do
+        before do
+          prepare_duplicate_data
+          choose_local_account_to_keep
+        end
+
+        let(:duplicate_account_username) { 'username' }
+        let(:duplicate_account_domain) { 'host.example' }
+
+        it 'runs the deduplication process' do
+          expect { subject }
+            .to output_results(
+              'Deduplicating accounts',
+              'Multiple local accounts were found for',
+              'Restoring index_accounts_on_username_and_domain_lower',
+              'Reindexing textual indexes on accounts…',
+              'Finished!'
+            )
+            .and change(duplicate_remote_accounts, :count).from(2).to(1)
+            .and change(duplicate_local_accounts, :count).from(2).to(1)
+        end
+
+        def duplicate_remote_accounts
+          Account.where(username: duplicate_account_username, domain: duplicate_account_domain)
+        end
+
+        def duplicate_local_accounts
+          Account.where(username: duplicate_account_username, domain: nil)
+        end
+
+        def prepare_duplicate_data
+          ActiveRecord::Base.connection.remove_index :accounts, name: :index_accounts_on_username_and_domain_lower
+          _remote_account = Fabricate(:account, username: duplicate_account_username, domain: duplicate_account_domain)
+          _remote_account_dupe = Fabricate.build(:account, username: duplicate_account_username, domain: duplicate_account_domain).save(validate: false)
+          _local_account = Fabricate(:account, username: duplicate_account_username, domain: nil)
+          _local_account_dupe = Fabricate.build(:account, username: duplicate_account_username, domain: nil).save(validate: false)
+        end
+
+        def choose_local_account_to_keep
+          allow(cli.shell)
+            .to receive(:ask)
+            .with(/Account to keep unchanged/, anything)
+            .and_return('0')
+            .once
+        end
+      end
+
+      context 'with duplicate users on email' do
+        before do
+          prepare_duplicate_data
+        end
+
+        let(:duplicate_email) { 'duplicate@example.host' }
+
+        it 'runs the deduplication process' do
+          expect { subject }
+            .to output_results(
+              'Deduplicating user records',
+              'Restoring users indexes',
+              'Finished!'
+            )
+            .and change(duplicate_users, :count).from(2).to(1)
+        end
+
+        def duplicate_users
+          User.where(email: duplicate_email)
+        end
+
+        def prepare_duplicate_data
+          ActiveRecord::Base.connection.remove_index :users, :email
+          Fabricate(:user, email: duplicate_email)
+          Fabricate.build(:user, email: duplicate_email).save(validate: false)
+        end
+      end
+
+      context 'with duplicate users on confirmation_token' do
+        before do
+          prepare_duplicate_data
+        end
+
+        let(:duplicate_confirmation_token) { '123ABC' }
+
+        it 'runs the deduplication process' do
+          expect { subject }
+            .to output_results(
+              'Deduplicating user records',
+              'Unsetting confirmation token',
+              'Restoring users indexes',
+              'Finished!'
+            )
+            .and change(duplicate_users, :count).from(2).to(1)
+        end
+
+        def duplicate_users
+          User.where(confirmation_token: duplicate_confirmation_token)
+        end
+
+        def prepare_duplicate_data
+          ActiveRecord::Base.connection.remove_index :users, :confirmation_token
+          Fabricate(:user, confirmation_token: duplicate_confirmation_token)
+          Fabricate.build(:user, confirmation_token: duplicate_confirmation_token).save(validate: false)
+        end
+      end
+
+      context 'with duplicate users on reset_password_token' do
+        before do
+          prepare_duplicate_data
+        end
+
+        let(:duplicate_reset_password_token) { '123ABC' }
+
+        it 'runs the deduplication process' do
+          expect { subject }
+            .to output_results(
+              'Deduplicating user records',
+              'Unsetting password reset token',
+              'Restoring users indexes',
+              'Finished!'
+            )
+            .and change(duplicate_users, :count).from(2).to(1)
+        end
+
+        def duplicate_users
+          User.where(reset_password_token: duplicate_reset_password_token)
+        end
+
+        def prepare_duplicate_data
+          ActiveRecord::Base.connection.remove_index :users, :reset_password_token
+          Fabricate(:user, reset_password_token: duplicate_reset_password_token)
+          Fabricate.build(:user, reset_password_token: duplicate_reset_password_token).save(validate: false)
+        end
+      end
+
+      context 'with duplicate account_domain_blocks' do
+        before do
+          prepare_duplicate_data
+        end
+
+        let(:duplicate_domain) { 'example.host' }
+        let(:account) { Fabricate(:account) }
+
+        it 'runs the deduplication process' do
+          expect { subject }
+            .to output_results(
+              'Removing duplicate account domain blocks',
+              'Restoring account domain blocks indexes',
+              'Finished!'
+            )
+            .and change(duplicate_account_domain_blocks, :count).from(2).to(1)
+        end
+
+        def duplicate_account_domain_blocks
+          AccountDomainBlock.where(account: account, domain: duplicate_domain)
+        end
+
+        def prepare_duplicate_data
+          ActiveRecord::Base.connection.remove_index :account_domain_blocks, [:account_id, :domain]
+          Fabricate(:account_domain_block, account: account, domain: duplicate_domain)
+          Fabricate.build(:account_domain_block, account: account, domain: duplicate_domain).save(validate: false)
+        end
+      end
+
+      context 'with duplicate announcement_reactions' do
+        before do
+          prepare_duplicate_data
+        end
+
+        let(:account) { Fabricate(:account) }
+        let(:announcement) { Fabricate(:announcement) }
+        let(:name) { Fabricate(:custom_emoji).shortcode }
+
+        it 'runs the deduplication process' do
+          expect { subject }
+            .to output_results(
+              'Removing duplicate announcement reactions',
+              'Restoring announcement_reactions indexes',
+              'Finished!'
+            )
+            .and change(duplicate_announcement_reactions, :count).from(2).to(1)
+        end
+
+        def duplicate_announcement_reactions
+          AnnouncementReaction.where(account: account, announcement: announcement, name: name)
+        end
+
+        def prepare_duplicate_data
+          ActiveRecord::Base.connection.remove_index :announcement_reactions, [:account_id, :announcement_id, :name]
+          Fabricate(:announcement_reaction, account: account, announcement: announcement, name: name)
+          Fabricate.build(:announcement_reaction, account: account, announcement: announcement, name: name).save(validate: false)
+        end
+      end
+
+      context 'with duplicate conversations' do
+        before do
+          prepare_duplicate_data
+        end
+
+        let(:uri) { 'https://example.host/path' }
+
+        it 'runs the deduplication process' do
+          expect { subject }
+            .to output_results(
+              'Deduplicating conversations',
+              'Restoring conversations indexes',
+              'Finished!'
+            )
+            .and change(duplicate_conversations, :count).from(2).to(1)
+        end
+
+        def duplicate_conversations
+          Conversation.where(uri: uri)
+        end
+
+        def prepare_duplicate_data
+          ActiveRecord::Base.connection.remove_index :conversations, :uri
+          Fabricate(:conversation, uri: uri)
+          Fabricate.build(:conversation, uri: uri).save(validate: false)
+        end
+      end
+
+      context 'with duplicate custom_emojis' do
+        before do
+          prepare_duplicate_data
+        end
+
+        let(:duplicate_shortcode) { 'wowzers' }
+        let(:duplicate_domain) { 'example.host' }
+
+        it 'runs the deduplication process' do
+          expect { subject }
+            .to output_results(
+              'Deduplicating custom_emojis',
+              'Restoring custom_emojis indexes',
+              'Finished!'
+            )
+            .and change(duplicate_custom_emojis, :count).from(2).to(1)
+        end
+
+        def duplicate_custom_emojis
+          CustomEmoji.where(shortcode: duplicate_shortcode, domain: duplicate_domain)
+        end
+
+        def prepare_duplicate_data
+          ActiveRecord::Base.connection.remove_index :custom_emojis, [:shortcode, :domain]
+          Fabricate(:custom_emoji, shortcode: duplicate_shortcode, domain: duplicate_domain)
+          Fabricate.build(:custom_emoji, shortcode: duplicate_shortcode, domain: duplicate_domain).save(validate: false)
+        end
+      end
+
+      context 'with duplicate custom_emoji_categories' do
+        before do
+          prepare_duplicate_data
+        end
+
+        let(:duplicate_name) { 'name_value' }
+
+        it 'runs the deduplication process' do
+          expect { subject }
+            .to output_results(
+              'Deduplicating custom_emoji_categories',
+              'Restoring custom_emoji_categories indexes',
+              'Finished!'
+            )
+            .and change(duplicate_custom_emoji_categories, :count).from(2).to(1)
+        end
+
+        def duplicate_custom_emoji_categories
+          CustomEmojiCategory.where(name: duplicate_name)
+        end
+
+        def prepare_duplicate_data
+          ActiveRecord::Base.connection.remove_index :custom_emoji_categories, :name
+          Fabricate(:custom_emoji_category, name: duplicate_name)
+          Fabricate.build(:custom_emoji_category, name: duplicate_name).save(validate: false)
+        end
+      end
+
+      context 'with duplicate domain_allows' do
+        before do
+          prepare_duplicate_data
+        end
+
+        let(:domain) { 'example.host' }
+
+        it 'runs the deduplication process' do
+          expect { subject }
+            .to output_results(
+              'Deduplicating domain_allows',
+              'Restoring domain_allows indexes',
+              'Finished!'
+            )
+            .and change(duplicate_domain_allows, :count).from(2).to(1)
+        end
+
+        def duplicate_domain_allows
+          DomainAllow.where(domain: domain)
+        end
+
+        def prepare_duplicate_data
+          ActiveRecord::Base.connection.remove_index :domain_allows, :domain
+          Fabricate(:domain_allow, domain: domain)
+          Fabricate.build(:domain_allow, domain: domain).save(validate: false)
+        end
+      end
+
+      context 'with duplicate domain_blocks' do
+        before do
+          prepare_duplicate_data
+        end
+
+        let(:domain) { 'example.host' }
+
+        it 'runs the deduplication process' do
+          expect { subject }
+            .to output_results(
+              'Deduplicating domain_blocks',
+              'Restoring domain_blocks indexes',
+              'Finished!'
+            )
+            .and change(duplicate_domain_blocks, :count).from(2).to(1)
+        end
+
+        def duplicate_domain_blocks
+          DomainBlock.where(domain: domain)
+        end
+
+        def prepare_duplicate_data
+          ActiveRecord::Base.connection.remove_index :domain_blocks, :domain
+          Fabricate(:domain_block, domain: domain)
+          Fabricate.build(:domain_block, domain: domain).save(validate: false)
+        end
+      end
+
+      context 'with duplicate email_domain_blocks' do
+        before do
+          prepare_duplicate_data
+        end
+
+        let(:domain) { 'example.host' }
+
+        it 'runs the deduplication process' do
+          expect { subject }
+            .to output_results(
+              'Deduplicating email_domain_blocks',
+              'Restoring email_domain_blocks indexes',
+              'Finished!'
+            )
+            .and change(duplicate_email_domain_blocks, :count).from(2).to(1)
+        end
+
+        def duplicate_email_domain_blocks
+          EmailDomainBlock.where(domain: domain)
+        end
+
+        def prepare_duplicate_data
+          ActiveRecord::Base.connection.remove_index :email_domain_blocks, :domain
+          Fabricate(:email_domain_block, domain: domain)
+          Fabricate.build(:email_domain_block, domain: domain).save(validate: false)
+        end
+      end
+
+      context 'with duplicate media_attachments' do
+        before do
+          prepare_duplicate_data
+        end
+
+        let(:shortcode) { 'codenam' }
+
+        it 'runs the deduplication process' do
+          expect { subject }
+            .to output_results(
+              'Deduplicating media_attachments',
+              'Restoring media_attachments indexes',
+              'Finished!'
+            )
+            .and change(duplicate_media_attachments, :count).from(2).to(1)
+        end
+
+        def duplicate_media_attachments
+          MediaAttachment.where(shortcode: shortcode)
+        end
+
+        def prepare_duplicate_data
+          ActiveRecord::Base.connection.remove_index :media_attachments, :shortcode
+          Fabricate(:media_attachment, shortcode: shortcode)
+          Fabricate.build(:media_attachment, shortcode: shortcode).save(validate: false)
+        end
+      end
+
+      context 'with duplicate preview_cards' do
+        before do
+          prepare_duplicate_data
+        end
+
+        let(:url) { 'https://example.host/path' }
+
+        it 'runs the deduplication process' do
+          expect { subject }
+            .to output_results(
+              'Deduplicating preview_cards',
+              'Restoring preview_cards indexes',
+              'Finished!'
+            )
+            .and change(duplicate_preview_cards, :count).from(2).to(1)
+        end
+
+        def duplicate_preview_cards
+          PreviewCard.where(url: url)
+        end
+
+        def prepare_duplicate_data
+          ActiveRecord::Base.connection.remove_index :preview_cards, :url
+          Fabricate(:preview_card, url: url)
+          Fabricate.build(:preview_card, url: url).save(validate: false)
+        end
+      end
+
+      context 'with duplicate statuses' do
+        before do
+          prepare_duplicate_data
+        end
+
+        let(:uri) { 'https://example.host/path' }
+        let(:account) { Fabricate(:account) }
+
+        it 'runs the deduplication process' do
+          expect { subject }
+            .to output_results(
+              'Deduplicating statuses',
+              'Restoring statuses indexes',
+              'Finished!'
+            )
+            .and change(duplicate_statuses, :count).from(2).to(1)
+        end
+
+        def duplicate_statuses
+          Status.where(uri: uri)
+        end
+
+        def prepare_duplicate_data
+          ActiveRecord::Base.connection.remove_index :statuses, :uri
+          Fabricate(:status, account: account, uri: uri)
+          duplicate = Fabricate.build(:status, account: account, uri: uri)
+          duplicate.save(validate: false)
+          Fabricate(:status_pin, account: account, status: duplicate)
+          Fabricate(:status, in_reply_to_id: duplicate.id)
+          Fabricate(:status, reblog_of_id: duplicate.id)
+        end
+      end
+
+      context 'with duplicate tags' do
+        before do
+          prepare_duplicate_data
+        end
+
+        let(:name) { 'tagname' }
+
+        it 'runs the deduplication process' do
+          expect { subject }
+            .to output_results(
+              'Deduplicating tags',
+              'Restoring tags indexes',
+              'Finished!'
+            )
+            .and change(duplicate_tags, :count).from(2).to(1)
+        end
+
+        def duplicate_tags
+          Tag.where(name: name)
+        end
+
+        def prepare_duplicate_data
+          ActiveRecord::Base.connection.remove_index :tags, name: 'index_tags_on_name_lower_btree'
+          Fabricate(:tag, name: name)
+          Fabricate.build(:tag, name: name).save(validate: false)
+        end
+      end
+
+      context 'with duplicate webauthn_credentials' do
+        before do
+          prepare_duplicate_data
+        end
+
+        let(:external_id) { '123_123_123' }
+
+        it 'runs the deduplication process' do
+          expect { subject }
+            .to output_results(
+              'Deduplicating webauthn_credentials',
+              'Restoring webauthn_credentials indexes',
+              'Finished!'
+            )
+            .and change(duplicate_webauthn_credentials, :count).from(2).to(1)
+        end
+
+        def duplicate_webauthn_credentials
+          WebauthnCredential.where(external_id: external_id)
+        end
+
+        def prepare_duplicate_data
+          ActiveRecord::Base.connection.remove_index :webauthn_credentials, :external_id
+          Fabricate(:webauthn_credential, external_id: external_id)
+          Fabricate.build(:webauthn_credential, external_id: external_id).save(validate: false)
+        end
+      end
+
+      context 'with duplicate webhooks' do
+        before do
+          prepare_duplicate_data
+        end
+
+        let(:url) { 'https://example.host/path' }
+
+        it 'runs the deduplication process' do
+          expect { subject }
+            .to output_results(
+              'Deduplicating webhooks',
+              'Restoring webhooks indexes',
+              'Finished!'
+            )
+            .and change(duplicate_webhooks, :count).from(2).to(1)
+        end
+
+        def duplicate_webhooks
+          Webhook.where(url: url)
+        end
+
+        def prepare_duplicate_data
+          ActiveRecord::Base.connection.remove_index :webhooks, :url
+          Fabricate(:webhook, url: url)
+          Fabricate.build(:webhook, url: url).save(validate: false)
+        end
+      end
+
+      def agree_to_backup_warning
+        allow(cli.shell)
+          .to receive(:yes?)
+          .with('Continue? (Yes/No)')
+          .and_return(true)
+          .once
+      end
+    end
   end
 end
diff --git a/spec/lib/vacuum/applications_vacuum_spec.rb b/spec/lib/vacuum/applications_vacuum_spec.rb
index 57a222aafc8..df5c8606027 100644
--- a/spec/lib/vacuum/applications_vacuum_spec.rb
+++ b/spec/lib/vacuum/applications_vacuum_spec.rb
@@ -13,11 +13,11 @@ RSpec.describe Vacuum::ApplicationsVacuum do
     let!(:unused_app)      { Fabricate(:application, created_at: 1.month.ago) }
     let!(:recent_app)      { Fabricate(:application, created_at: 1.hour.ago) }
 
-    let!(:active_access_token) { Fabricate(:access_token, application: app_with_token) }
-    let!(:active_access_grant) { Fabricate(:access_grant, application: app_with_grant) }
-    let!(:user) { Fabricate(:user, created_by_application: app_with_signup) }
-
     before do
+      Fabricate(:access_token, application: app_with_token)
+      Fabricate(:access_grant, application: app_with_grant)
+      Fabricate(:user, created_by_application: app_with_signup)
+
       subject.perform
     end
 
diff --git a/spec/lib/vacuum/preview_cards_vacuum_spec.rb b/spec/lib/vacuum/preview_cards_vacuum_spec.rb
index c1b7f7e9c50..9dbdf0bc2fe 100644
--- a/spec/lib/vacuum/preview_cards_vacuum_spec.rb
+++ b/spec/lib/vacuum/preview_cards_vacuum_spec.rb
@@ -30,5 +30,9 @@ RSpec.describe Vacuum::PreviewCardsVacuum do
     it 'does not delete attached preview cards' do
       expect(new_preview_card.reload).to be_persisted
     end
+
+    it 'does not delete orphaned preview cards in the retention period' do
+      expect(orphaned_preview_card.reload).to be_persisted
+    end
   end
 end
diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb
index 522549125fe..87aa8bc7543 100644
--- a/spec/models/account_spec.rb
+++ b/spec/models/account_spec.rb
@@ -954,6 +954,7 @@ RSpec.describe Account do
 
       it 'returns every usable non-suspended account' do
         expect(described_class.searchable).to contain_exactly(silenced_local, silenced_remote, local_account, remote_account)
+        expect(described_class.searchable).to_not include(suspended_local, suspended_remote, unconfirmed, unapproved)
       end
 
       it 'does not mess with previously-applied scopes' do
diff --git a/spec/models/account_statuses_cleanup_policy_spec.rb b/spec/models/account_statuses_cleanup_policy_spec.rb
index 74fff30c95a..da2a774b2d5 100644
--- a/spec/models/account_statuses_cleanup_policy_spec.rb
+++ b/spec/models/account_statuses_cleanup_policy_spec.rb
@@ -235,13 +235,17 @@ RSpec.describe AccountStatusesCleanupPolicy do
   describe '#compute_cutoff_id' do
     subject { account_statuses_cleanup_policy.compute_cutoff_id }
 
-    let!(:unrelated_status) { Fabricate(:status, created_at: 3.years.ago) }
     let(:account_statuses_cleanup_policy) { Fabricate(:account_statuses_cleanup_policy, account: account) }
 
+    before { Fabricate(:status, created_at: 3.years.ago) }
+
     context 'when the account has posted multiple toots' do
-      let!(:very_old_status)   { Fabricate(:status, created_at: 3.years.ago, account: account) }
-      let!(:old_status)        { Fabricate(:status, created_at: 3.weeks.ago, account: account) }
-      let!(:recent_status)     { Fabricate(:status, created_at: 2.days.ago, account: account) }
+      let!(:old_status) { Fabricate(:status, created_at: 3.weeks.ago, account: account) }
+
+      before do
+        Fabricate(:status, created_at: 3.years.ago, account: account)
+        Fabricate(:status, created_at: 2.days.ago, account: account)
+      end
 
       it 'returns the most recent id that is still below policy age' do
         expect(subject).to eq old_status.id
@@ -270,16 +274,16 @@ RSpec.describe AccountStatusesCleanupPolicy do
     let!(:faved_secondary) { Fabricate(:status, created_at: 1.year.ago, account: account) }
     let!(:reblogged_primary) { Fabricate(:status, created_at: 1.year.ago, account: account) }
     let!(:reblogged_secondary) { Fabricate(:status, created_at: 1.year.ago, account: account) }
-    let!(:recent_status)     { Fabricate(:status, created_at: 2.days.ago, account: account) }
-
-    let!(:media_attachment)  { Fabricate(:media_attachment, account: account, status: status_with_media) }
-    let!(:status_pin)        { Fabricate(:status_pin, account: account, status: pinned_status) }
-    let!(:favourite)         { Fabricate(:favourite, account: account, status: self_faved) }
-    let!(:bookmark)          { Fabricate(:bookmark, account: account, status: self_bookmarked) }
+    let!(:recent_status) { Fabricate(:status, created_at: 2.days.ago, account: account) }
 
     let(:account_statuses_cleanup_policy) { Fabricate(:account_statuses_cleanup_policy, account: account) }
 
     before do
+      Fabricate(:media_attachment, account: account, status: status_with_media)
+      Fabricate(:status_pin, account: account, status: pinned_status)
+      Fabricate(:favourite, account: account, status: self_faved)
+      Fabricate(:bookmark, account: account, status: self_bookmarked)
+
       faved_primary.status_stat.update(favourites_count: 4)
       faved_secondary.status_stat.update(favourites_count: 5)
       reblogged_primary.status_stat.update(reblogs_count: 4)
diff --git a/spec/models/canonical_email_block_spec.rb b/spec/models/canonical_email_block_spec.rb
index 0acff823779..c63483f968f 100644
--- a/spec/models/canonical_email_block_spec.rb
+++ b/spec/models/canonical_email_block_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe CanonicalEmailBlock do
   end
 
   describe '.block?' do
-    let!(:canonical_email_block) { Fabricate(:canonical_email_block, email: 'foo@bar.com') }
+    before { Fabricate(:canonical_email_block, email: 'foo@bar.com') }
 
     it 'returns true for the same email' do
       expect(described_class.block?('foo@bar.com')).to be true
diff --git a/spec/models/status_spec.rb b/spec/models/status_spec.rb
index 8868308d323..4c6d886a5c2 100644
--- a/spec/models/status_spec.rb
+++ b/spec/models/status_spec.rb
@@ -352,17 +352,29 @@ RSpec.describe Status do
 
     context 'when given one tag' do
       it 'returns the expected statuses' do
-        expect(described_class.tagged_with([tag_cats.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_cats.id, status_with_all_tags.id)
-        expect(described_class.tagged_with([tag_dogs.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_dogs.id, status_with_all_tags.id)
-        expect(described_class.tagged_with([tag_zebras.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_tagged_with_zebras.id, status_with_all_tags.id)
+        expect(described_class.tagged_with([tag_cats.id]))
+          .to include(status_with_tag_cats, status_with_all_tags)
+          .and not_include(status_without_tags)
+        expect(described_class.tagged_with([tag_dogs.id]))
+          .to include(status_with_tag_dogs, status_with_all_tags)
+          .and not_include(status_without_tags)
+        expect(described_class.tagged_with([tag_zebras.id]))
+          .to include(status_tagged_with_zebras, status_with_all_tags)
+          .and not_include(status_without_tags)
       end
     end
 
     context 'when given multiple tags' do
       it 'returns the expected statuses' do
-        expect(described_class.tagged_with([tag_cats.id, tag_dogs.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_cats.id, status_with_tag_dogs.id, status_with_all_tags.id)
-        expect(described_class.tagged_with([tag_cats.id, tag_zebras.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_cats.id, status_tagged_with_zebras.id, status_with_all_tags.id)
-        expect(described_class.tagged_with([tag_dogs.id, tag_zebras.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_dogs.id, status_tagged_with_zebras.id, status_with_all_tags.id)
+        expect(described_class.tagged_with([tag_cats.id, tag_dogs.id]))
+          .to include(status_with_tag_cats, status_with_tag_dogs, status_with_all_tags)
+          .and not_include(status_without_tags)
+        expect(described_class.tagged_with([tag_cats.id, tag_zebras.id]))
+          .to include(status_with_tag_cats, status_tagged_with_zebras, status_with_all_tags)
+          .and not_include(status_without_tags)
+        expect(described_class.tagged_with([tag_dogs.id, tag_zebras.id]))
+          .to include(status_with_tag_dogs, status_tagged_with_zebras, status_with_all_tags)
+          .and not_include(status_without_tags)
       end
     end
   end
@@ -379,17 +391,26 @@ RSpec.describe Status do
 
     context 'when given one tag' do
       it 'returns the expected statuses' do
-        expect(described_class.tagged_with_all([tag_cats.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_cats.id, status_with_all_tags.id)
-        expect(described_class.tagged_with_all([tag_dogs.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_dogs.id, status_with_all_tags.id)
-        expect(described_class.tagged_with_all([tag_zebras.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_tagged_with_zebras.id)
+        expect(described_class.tagged_with_all([tag_cats.id]))
+          .to include(status_with_tag_cats, status_with_all_tags)
+          .and not_include(status_without_tags)
+        expect(described_class.tagged_with_all([tag_dogs.id]))
+          .to include(status_with_tag_dogs, status_with_all_tags)
+          .and not_include(status_without_tags)
+        expect(described_class.tagged_with_all([tag_zebras.id]))
+          .to include(status_tagged_with_zebras)
+          .and not_include(status_without_tags)
       end
     end
 
     context 'when given multiple tags' do
       it 'returns the expected statuses' do
-        expect(described_class.tagged_with_all([tag_cats.id, tag_dogs.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_all_tags.id)
-        expect(described_class.tagged_with_all([tag_cats.id, tag_zebras.id]).reorder(:id).pluck(:id).uniq).to eq []
-        expect(described_class.tagged_with_all([tag_dogs.id, tag_zebras.id]).reorder(:id).pluck(:id).uniq).to eq []
+        expect(described_class.tagged_with_all([tag_cats.id, tag_dogs.id]))
+          .to include(status_with_all_tags)
+        expect(described_class.tagged_with_all([tag_cats.id, tag_zebras.id]))
+          .to eq []
+        expect(described_class.tagged_with_all([tag_dogs.id, tag_zebras.id]))
+          .to eq []
       end
     end
   end
@@ -406,17 +427,29 @@ RSpec.describe Status do
 
     context 'when given one tag' do
       it 'returns the expected statuses' do
-        expect(described_class.tagged_with_none([tag_cats.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_dogs.id, status_tagged_with_zebras.id, status_without_tags.id)
-        expect(described_class.tagged_with_none([tag_dogs.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_cats.id, status_tagged_with_zebras.id, status_without_tags.id)
-        expect(described_class.tagged_with_none([tag_zebras.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_cats.id, status_with_tag_dogs.id, status_without_tags.id)
+        expect(described_class.tagged_with_none([tag_cats.id]))
+          .to include(status_with_tag_dogs, status_tagged_with_zebras, status_without_tags)
+          .and not_include(status_with_all_tags)
+        expect(described_class.tagged_with_none([tag_dogs.id]))
+          .to include(status_with_tag_cats, status_tagged_with_zebras, status_without_tags)
+          .and not_include(status_with_all_tags)
+        expect(described_class.tagged_with_none([tag_zebras.id]))
+          .to include(status_with_tag_cats, status_with_tag_dogs, status_without_tags)
+          .and not_include(status_with_all_tags)
       end
     end
 
     context 'when given multiple tags' do
       it 'returns the expected statuses' do
-        expect(described_class.tagged_with_none([tag_cats.id, tag_dogs.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_tagged_with_zebras.id, status_without_tags.id)
-        expect(described_class.tagged_with_none([tag_cats.id, tag_zebras.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_dogs.id, status_without_tags.id)
-        expect(described_class.tagged_with_none([tag_dogs.id, tag_zebras.id]).reorder(:id).pluck(:id).uniq).to contain_exactly(status_with_tag_cats.id, status_without_tags.id)
+        expect(described_class.tagged_with_none([tag_cats.id, tag_dogs.id]))
+          .to include(status_tagged_with_zebras, status_without_tags)
+          .and not_include(status_with_all_tags)
+        expect(described_class.tagged_with_none([tag_cats.id, tag_zebras.id]))
+          .to include(status_with_tag_dogs, status_without_tags)
+          .and not_include(status_with_all_tags)
+        expect(described_class.tagged_with_none([tag_dogs.id, tag_zebras.id]))
+          .to include(status_with_tag_cats, status_without_tags)
+          .and not_include(status_with_all_tags)
       end
     end
   end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index a2f8d2ca44b..ab5bd39b7b4 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -175,16 +175,8 @@ RSpec.describe User do
       let(:user) { Fabricate(:user, confirmed_at: nil, unconfirmed_email: new_email) }
 
       context 'when the user is already approved' do
-        around do |example|
-          registrations_mode = Setting.registrations_mode
-          Setting.registrations_mode = 'approved'
-
-          example.run
-
-          Setting.registrations_mode = registrations_mode
-        end
-
         before do
+          Setting.registrations_mode = 'approved'
           user.approve!
         end
 
@@ -199,13 +191,8 @@ RSpec.describe User do
       end
 
       context 'when the user does not require explicit approval' do
-        around do |example|
-          registrations_mode = Setting.registrations_mode
+        before do
           Setting.registrations_mode = 'open'
-
-          example.run
-
-          Setting.registrations_mode = registrations_mode
         end
 
         it 'sets email to unconfirmed_email' do
@@ -219,13 +206,8 @@ RSpec.describe User do
       end
 
       context 'when the user requires explicit approval but is not approved' do
-        around do |example|
-          registrations_mode = Setting.registrations_mode
+        before do
           Setting.registrations_mode = 'approved'
-
-          example.run
-
-          Setting.registrations_mode = registrations_mode
         end
 
         it 'sets email to unconfirmed_email' do
@@ -243,16 +225,8 @@ RSpec.describe User do
   describe '#approve!' do
     subject { user.approve! }
 
-    around do |example|
-      registrations_mode = Setting.registrations_mode
-      Setting.registrations_mode = 'approved'
-
-      example.run
-
-      Setting.registrations_mode = registrations_mode
-    end
-
     before do
+      Setting.registrations_mode = 'approved'
       allow(TriggerWebhookWorker).to receive(:perform_async)
     end
 
@@ -448,6 +422,7 @@ RSpec.describe User do
 
     it 'deactivates all sessions' do
       expect(user.session_activations.count).to eq 0
+      expect { session_activation.reload }.to raise_error(ActiveRecord::RecordNotFound)
     end
 
     it 'revokes all access tokens' do
@@ -456,6 +431,7 @@ RSpec.describe User do
 
     it 'removes push subscriptions' do
       expect(Web::PushSubscription.where(user: user).or(Web::PushSubscription.where(access_token: access_token)).count).to eq 0
+      expect { web_push_subscription.reload }.to raise_error(ActiveRecord::RecordNotFound)
     end
   end
 
diff --git a/spec/presenters/instance_presenter_spec.rb b/spec/presenters/instance_presenter_spec.rb
index f20dce5931f..3e8a2c9f7ad 100644
--- a/spec/presenters/instance_presenter_spec.rb
+++ b/spec/presenters/instance_presenter_spec.rb
@@ -6,12 +6,6 @@ describe InstancePresenter do
   let(:instance_presenter) { described_class.new }
 
   describe '#description' do
-    around do |example|
-      site_description = Setting.site_short_description
-      example.run
-      Setting.site_short_description = site_description
-    end
-
     it 'delegates site_description to Setting' do
       Setting.site_short_description = 'Site desc'
       expect(instance_presenter.description).to eq 'Site desc'
@@ -19,12 +13,6 @@ describe InstancePresenter do
   end
 
   describe '#extended_description' do
-    around do |example|
-      site_extended_description = Setting.site_extended_description
-      example.run
-      Setting.site_extended_description = site_extended_description
-    end
-
     it 'delegates site_extended_description to Setting' do
       Setting.site_extended_description = 'Extended desc'
       expect(instance_presenter.extended_description).to eq 'Extended desc'
@@ -32,12 +20,6 @@ describe InstancePresenter do
   end
 
   describe '#email' do
-    around do |example|
-      site_contact_email = Setting.site_contact_email
-      example.run
-      Setting.site_contact_email = site_contact_email
-    end
-
     it 'delegates contact_email to Setting' do
       Setting.site_contact_email = 'admin@example.com'
       expect(instance_presenter.contact.email).to eq 'admin@example.com'
@@ -45,12 +27,6 @@ describe InstancePresenter do
   end
 
   describe '#account' do
-    around do |example|
-      site_contact_username = Setting.site_contact_username
-      example.run
-      Setting.site_contact_username = site_contact_username
-    end
-
     it 'returns the account for the site contact username' do
       Setting.site_contact_username = 'aaa'
       account = Fabricate(:account, username: 'aaa')
diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb
index 0e68fbe1219..c9352a4d5d5 100644
--- a/spec/rails_helper.rb
+++ b/spec/rails_helper.rb
@@ -153,6 +153,7 @@ RSpec::Sidekiq.configure do |config|
 end
 
 RSpec::Matchers.define_negated_matcher :not_change, :change
+RSpec::Matchers.define_negated_matcher :not_include, :include
 
 def request_fixture(name)
   Rails.root.join('spec', 'fixtures', 'requests', name).read
diff --git a/spec/requests/api/v1/instances/activity_spec.rb b/spec/requests/api/v1/instances/activity_spec.rb
index d1f92ef36e5..4f2bc91ad68 100644
--- a/spec/requests/api/v1/instances/activity_spec.rb
+++ b/spec/requests/api/v1/instances/activity_spec.rb
@@ -4,12 +4,6 @@ require 'rails_helper'
 
 RSpec.describe 'Activity' do
   describe 'GET /api/v1/instance/activity' do
-    around do |example|
-      original = Setting.activity_api_enabled
-      example.run
-      Setting.activity_api_enabled = original
-    end
-
     context 'with activity api enabled' do
       before { Setting.activity_api_enabled = true }
 
diff --git a/spec/requests/api/v1/instances/domain_blocks_spec.rb b/spec/requests/api/v1/instances/domain_blocks_spec.rb
index 99b5e2b6aa4..397ecff084a 100644
--- a/spec/requests/api/v1/instances/domain_blocks_spec.rb
+++ b/spec/requests/api/v1/instances/domain_blocks_spec.rb
@@ -4,12 +4,6 @@ require 'rails_helper'
 
 RSpec.describe 'Domain Blocks' do
   describe 'GET /api/v1/instance/domain_blocks' do
-    around do |example|
-      original = Setting.show_domain_blocks
-      example.run
-      Setting.show_domain_blocks = original
-    end
-
     before do
       Fabricate(:domain_block)
     end
diff --git a/spec/requests/api/v1/instances/peers_spec.rb b/spec/requests/api/v1/instances/peers_spec.rb
index d3400ae8fdd..1a7975f8b79 100644
--- a/spec/requests/api/v1/instances/peers_spec.rb
+++ b/spec/requests/api/v1/instances/peers_spec.rb
@@ -4,12 +4,6 @@ require 'rails_helper'
 
 RSpec.describe 'Peers' do
   describe 'GET /api/v1/instance/peers' do
-    around do |example|
-      original = Setting.peers_api_enabled
-      example.run
-      Setting.peers_api_enabled = original
-    end
-
     context 'with peers api enabled' do
       before { Setting.peers_api_enabled = true }
 
diff --git a/spec/requests/api/v1/suggestions_spec.rb b/spec/requests/api/v1/suggestions_spec.rb
index 42b7f86629b..dc89613fc52 100644
--- a/spec/requests/api/v1/suggestions_spec.rb
+++ b/spec/requests/api/v1/suggestions_spec.rb
@@ -13,13 +13,12 @@ RSpec.describe 'Suggestions' do
       get '/api/v1/suggestions', headers: headers, params: params
     end
 
-    let(:bob)    { Fabricate(:account) }
-    let(:jeff)   { Fabricate(:account) }
+    let(:bob) { Fabricate(:account) }
+    let(:jeff) { Fabricate(:account) }
     let(:params) { {} }
 
     before do
-      PotentialFriendshipTracker.record(user.account_id, bob.id, :reblog)
-      PotentialFriendshipTracker.record(user.account_id, jeff.id, :favourite)
+      Setting.bootstrap_timeline_accounts = [bob, jeff].map(&:acct).join(',')
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write'
@@ -65,17 +64,15 @@ RSpec.describe 'Suggestions' do
       delete "/api/v1/suggestions/#{jeff.id}", headers: headers
     end
 
-    let(:suggestions_source) { instance_double(AccountSuggestions::PastInteractionsSource, remove: nil) }
-    let(:bob)                { Fabricate(:account) }
-    let(:jeff)               { Fabricate(:account) }
+    let(:bob) { Fabricate(:account) }
+    let(:jeff) { Fabricate(:account) }
+    let(:scopes) { 'write' }
 
     before do
-      PotentialFriendshipTracker.record(user.account_id, bob.id, :reblog)
-      PotentialFriendshipTracker.record(user.account_id, jeff.id, :favourite)
-      allow(AccountSuggestions::PastInteractionsSource).to receive(:new).and_return(suggestions_source)
+      Setting.bootstrap_timeline_accounts = [bob, jeff].map(&:acct).join(',')
     end
 
-    it_behaves_like 'forbidden for wrong scope', 'write'
+    it_behaves_like 'forbidden for wrong scope', 'read'
 
     it 'returns http success' do
       subject
@@ -86,8 +83,7 @@ RSpec.describe 'Suggestions' do
     it 'removes the specified suggestion' do
       subject
 
-      expect(suggestions_source).to have_received(:remove).with(user.account, jeff.id.to_s).once
-      expect(suggestions_source).to_not have_received(:remove).with(user.account, bob.id.to_s)
+      expect(FollowRecommendationMute.exists?(account: user.account, target_account: jeff)).to be true
     end
 
     context 'without an authorization header' do
diff --git a/spec/requests/cache_spec.rb b/spec/requests/cache_spec.rb
index ce73d3af75d..dbba228740a 100644
--- a/spec/requests/cache_spec.rb
+++ b/spec/requests/cache_spec.rb
@@ -242,17 +242,11 @@ describe 'Caching behavior' do
     end
 
     describe '/api/v1/instance/domain_blocks' do
-      around do |example|
-        old_setting = Setting.show_domain_blocks
+      before do
         Setting.show_domain_blocks = show_domain_blocks
-
-        example.run
-
-        Setting.show_domain_blocks = old_setting
+        get '/api/v1/instance/domain_blocks'
       end
 
-      before { get '/api/v1/instance/domain_blocks' }
-
       context 'when set to be publicly-available' do
         let(:show_domain_blocks) { 'all' }
 
@@ -365,16 +359,8 @@ describe 'Caching behavior' do
     end
 
     describe '/api/v1/instance/domain_blocks' do
-      around do |example|
-        old_setting = Setting.show_domain_blocks
-        Setting.show_domain_blocks = show_domain_blocks
-
-        example.run
-
-        Setting.show_domain_blocks = old_setting
-      end
-
       before do
+        Setting.show_domain_blocks = show_domain_blocks
         get '/api/v1/instance/domain_blocks', headers: { 'Authorization' => "Bearer #{token.token}" }
       end
 
diff --git a/spec/requests/signature_verification_spec.rb b/spec/requests/signature_verification_spec.rb
new file mode 100644
index 00000000000..b753750b840
--- /dev/null
+++ b/spec/requests/signature_verification_spec.rb
@@ -0,0 +1,332 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe 'signature verification concern' do
+  before do
+    stub_tests_controller
+
+    # Signature checking is time-dependent, so travel to a fixed date
+    travel_to '2023-12-20T10:00:00Z'
+  end
+
+  after { Rails.application.reload_routes! }
+
+  # Include the private key so the tests can be easily adjusted and reviewed
+  let(:actor_keypair) do
+    OpenSSL::PKey.read(<<~PEM_TEXT)
+      -----BEGIN RSA PRIVATE KEY-----
+      MIIEowIBAAKCAQEAqIAYvNFGbZ5g4iiK6feSdXD4bDStFM58A7tHycYXaYtzZQpI
+      eHXAmaXuZzXIwtrP4N0gIk8JNwZvXj2UPS+S07t0V9wNK94he01LV5EMz/GN4eNn
+      FmDL64HIEuKLvV8TvgjbUPRD6Y5X0UpKi2ZIFLSb96Q5w0Z/k7ntpVKV52y8kz5F
+      jr/O/0JuHryZe0yItzJh8kzFfeMf0EXzfSnaKvT7P9jhgC6uTre+jXyvVZjiHDrn
+      qvvucdI3I7DRfXo1OqARBrLjy+TdseUAjNYJ+OuPRI1URIWQI01DCHqcohVu9+Ar
+      +BiCjFp3ua+XMuJvrvbD61d1Fvig/9nbBRR+8QIDAQABAoIBAAgySHnFWI6gItR3
+      fkfiqIm80cHCN3Xk1C6iiVu+3oBOZbHpW9R7vl9e/WOA/9O+LPjiSsQOegtWnVvd
+      RRjrl7Hj20VDlZKv5Mssm6zOGAxksrcVbqwdj+fUJaNJCL0AyyseH0x/IE9T8rDC
+      I1GH+3tB3JkhkIN/qjipdX5ab8MswEPu8IC4ViTpdBgWYY/xBcAHPw4xuL0tcwzh
+      FBlf4DqoEVQo8GdK5GAJ2Ny0S4xbXHUURzx/R4y4CCts7niAiLGqd9jmLU1kUTMk
+      QcXfQYK6l+unLc7wDYAz7sFEHh04M48VjWwiIZJnlCqmQbLda7uhhu8zkF1DqZTu
+      ulWDGQECgYEA0TIAc8BQBVab979DHEEmMdgqBwxLY3OIAk0b+r50h7VBGWCDPRsC
+      STD73fQY3lNet/7/jgSGwwAlAJ5PpMXxXiZAE3bUwPmHzgF7pvIOOLhA8O07tHSO
+      L2mvQe6NPzjZ+6iAO2U9PkClxcvGvPx2OBvisfHqZLmxC9PIVxzruQECgYEAzjM6
+      BTUXa6T/qHvLFbN699BXsUOGmHBGaLRapFDBfVvgZrwqYQcZpBBhesLdGTGSqwE7
+      gWsITPIJ+Ldo+38oGYyVys+w/V67q6ud7hgSDTW3hSvm+GboCjk6gzxlt9hQ0t9X
+      8vfDOYhEXvVUJNv3mYO60ENqQhILO4bQ0zi+VfECgYBb/nUccfG+pzunU0Cb6Dp3
+      qOuydcGhVmj1OhuXxLFSDG84Tazo7juvHA9mp7VX76mzmDuhpHPuxN2AzB2SBEoE
+      cSW0aYld413JRfWukLuYTc6hJHIhBTCRwRQFFnae2s1hUdQySm8INT2xIc+fxBXo
+      zrp+Ljg5Wz90SAnN5TX0AQKBgDaatDOq0o/r+tPYLHiLtfWoE4Dau+rkWJDjqdk3
+      lXWn/e3WyHY3Vh/vQpEqxzgju45TXjmwaVtPATr+/usSykCxzP0PMPR3wMT+Rm1F
+      rIoY/odij+CaB7qlWwxj0x/zRbwB7x1lZSp4HnrzBpxYL+JUUwVRxPLIKndSBTza
+      GvVRAoGBAIVBcNcRQYF4fvZjDKAb4fdBsEuHmycqtRCsnkGOz6ebbEQznSaZ0tZE
+      +JuouZaGjyp8uPjNGD5D7mIGbyoZ3KyG4mTXNxDAGBso1hrNDKGBOrGaPhZx8LgO
+      4VXJ+ybXrATf4jr8ccZYsZdFpOphPzz+j55Mqg5vac5P1XjmsGTb
+      -----END RSA PRIVATE KEY-----
+    PEM_TEXT
+  end
+
+  context 'without a Signature header' do
+    it 'does not treat the request as signed' do
+      get '/activitypub/success'
+
+      expect(response).to have_http_status(200)
+      expect(body_as_json).to match(
+        signed_request: false,
+        signature_actor_id: nil,
+        error: 'Request not signed'
+      )
+    end
+
+    context 'when a signature is required' do
+      it 'returns http unauthorized with appropriate error' do
+        get '/activitypub/signature_required'
+
+        expect(response).to have_http_status(401)
+        expect(body_as_json).to match(
+          error: 'Request not signed'
+        )
+      end
+    end
+  end
+
+  context 'with an HTTP Signature from a known account' do
+    let!(:actor) { Fabricate(:account, domain: 'remote.domain', uri: 'https://remote.domain/users/bob', private_key: nil, public_key: actor_keypair.public_key.to_pem) }
+
+    context 'with a valid signature on a GET request' do
+      let(:signature_header) do
+        'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="' # rubocop:disable Layout/LineLength
+      end
+
+      it 'successfuly verifies signature', :aggregate_failures do
+        expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success', { 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Host' => 'www.example.com' })
+
+        get '/activitypub/success', headers: {
+          'Host' => 'www.example.com',
+          'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+          'Signature' => signature_header,
+        }
+
+        expect(response).to have_http_status(200)
+        expect(body_as_json).to match(
+          signed_request: true,
+          signature_actor_id: actor.id.to_s
+        )
+      end
+    end
+
+    context 'with a mismatching path' do
+      it 'fails to verify signature', :aggregate_failures do
+        get '/activitypub/alternative-path', headers: {
+          'Host' => 'www.example.com',
+          'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+          'Signature' => 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="', # rubocop:disable Layout/LineLength
+        }
+
+        expect(body_as_json).to match(
+          signed_request: true,
+          signature_actor_id: nil,
+          error: anything
+        )
+      end
+    end
+
+    context 'with a mismatching method' do
+      it 'fails to verify signature', :aggregate_failures do
+        post '/activitypub/success', headers: {
+          'Host' => 'www.example.com',
+          'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+          'Signature' => 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="', # rubocop:disable Layout/LineLength
+        }
+
+        expect(body_as_json).to match(
+          signed_request: true,
+          signature_actor_id: nil,
+          error: anything
+        )
+      end
+    end
+
+    context 'with an unparsable date' do
+      let(:signature_header) do
+        'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="d4B7nfx8RJcfdJDu1J//5WzPzK/hgtPkdzZx49lu5QhnE7qdV3lgyVimmhCFrO16bwvzIp9iRMyRLkNFxLiEeVaa1gqeKbldGSnU0B0OMjx7rFBa65vLuzWQOATDitVGiBEYqoK4v0DMuFCz2DtFaA/DIUZ3sty8bZ/Ea3U1nByLOO6MacARA3zhMSI0GNxGqsSmZmG0hPLavB3jIXoE3IDoQabMnC39jrlcO/a8h1iaxBm2WD8TejrImJullgqlJIFpKhIHI3ipQkvTGPlm9dx0y+beM06qBvWaWQcmT09eRIUefVsOAzIhUtS/7FVb/URhZvircIJDa7vtiFcmZQ=="' # rubocop:disable Layout/LineLength
+      end
+
+      it 'fails to verify signature', :aggregate_failures do
+        expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success', { 'Date' => 'wrong date', 'Host' => 'www.example.com' })
+
+        get '/activitypub/success', headers: {
+          'Host' => 'www.example.com',
+          'Date' => 'wrong date',
+          'Signature' => signature_header,
+        }
+
+        expect(body_as_json).to match(
+          signed_request: true,
+          signature_actor_id: nil,
+          error: 'Invalid Date header: not RFC 2616 compliant date: "wrong date"'
+        )
+      end
+    end
+
+    context 'with a request older than a day' do
+      let(:signature_header) do
+        'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="G1NuJv4zgoZ3B/ZIjzDWZHK4RC+5pYee74q8/LJEMCWXhcnAomcb9YHaqk1QYfQvcBUIXw3UZ3Q9xO8F9y0i8G5mzJHfQ+OgHqCoJk8EmGwsUXJMh5s1S5YFCRt8TT12TmJZz0VMqLq85ubueSYBM7QtUE/FzFIVLvz4RysgXxaXQKzdnM6+gbUEEKdCURpXdQt2NXQhp4MAmZH3+0lQoR6VxdsK0hx0Ji2PNp1nuqFTlYqNWZazVdLBN+9rETLRmvGXknvg9jOxTTppBVWnkAIl26HtLS3wwFVvz4pJzi9OQDOvLziehVyLNbU61hky+oJ215e2HuKSe2hxHNl1MA=="' # rubocop:disable Layout/LineLength
+      end
+
+      it 'fails to verify signature', :aggregate_failures do
+        expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success', { 'Date' => 'Wed, 18 Dec 2023 10:00:00 GMT', 'Host' => 'www.example.com' })
+
+        get '/activitypub/success', headers: {
+          'Host' => 'www.example.com',
+          'Date' => 'Wed, 18 Dec 2023 10:00:00 GMT',
+          'Signature' => signature_header,
+        }
+
+        expect(body_as_json).to match(
+          signed_request: true,
+          signature_actor_id: nil,
+          error: 'Signed request date outside acceptable time window'
+        )
+      end
+    end
+
+    context 'with a valid signature on a POST request' do
+      let(:digest_header) { 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=' }
+      let(:signature_header) do
+        'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="host date digest (request-target)",signature="gmhMjgMROGElJU3fpehV2acD5kMHeELi8EFP2UPHOdQ54H0r55AxIpji+J3lPe+N2qSb/4H1KXIh6f0lRu8TGSsu12OQmg5hiO8VA9flcA/mh9Lpk+qwlQZIPRqKP9xUEfqD+Z7ti5wPzDKrWAUK/7FIqWgcT/mlqB1R1MGkpMFc/q4CIs2OSNiWgA4K+Kp21oQxzC2kUuYob04gAZ7cyE/FTia5t08uv6lVYFdRsn4XNPn1MsHgFBwBMRG79ng3SyhoG4PrqBEi5q2IdLq3zfre/M6He3wlCpyO2VJNdGVoTIzeZ0Zz8jUscPV3XtWUchpGclLGSaKaq/JyNZeiYQ=="' # rubocop:disable Layout/LineLength
+      end
+
+      it 'successfuly verifies signature', :aggregate_failures do
+        expect(digest_header).to eq digest_value('Hello world')
+        expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'post /activitypub/success', { 'Host' => 'www.example.com', 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Digest' => digest_header })
+
+        post '/activitypub/success', params: 'Hello world', headers: {
+          'Host' => 'www.example.com',
+          'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+          'Digest' => digest_header,
+          'Signature' => signature_header,
+        }
+
+        expect(response).to have_http_status(200)
+        expect(body_as_json).to match(
+          signed_request: true,
+          signature_actor_id: actor.id.to_s
+        )
+      end
+    end
+
+    context 'when the Digest of a POST request is not signed' do
+      let(:digest_header) { 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=' }
+      let(:signature_header) do
+        'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="host date (request-target)",signature="CPD704CG8aCm8X8qIP8kkkiGp1qwFLk/wMVQHOGP0Txxan8c2DZtg/KK7eN8RG8tHx8br/yS2hJs51x4kXImYukGzNJd7ihE3T8lp+9RI1tCcdobTzr/VcVJHDFySdQkg266GCMijRQRZfNvqlJLiisr817PI+gNVBI5qV+vnVd1XhWCEZ+YSmMe8UqYARXAYNqMykTheojqGpTeTFGPUpTQA2Fmt2BipwIjcFDm2Hpihl2kB0MUS0x3zPmHDuadvzoBbN6m3usPDLgYrpALlh+wDs1dYMntcwdwawRKY1oE1XNtgOSum12wntDq3uYL4gya2iPdcw3c929b4koUzw=="' # rubocop:disable Layout/LineLength
+      end
+
+      it 'fails to verify signature', :aggregate_failures do
+        expect(digest_header).to eq digest_value('Hello world')
+        expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'post /activitypub/success', { 'Host' => 'www.example.com', 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT' })
+
+        post '/activitypub/success', params: 'Hello world', headers: {
+          'Host' => 'www.example.com',
+          'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+          'Digest' => digest_header,
+          'Signature' => signature_header,
+        }
+
+        expect(body_as_json).to match(
+          signed_request: true,
+          signature_actor_id: nil,
+          error: 'Mastodon requires the Digest header to be signed when doing a POST request'
+        )
+      end
+    end
+
+    context 'with a tampered body on a POST request' do
+      let(:digest_header) { 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=' }
+      let(:signature_header) do
+        'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="host date digest (request-target)",signature="gmhMjgMROGElJU3fpehV2acD5kMHeELi8EFP2UPHOdQ54H0r55AxIpji+J3lPe+N2qSb/4H1KXIh6f0lRu8TGSsu12OQmg5hiO8VA9flcA/mh9Lpk+qwlQZIPRqKP9xUEfqD+Z7ti5wPzDKrWAUK/7FIqWgcT/mlqB1R1MGkpMFc/q4CIs2OSNiWgA4K+Kp21oQxzC2kUuYob04gAZ7cyE/FTia5t08uv6lVYFdRsn4XNPn1MsHgFBwBMRG79ng3SyhoG4PrqBEi5q2IdLq3zfre/M6He3wlCpyO2VJNdGVoTIzeZ0Zz8jUscPV3XtWUchpGclLGSaKaq/JyNZeiYQ=="' # rubocop:disable Layout/LineLength
+      end
+
+      it 'fails to verify signature', :aggregate_failures do
+        expect(digest_header).to_not eq digest_value('Hello world!')
+        expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'post /activitypub/success', { 'Host' => 'www.example.com', 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Digest' => digest_header })
+
+        post '/activitypub/success', params: 'Hello world!', headers: {
+          'Host' => 'www.example.com',
+          'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+          'Digest' => 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=',
+          'Signature' => signature_header,
+        }
+
+        expect(body_as_json).to match(
+          signed_request: true,
+          signature_actor_id: nil,
+          error: 'Invalid Digest value. Computed SHA-256 digest: wFNeS+K3n/2TKRMFQ2v4iTFOSj+uwF7P/Lt98xrZ5Ro=; given: ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw='
+        )
+      end
+    end
+
+    context 'with a tampered path in a POST request' do
+      it 'fails to verify signature', :aggregate_failures do
+        post '/activitypub/alternative-path', params: 'Hello world', headers: {
+          'Host' => 'www.example.com',
+          'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+          'Digest' => 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=',
+          'Signature' => 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="host date digest (request-target)",signature="gmhMjgMROGElJU3fpehV2acD5kMHeELi8EFP2UPHOdQ54H0r55AxIpji+J3lPe+N2qSb/4H1KXIh6f0lRu8TGSsu12OQmg5hiO8VA9flcA/mh9Lpk+qwlQZIPRqKP9xUEfqD+Z7ti5wPzDKrWAUK/7FIqWgcT/mlqB1R1MGkpMFc/q4CIs2OSNiWgA4K+Kp21oQxzC2kUuYob04gAZ7cyE/FTia5t08uv6lVYFdRsn4XNPn1MsHgFBwBMRG79ng3SyhoG4PrqBEi5q2IdLq3zfre/M6He3wlCpyO2VJNdGVoTIzeZ0Zz8jUscPV3XtWUchpGclLGSaKaq/JyNZeiYQ=="', # rubocop:disable Layout/LineLength
+        }
+
+        expect(response).to have_http_status(200)
+        expect(body_as_json).to match(
+          signed_request: true,
+          signature_actor_id: nil,
+          error: anything
+        )
+      end
+    end
+  end
+
+  context 'with an inaccessible key' do
+    before do
+      stub_request(:get, 'https://remote.domain/users/alice#main-key').to_return(status: 404)
+    end
+
+    it 'fails to verify signature', :aggregate_failures do
+      get '/activitypub/success', headers: {
+        'Host' => 'www.example.com',
+        'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+        'Signature' => 'keyId="https://remote.domain/users/alice#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="', # rubocop:disable Layout/LineLength
+      }
+
+      expect(body_as_json).to match(
+        signed_request: true,
+        signature_actor_id: nil,
+        error: 'Unable to fetch key JSON at https://remote.domain/users/alice#main-key'
+      )
+    end
+  end
+
+  private
+
+  def stub_tests_controller
+    stub_const('ActivityPub::TestsController', activitypub_tests_controller)
+
+    Rails.application.routes.draw do
+      # NOTE: RouteSet#draw removes all routes, so we need to re-insert one
+      resource :instance_actor, path: 'actor', only: [:show]
+
+      match :via => [:get, :post], '/activitypub/success' => 'activitypub/tests#success'
+      match :via => [:get, :post], '/activitypub/alternative-path' => 'activitypub/tests#alternative_success'
+      match :via => [:get, :post], '/activitypub/signature_required' => 'activitypub/tests#signature_required'
+    end
+  end
+
+  def activitypub_tests_controller
+    Class.new(ApplicationController) do
+      include SignatureVerification
+
+      before_action :require_actor_signature!, only: [:signature_required]
+
+      def success
+        render json: {
+          signed_request: signed_request?,
+          signature_actor_id: signed_request_actor&.id&.to_s,
+        }.merge(signature_verification_failure_reason || {})
+      end
+
+      alias_method :alternative_success, :success
+      alias_method :signature_required, :success
+    end
+  end
+
+  def digest_value(body)
+    "SHA-256=#{Digest::SHA256.base64digest(body)}"
+  end
+
+  def build_signature_string(keypair, key_id, request_target, headers)
+    algorithm = 'rsa-sha256'
+    signed_headers = headers.merge({ '(request-target)' => request_target })
+    signed_string = signed_headers.map { |key, value| "#{key.downcase}: #{value}" }.join("\n")
+    signature = Base64.strict_encode64(keypair.sign(OpenSSL::Digest.new('SHA256'), signed_string))
+
+    "keyId=\"#{key_id}\",algorithm=\"#{algorithm}\",headers=\"#{signed_headers.keys.join(' ').downcase}\",signature=\"#{signature}\""
+  end
+end
diff --git a/spec/services/account_statuses_cleanup_service_spec.rb b/spec/services/account_statuses_cleanup_service_spec.rb
index f7a88a9172f..0ac113f1055 100644
--- a/spec/services/account_statuses_cleanup_service_spec.rb
+++ b/spec/services/account_statuses_cleanup_service_spec.rb
@@ -39,6 +39,13 @@ describe AccountStatusesCleanupService, type: :service do
         it 'actually deletes the statuses' do
           subject.call(account_policy, 10)
           expect(Status.find_by(id: [very_old_status.id, old_status.id, another_old_status.id])).to be_nil
+          expect { recent_status.reload }.to_not raise_error
+        end
+
+        it 'preserves recent and unrelated statuses' do
+          subject.call(account_policy, 10)
+          expect { unrelated_status.reload }.to_not raise_error
+          expect { recent_status.reload }.to_not raise_error
         end
       end
 
diff --git a/spec/services/activitypub/fetch_featured_collection_service_spec.rb b/spec/services/activitypub/fetch_featured_collection_service_spec.rb
index 466da891a8b..a98108cea37 100644
--- a/spec/services/activitypub/fetch_featured_collection_service_spec.rb
+++ b/spec/services/activitypub/fetch_featured_collection_service_spec.rb
@@ -87,6 +87,7 @@ RSpec.describe ActivityPub::FetchFeaturedCollectionService, type: :service do
         'https://example.com/account/pinned/unknown-inlined',
         'https://example.com/account/pinned/unknown-reachable'
       )
+      expect(actor.pinned_statuses).to_not include(known_status)
     end
   end
 
diff --git a/spec/services/activitypub/fetch_remote_status_service_spec.rb b/spec/services/activitypub/fetch_remote_status_service_spec.rb
index 826b67d8840..3c64feaeec8 100644
--- a/spec/services/activitypub/fetch_remote_status_service_spec.rb
+++ b/spec/services/activitypub/fetch_remote_status_service_spec.rb
@@ -8,7 +8,6 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do
   subject { described_class.new }
 
   let!(:sender) { Fabricate(:account, domain: 'foo.bar', uri: 'https://foo.bar') }
-  let!(:recipient) { Fabricate(:account) }
 
   let(:existing_status) { nil }
 
diff --git a/spec/services/activitypub/process_account_service_spec.rb b/spec/services/activitypub/process_account_service_spec.rb
index 09eb5ddee3b..58dd2badbb3 100644
--- a/spec/services/activitypub/process_account_service_spec.rb
+++ b/spec/services/activitypub/process_account_service_spec.rb
@@ -33,7 +33,7 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do
   end
 
   context 'when account is not suspended' do
-    subject { described_class.new.call('alice', 'example.com', payload) }
+    subject { described_class.new.call(account.username, account.domain, payload) }
 
     let!(:account) { Fabricate(:account, username: 'alice', domain: 'example.com') }
 
diff --git a/spec/services/activitypub/process_collection_service_spec.rb b/spec/services/activitypub/process_collection_service_spec.rb
index df526daf34c..f4a2b8fec69 100644
--- a/spec/services/activitypub/process_collection_service_spec.rb
+++ b/spec/services/activitypub/process_collection_service_spec.rb
@@ -242,7 +242,8 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
         it 'does not process forged payload' do
           allow(ActivityPub::Activity).to receive(:factory)
 
-          subject.call(json, forwarder)
+          expect { subject.call(json, forwarder) }
+            .to_not change(actor.reload.statuses, :count)
 
           expect(ActivityPub::Activity).to_not have_received(:factory).with(
             hash_including(
diff --git a/spec/services/app_sign_up_service_spec.rb b/spec/services/app_sign_up_service_spec.rb
index d5946cf9b02..0adb473f177 100644
--- a/spec/services/app_sign_up_service_spec.rb
+++ b/spec/services/app_sign_up_service_spec.rb
@@ -28,13 +28,8 @@ RSpec.describe AppSignUpService, type: :service do
     end
 
     context 'when registrations are closed' do
-      around do |example|
-        tmp = Setting.registrations_mode
+      before do
         Setting.registrations_mode = 'none'
-
-        example.run
-
-        Setting.registrations_mode = tmp
       end
 
       it 'raises an error', :aggregate_failures do
diff --git a/spec/services/batched_remove_status_service_spec.rb b/spec/services/batched_remove_status_service_spec.rb
index 8201c9d51f9..991a852f0e2 100644
--- a/spec/services/batched_remove_status_service_spec.rb
+++ b/spec/services/batched_remove_status_service_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe BatchedRemoveStatusService, type: :service do
   let!(:jeff)   { Fabricate(:account) }
   let!(:hank)   { Fabricate(:account, username: 'hank', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }
 
-  let(:status_alice_hello) { PostStatusService.new.call(alice, text: 'Hello @bob@example.com') }
+  let(:status_alice_hello) { PostStatusService.new.call(alice, text: "Hello @#{bob.pretty_acct}") }
   let(:status_alice_other) { PostStatusService.new.call(alice, text: 'Another status') }
 
   before do
diff --git a/spec/services/block_domain_service_spec.rb b/spec/services/block_domain_service_spec.rb
index 36dce9d1963..fc3a1397d96 100644
--- a/spec/services/block_domain_service_spec.rb
+++ b/spec/services/block_domain_service_spec.rb
@@ -21,19 +21,19 @@ RSpec.describe BlockDomainService, type: :service do
     end
 
     it 'removes remote accounts from that domain' do
-      expect(Account.find_remote('badguy666', 'evil.org').suspended?).to be true
+      expect(bad_account.reload.suspended?).to be true
     end
 
     it 'records suspension date appropriately' do
-      expect(Account.find_remote('badguy666', 'evil.org').suspended_at).to eq DomainBlock.find_by(domain: 'evil.org').created_at
+      expect(bad_account.reload.suspended_at).to eq DomainBlock.find_by(domain: 'evil.org').created_at
     end
 
     it 'keeps already-banned accounts banned' do
-      expect(Account.find_remote('badguy', 'evil.org').suspended?).to be true
+      expect(already_banned_account.reload.suspended?).to be true
     end
 
     it 'does not overwrite suspension date of already-banned accounts' do
-      expect(Account.find_remote('badguy', 'evil.org').suspended_at).to_not eq DomainBlock.find_by(domain: 'evil.org').created_at
+      expect(already_banned_account.reload.suspended_at).to_not eq DomainBlock.find_by(domain: 'evil.org').created_at
     end
 
     it 'removes the remote accounts\'s statuses and media attachments' do
@@ -53,19 +53,19 @@ RSpec.describe BlockDomainService, type: :service do
     end
 
     it 'silences remote accounts from that domain' do
-      expect(Account.find_remote('badguy666', 'evil.org').silenced?).to be true
+      expect(bad_account.reload.silenced?).to be true
     end
 
     it 'records suspension date appropriately' do
-      expect(Account.find_remote('badguy666', 'evil.org').silenced_at).to eq DomainBlock.find_by(domain: 'evil.org').created_at
+      expect(bad_account.reload.silenced_at).to eq DomainBlock.find_by(domain: 'evil.org').created_at
     end
 
     it 'keeps already-banned accounts banned' do
-      expect(Account.find_remote('badguy', 'evil.org').silenced?).to be true
+      expect(already_banned_account.reload.silenced?).to be true
     end
 
     it 'does not overwrite suspension date of already-banned accounts' do
-      expect(Account.find_remote('badguy', 'evil.org').silenced_at).to_not eq DomainBlock.find_by(domain: 'evil.org').created_at
+      expect(already_banned_account.reload.silenced_at).to_not eq DomainBlock.find_by(domain: 'evil.org').created_at
     end
 
     it 'leaves the domains status and attachments, but clears media' do
diff --git a/spec/services/bulk_import_service_spec.rb b/spec/services/bulk_import_service_spec.rb
index 3a3f95ccc62..f703650c378 100644
--- a/spec/services/bulk_import_service_spec.rb
+++ b/spec/services/bulk_import_service_spec.rb
@@ -271,14 +271,15 @@ RSpec.describe BulkImportService do
       let(:import_type) { 'domain_blocking' }
       let(:overwrite)   { false }
 
-      let!(:rows) do
+      let(:rows) do
         [
           { 'domain' => 'blocked.com' },
           { 'domain' => 'to_block.com' },
-        ].map { |data| import.rows.create!(data: data) }
+        ]
       end
 
       before do
+        rows.each { |data| import.rows.create!(data: data) }
         account.block_domain!('alreadyblocked.com')
         account.block_domain!('blocked.com')
       end
@@ -298,14 +299,15 @@ RSpec.describe BulkImportService do
       let(:import_type) { 'domain_blocking' }
       let(:overwrite)   { true }
 
-      let!(:rows) do
+      let(:rows) do
         [
           { 'domain' => 'blocked.com' },
           { 'domain' => 'to_block.com' },
-        ].map { |data| import.rows.create!(data: data) }
+        ]
       end
 
       before do
+        rows.each { |data| import.rows.create!(data: data) }
         account.block_domain!('alreadyblocked.com')
         account.block_domain!('blocked.com')
       end
diff --git a/spec/services/delete_account_service_spec.rb b/spec/services/delete_account_service_spec.rb
index a2c57f1c1ee..f04ba9cf140 100644
--- a/spec/services/delete_account_service_spec.rb
+++ b/spec/services/delete_account_service_spec.rb
@@ -28,74 +28,69 @@ RSpec.describe DeleteAccountService, type: :service do
     let!(:account_note) { Fabricate(:account_note, account: account) }
 
     it 'deletes associated owned and target records and target notifications' do
-      expect { subject }
-        .to delete_associated_owned_records
-        .and delete_associated_target_records
-        .and delete_associated_target_notifications
+      subject
+
+      expect_deletion_of_associated_owned_records
+      expect_deletion_of_associated_target_records
+      expect_deletion_of_associated_target_notifications
     end
 
-    def delete_associated_owned_records
-      change do
-        [
-          account.statuses,
-          account.media_attachments,
-          account.notifications,
-          account.favourites,
-          account.active_relationships,
-          account.passive_relationships,
-          account.polls,
-          account.account_notes,
-        ].map(&:count)
-      end.from([2, 1, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0, 0])
+    def expect_deletion_of_associated_owned_records
+      expect { status.reload }.to raise_error(ActiveRecord::RecordNotFound)
+      expect { status_with_mention.reload }.to raise_error(ActiveRecord::RecordNotFound)
+      expect { mention.reload }.to raise_error(ActiveRecord::RecordNotFound)
+      expect { media_attachment.reload }.to raise_error(ActiveRecord::RecordNotFound)
+      expect { notification.reload }.to raise_error(ActiveRecord::RecordNotFound)
+      expect { favourite.reload }.to raise_error(ActiveRecord::RecordNotFound)
+      expect { active_relationship.reload }.to raise_error(ActiveRecord::RecordNotFound)
+      expect { passive_relationship.reload }.to raise_error(ActiveRecord::RecordNotFound)
+      expect { poll.reload }.to raise_error(ActiveRecord::RecordNotFound)
+      expect { poll_vote.reload }.to raise_error(ActiveRecord::RecordNotFound)
+      expect { account_note.reload }.to raise_error(ActiveRecord::RecordNotFound)
     end
 
-    def delete_associated_target_records
-      change(account_pins_for_account, :count).from(1).to(0)
+    def expect_deletion_of_associated_target_records
+      expect { endorsement.reload }.to raise_error(ActiveRecord::RecordNotFound)
     end
 
-    def account_pins_for_account
-      AccountPin.where(target_account: account)
-    end
-
-    def delete_associated_target_notifications
-      change do
-        %w(
-          poll favourite status mention follow
-        ).map { |type| Notification.where(type: type).count }
-      end.from([1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0])
+    def expect_deletion_of_associated_target_notifications
+      expect { favourite_notification.reload }.to raise_error(ActiveRecord::RecordNotFound)
+      expect { follow_notification.reload }.to raise_error(ActiveRecord::RecordNotFound)
+      expect { mention_notification.reload }.to raise_error(ActiveRecord::RecordNotFound)
+      expect { poll_notification.reload }.to raise_error(ActiveRecord::RecordNotFound)
+      expect { status_notification.reload }.to raise_error(ActiveRecord::RecordNotFound)
     end
   end
 
   describe '#call on local account' do
     before do
-      stub_request(:post, 'https://alice.com/inbox').to_return(status: 201)
-      stub_request(:post, 'https://bob.com/inbox').to_return(status: 201)
+      stub_request(:post, remote_alice.inbox_url).to_return(status: 201)
+      stub_request(:post, remote_bob.inbox_url).to_return(status: 201)
     end
 
     let!(:remote_alice) { Fabricate(:account, inbox_url: 'https://alice.com/inbox', domain: 'alice.com', protocol: :activitypub) }
     let!(:remote_bob) { Fabricate(:account, inbox_url: 'https://bob.com/inbox', domain: 'bob.com', protocol: :activitypub) }
 
     include_examples 'common behavior' do
-      let!(:account) { Fabricate(:account) }
-      let!(:local_follower) { Fabricate(:account) }
+      let(:account) { Fabricate(:account) }
+      let(:local_follower) { Fabricate(:account) }
 
       it 'sends a delete actor activity to all known inboxes' do
         subject
-        expect(a_request(:post, 'https://alice.com/inbox')).to have_been_made.once
-        expect(a_request(:post, 'https://bob.com/inbox')).to have_been_made.once
+        expect(a_request(:post, remote_alice.inbox_url)).to have_been_made.once
+        expect(a_request(:post, remote_bob.inbox_url)).to have_been_made.once
       end
     end
   end
 
   describe '#call on remote account' do
     before do
-      stub_request(:post, 'https://alice.com/inbox').to_return(status: 201)
-      stub_request(:post, 'https://bob.com/inbox').to_return(status: 201)
+      stub_request(:post, account.inbox_url).to_return(status: 201)
     end
 
     include_examples 'common behavior' do
-      let!(:account) { Fabricate(:account, inbox_url: 'https://bob.com/inbox', protocol: :activitypub, domain: 'bob.com') }
-      let!(:local_follower) { Fabricate(:account) }
+      let(:account) { Fabricate(:account, inbox_url: 'https://bob.com/inbox', protocol: :activitypub, domain: 'bob.com') }
+      let(:local_follower) { Fabricate(:account) }
 
       it 'sends expected activities to followed and follower inboxes' do
         subject
diff --git a/spec/services/import_service_spec.rb b/spec/services/import_service_spec.rb
index 3936b2363f7..d67da66caf4 100644
--- a/spec/services/import_service_spec.rb
+++ b/spec/services/import_service_spec.rb
@@ -190,7 +190,7 @@ RSpec.describe ImportService, type: :service do
 
     # Make sure to not actually go to the remote server
     before do
-      stub_request(:post, 'https://թութ.հայ/inbox').to_return(status: 200)
+      stub_request(:post, nare.inbox_url).to_return(status: 200)
     end
 
     it 'follows the listed account' do
diff --git a/spec/services/notify_service_spec.rb b/spec/services/notify_service_spec.rb
index 8fcb5865804..50055dafc76 100644
--- a/spec/services/notify_service_spec.rb
+++ b/spec/services/notify_service_spec.rb
@@ -67,9 +67,10 @@ RSpec.describe NotifyService, type: :service do
 
       context 'when the message chain is initiated by recipient, but is not direct message' do
         let(:reply_to) { Fabricate(:status, account: recipient) }
-        let!(:mention) { Fabricate(:mention, account: sender, status: reply_to) }
         let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct, thread: reply_to)) }
 
+        before { Fabricate(:mention, account: sender, status: reply_to) }
+
         it 'does not notify' do
           expect { subject }.to_not change(Notification, :count)
         end
@@ -77,10 +78,11 @@ RSpec.describe NotifyService, type: :service do
 
       context 'when the message chain is initiated by recipient, but without a mention to the sender, even if the sender sends multiple messages in a row' do
         let(:reply_to) { Fabricate(:status, account: recipient) }
-        let!(:mention) { Fabricate(:mention, account: sender, status: reply_to) }
         let(:dummy_reply) { Fabricate(:status, account: sender, visibility: :direct, thread: reply_to) }
         let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct, thread: dummy_reply)) }
 
+        before { Fabricate(:mention, account: sender, status: reply_to) }
+
         it 'does not notify' do
           expect { subject }.to_not change(Notification, :count)
         end
@@ -88,9 +90,10 @@ RSpec.describe NotifyService, type: :service do
 
       context 'when the message chain is initiated by the recipient with a mention to the sender' do
         let(:reply_to) { Fabricate(:status, account: recipient, visibility: :direct) }
-        let!(:mention) { Fabricate(:mention, account: sender, status: reply_to) }
         let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct, thread: reply_to)) }
 
+        before { Fabricate(:mention, account: sender, status: reply_to) }
+
         it 'does notify' do
           expect { subject }.to change(Notification, :count)
         end
diff --git a/spec/services/remove_status_service_spec.rb b/spec/services/remove_status_service_spec.rb
index 7754ae80047..08ce0b0456f 100644
--- a/spec/services/remove_status_service_spec.rb
+++ b/spec/services/remove_status_service_spec.rb
@@ -12,15 +12,15 @@ RSpec.describe RemoveStatusService, type: :service do
   let!(:bill)   { Fabricate(:account, username: 'bill', protocol: :activitypub, domain: 'example2.com', inbox_url: 'http://example2.com/inbox') }
 
   before do
-    stub_request(:post, 'http://example.com/inbox').to_return(status: 200)
-    stub_request(:post, 'http://example2.com/inbox').to_return(status: 200)
+    stub_request(:post, hank.inbox_url).to_return(status: 200)
+    stub_request(:post, bill.inbox_url).to_return(status: 200)
 
     jeff.follow!(alice)
     hank.follow!(alice)
   end
 
   context 'when removed status is not a reblog' do
-    let!(:status) { PostStatusService.new.call(alice, text: 'Hello @bob@example.com ThisIsASecret') }
+    let!(:status) { PostStatusService.new.call(alice, text: "Hello @#{bob.pretty_acct} ThisIsASecret") }
 
     before do
       FavouriteService.new.call(jeff, status)
@@ -39,7 +39,7 @@ RSpec.describe RemoveStatusService, type: :service do
 
     it 'sends Delete activity to followers' do
       subject.call(status)
-      expect(a_request(:post, 'http://example.com/inbox').with(
+      expect(a_request(:post, hank.inbox_url).with(
                body: hash_including({
                  'type' => 'Delete',
                  'object' => {
@@ -53,7 +53,7 @@ RSpec.describe RemoveStatusService, type: :service do
 
     it 'sends Delete activity to rebloggers' do
       subject.call(status)
-      expect(a_request(:post, 'http://example2.com/inbox').with(
+      expect(a_request(:post, bill.inbox_url).with(
                body: hash_including({
                  'type' => 'Delete',
                  'object' => {
@@ -78,7 +78,7 @@ RSpec.describe RemoveStatusService, type: :service do
 
     it 'sends Undo activity to followers' do
       subject.call(status)
-      expect(a_request(:post, 'http://example.com/inbox').with(
+      expect(a_request(:post, hank.inbox_url).with(
                body: hash_including({
                  'type' => 'Undo',
                  'object' => hash_including({
@@ -96,7 +96,7 @@ RSpec.describe RemoveStatusService, type: :service do
 
     it 'sends Undo activity to followers' do
       subject.call(status)
-      expect(a_request(:post, 'http://example.com/inbox').with(
+      expect(a_request(:post, hank.inbox_url).with(
                body: hash_including({
                  'type' => 'Undo',
                  'object' => hash_including({
diff --git a/spec/services/report_service_spec.rb b/spec/services/report_service_spec.rb
index d3bcd5d31cb..2a919f6405e 100644
--- a/spec/services/report_service_spec.rb
+++ b/spec/services/report_service_spec.rb
@@ -156,9 +156,8 @@ RSpec.describe ReportService, type: :service do
       -> {  described_class.new.call(source_account, target_account) }
     end
 
-    let!(:other_report) { Fabricate(:report, target_account: target_account) }
-
     before do
+      Fabricate(:report, target_account: target_account)
       ActionMailer::Base.deliveries.clear
       source_account.user.settings['notification_emails.report'] = true
       source_account.user.save
diff --git a/spec/services/resolve_account_service_spec.rb b/spec/services/resolve_account_service_spec.rb
index 9f5fdca2975..3c4b182e298 100644
--- a/spec/services/resolve_account_service_spec.rb
+++ b/spec/services/resolve_account_service_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe ResolveAccountService, type: :service do
       let!(:remote_account) { Fabricate(:account, username: 'foo', domain: 'ap.example.com', protocol: 'activitypub') }
 
       context 'when domain is banned' do
-        let!(:domain_block) { Fabricate(:domain_block, domain: 'ap.example.com', severity: :suspend) }
+        before { Fabricate(:domain_block, domain: 'ap.example.com', severity: :suspend) }
 
         it 'does not return an account' do
           expect(subject.call('foo@ap.example.com', skip_webfinger: true)).to be_nil
@@ -214,6 +214,7 @@ RSpec.describe ResolveAccountService, type: :service do
       expect(account.domain).to eq 'ap.example.com'
       expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox'
       expect(account.uri).to eq 'https://ap.example.com/users/foo'
+      expect(status.reload.account).to eq(account)
     end
   end
 
diff --git a/spec/services/suspend_account_service_spec.rb b/spec/services/suspend_account_service_spec.rb
index c258995b7e4..b309ce511e0 100644
--- a/spec/services/suspend_account_service_spec.rb
+++ b/spec/services/suspend_account_service_spec.rb
@@ -46,9 +46,9 @@ RSpec.describe SuspendAccountService, type: :service do
       let!(:account)         { Fabricate(:account) }
       let!(:remote_follower) { Fabricate(:account, uri: 'https://alice.com', inbox_url: 'https://alice.com/inbox', protocol: :activitypub, domain: 'alice.com') }
       let!(:remote_reporter) { Fabricate(:account, uri: 'https://bob.com', inbox_url: 'https://bob.com/inbox', protocol: :activitypub, domain: 'bob.com') }
-      let!(:report)          { Fabricate(:report, account: remote_reporter, target_account: account) }
 
       before do
+        Fabricate(:report, account: remote_reporter, target_account: account)
         remote_follower.follow!(account)
       end
 
diff --git a/spec/services/unallow_domain_service_spec.rb b/spec/services/unallow_domain_service_spec.rb
index 19d40e7e869..d96e3541875 100644
--- a/spec/services/unallow_domain_service_spec.rb
+++ b/spec/services/unallow_domain_service_spec.rb
@@ -27,6 +27,7 @@ RSpec.describe UnallowDomainService, type: :service do
       end
 
       it 'removes remote accounts from that domain' do
+        expect { already_banned_account.reload }.to raise_error(ActiveRecord::RecordNotFound)
         expect(Account.where(domain: 'evil.org').exists?).to be false
       end
 
diff --git a/spec/services/unsuspend_account_service_spec.rb b/spec/services/unsuspend_account_service_spec.rb
index 2f737c62159..211c39e6c72 100644
--- a/spec/services/unsuspend_account_service_spec.rb
+++ b/spec/services/unsuspend_account_service_spec.rb
@@ -39,9 +39,9 @@ RSpec.describe UnsuspendAccountService, type: :service do
       let!(:account)         { Fabricate(:account) }
       let!(:remote_follower) { Fabricate(:account, uri: 'https://alice.com', inbox_url: 'https://alice.com/inbox', protocol: :activitypub, domain: 'alice.com') }
       let!(:remote_reporter) { Fabricate(:account, uri: 'https://bob.com', inbox_url: 'https://bob.com/inbox', protocol: :activitypub, domain: 'bob.com') }
-      let!(:report)          { Fabricate(:report, account: remote_reporter, target_account: account) }
 
       before do
+        Fabricate(:report, account: remote_reporter, target_account: account)
         remote_follower.follow!(account)
       end
 
diff --git a/spec/workers/scheduler/follow_recommendations_scheduler_spec.rb b/spec/workers/scheduler/follow_recommendations_scheduler_spec.rb
index 18d5260e426..246012e128c 100644
--- a/spec/workers/scheduler/follow_recommendations_scheduler_spec.rb
+++ b/spec/workers/scheduler/follow_recommendations_scheduler_spec.rb
@@ -29,14 +29,12 @@ describe Scheduler::FollowRecommendationsScheduler do
 
       it 'creates recommendations' do
         expect { scheduled_run }.to change(FollowRecommendation, :count).from(0).to(target_accounts.size)
-        expect(redis.zrange('follow_recommendations:en', 0, -1)).to match_array(target_accounts.pluck(:id).map(&:to_s))
       end
     end
 
     context 'when there are no accounts to recommend' do
       it 'does not create follow recommendations' do
         expect { scheduled_run }.to_not change(FollowRecommendation, :count)
-        expect(redis.zrange('follow_recommendations:en', 0, -1)).to be_empty
       end
     end
   end
diff --git a/spec/workers/scheduler/user_cleanup_scheduler_spec.rb b/spec/workers/scheduler/user_cleanup_scheduler_spec.rb
index 9909795008c..8fda246ba81 100644
--- a/spec/workers/scheduler/user_cleanup_scheduler_spec.rb
+++ b/spec/workers/scheduler/user_cleanup_scheduler_spec.rb
@@ -18,12 +18,11 @@ describe Scheduler::UserCleanupScheduler do
       confirmed_user.update!(confirmed_at: 1.day.ago)
     end
 
-    it 'deletes the old unconfirmed user' do
-      expect { subject.perform }.to change { User.exists?(old_unconfirmed_user.id) }.from(true).to(false)
-    end
-
-    it "deletes the old unconfirmed user's account" do
-      expect { subject.perform }.to change { Account.exists?(old_unconfirmed_user.account_id) }.from(true).to(false)
+    it 'deletes the old unconfirmed user, their account, and the moderation note' do
+      expect { subject.perform }
+        .to change { User.exists?(old_unconfirmed_user.id) }.from(true).to(false)
+        .and change { Account.exists?(old_unconfirmed_user.account_id) }.from(true).to(false)
+      expect { moderation_note.reload }.to raise_error(ActiveRecord::RecordNotFound)
     end
 
     it 'does not delete the new unconfirmed user or their account' do
diff --git a/yarn.lock b/yarn.lock
index a40bd34a023..2f3a53dedbc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -14509,8 +14509,8 @@ __metadata:
   linkType: hard
 
 "sass-loader@npm:^10.2.0":
-  version: 10.5.0
-  resolution: "sass-loader@npm:10.5.0"
+  version: 10.5.1
+  resolution: "sass-loader@npm:10.5.1"
   dependencies:
     klona: "npm:^2.0.4"
     loader-utils: "npm:^2.0.0"
@@ -14529,7 +14529,7 @@ __metadata:
       optional: true
     sass:
       optional: true
-  checksum: be5da7784fd21c4f526cc3afaa1a765ba44cdc2f9798ecbac87b296ab44184ac5ba9bbda68a7a86f8cdcb6130acceefeb8912260fca14bdfc97f9dad02658400
+  checksum: 841448d02045b0c65595eab4cb701384b01a2adcb3594beacbb767b0cee5bd9d444027f4fc3a10acef3fe1c7eb6510fccffdee72a20e9877777789a5e349cb49
   languageName: node
   linkType: hard