about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavid Yip <yipdw@member.fsf.org>2017-10-16 01:29:02 -0500
committerDavid Yip <yipdw@member.fsf.org>2017-10-16 01:29:02 -0500
commit6cd5b3bbe5a11fcf25bbefba2803f2ae840f39fc (patch)
tree4bb60f4493fb70cada728a373f74c18b87e8f95d
parentf72ad67a3967230afd63a9e2d84a2a69331c4787 (diff)
parent894da3dcca781e27ce9c5130f1021526ac8a6887 (diff)
Merge remote-tracking branch 'origin/master' into gs-master
-rw-r--r--CODEOWNERS8
-rw-r--r--Gemfile.lock4
-rw-r--r--app/controllers/admin/custom_emojis_controller.rb6
-rw-r--r--app/controllers/auth/registrations_controller.rb2
-rw-r--r--app/controllers/concerns/user_tracking_concern.rb6
-rw-r--r--app/javascript/mastodon/actions/accounts.js5
-rw-r--r--app/javascript/mastodon/components/relative_timestamp.js4
-rw-r--r--app/javascript/mastodon/features/compose/util/counter.js2
-rw-r--r--app/javascript/mastodon/features/home_timeline/index.js14
-rw-r--r--app/javascript/mastodon/locales/bg.json2
-rw-r--r--app/javascript/mastodon/locales/ca.json103
-rw-r--r--app/javascript/mastodon/locales/de.json4
-rw-r--r--app/javascript/mastodon/locales/defaultMessages.json2
-rw-r--r--app/javascript/mastodon/locales/en.json2
-rw-r--r--app/javascript/mastodon/locales/eo.json2
-rw-r--r--app/javascript/mastodon/locales/fa.json20
-rw-r--r--app/javascript/mastodon/locales/fi.json2
-rw-r--r--app/javascript/mastodon/locales/fr.json14
-rw-r--r--app/javascript/mastodon/locales/hu.json2
-rw-r--r--app/javascript/mastodon/locales/nl.json4
-rw-r--r--app/javascript/mastodon/locales/oc.json4
-rw-r--r--app/javascript/mastodon/locales/pt-BR.json28
-rw-r--r--app/javascript/mastodon/locales/th.json2
-rw-r--r--app/javascript/mastodon/reducers/timelines.js9
-rw-r--r--app/javascript/styles/about.scss130
-rw-r--r--app/lib/activitypub/activity/undo.rb3
-rw-r--r--app/lib/feed_manager.rb5
-rw-r--r--app/lib/ostatus/atom_serializer.rb16
-rw-r--r--app/models/notification.rb5
-rw-r--r--app/models/status.rb12
-rw-r--r--app/serializers/activitypub/activity_serializer.rb5
-rw-r--r--app/services/fetch_link_card_service.rb4
-rw-r--r--app/services/fetch_remote_status_service.rb2
-rw-r--r--app/services/send_interaction_service.rb4
-rw-r--r--app/services/subscribe_service.rb3
-rw-r--r--app/services/unsubscribe_service.rb3
-rw-r--r--app/views/admin/custom_emojis/_custom_emoji.html.haml2
-rw-r--r--app/views/tags/show.html.haml28
-rw-r--r--app/workers/activitypub/delivery_worker.rb3
-rw-r--r--app/workers/pubsubhubbub/delivery_worker.rb3
-rw-r--r--config/locales/activerecord.pt-BR.yml13
-rw-r--r--config/locales/ca.yml10
-rw-r--r--config/locales/devise.fr.yml12
-rw-r--r--config/locales/doorkeeper.fr.yml2
-rw-r--r--config/locales/fa.yml55
-rw-r--r--config/locales/fr.yml157
-rw-r--r--config/locales/ja.yml16
-rw-r--r--config/locales/ko.yml29
-rw-r--r--config/locales/nl.yml78
-rw-r--r--config/locales/oc.yml20
-rw-r--r--config/locales/pl.yml4
-rw-r--r--config/locales/pt-BR.yml79
-rw-r--r--config/locales/simple_form.fa.yml3
-rw-r--r--config/locales/simple_form.fr.yml10
-rw-r--r--config/locales/simple_form.ja.yml1
-rw-r--r--config/locales/simple_form.ko.yml17
-rw-r--r--spec/controllers/concerns/user_tracking_concern_spec.rb2
-rw-r--r--spec/lib/activitypub/activity/undo_spec.rb24
-rw-r--r--spec/models/feed_spec.rb40
-rw-r--r--spec/services/reblog_service_spec.rb6
60 files changed, 738 insertions, 319 deletions
diff --git a/CODEOWNERS b/CODEOWNERS
index b7291a40b..32919bd50 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -22,3 +22,11 @@
 /app/views/user_mailer/*.fr.text.erb @aldarone
 /config/locales/*.fr.yml @aldarone
 /config/locales/fr.yml @aldarone
+
+# Dutch
+/app/javascript/mastodon/locales/nl.json @jeroenpraat
+/app/javascript/mastodon/locales/whitelist_nl.json @jeroenpraat
+/app/views/user_mailer/*.nl.html.erb @jeroenpraat
+/app/views/user_mailer/*.nl.text.erb @jeroenpraat
+/config/locales/*.nl.yml @jeroenpraat
+/config/locales/nl.yml @jeroenpraat
diff --git a/Gemfile.lock b/Gemfile.lock
index b95e52b37..14ed0d309 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -193,7 +193,7 @@ GEM
       railties (>= 4.0.1)
     hamster (3.0.0)
       concurrent-ruby (~> 1.0)
-    hashdiff (0.3.6)
+    hashdiff (0.3.7)
     highline (1.7.8)
     hiredis (0.6.1)
     hkdf (0.3.0)
@@ -513,7 +513,7 @@ GEM
     uniform_notifier (1.10.0)
     warden (1.2.7)
       rack (>= 1.0)
-    webmock (3.0.1)
+    webmock (3.1.0)
       addressable (>= 2.3.6)
       crack (>= 0.3.2)
       hashdiff
diff --git a/app/controllers/admin/custom_emojis_controller.rb b/app/controllers/admin/custom_emojis_controller.rb
index ca81f3255..5cce5bce4 100644
--- a/app/controllers/admin/custom_emojis_controller.rb
+++ b/app/controllers/admin/custom_emojis_controller.rb
@@ -31,10 +31,12 @@ module Admin
       emoji = CustomEmoji.new(domain: nil, shortcode: @custom_emoji.shortcode, image: @custom_emoji.image)
 
       if emoji.save
-        redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.copied_msg')
+        flash[:notice] = I18n.t('admin.custom_emojis.copied_msg')
       else
-        redirect_to admin_custom_emojis_path, alert: I18n.t('admin.custom_emojis.copy_failed_msg')
+        flash[:alert] = I18n.t('admin.custom_emojis.copy_failed_msg')
       end
+
+      redirect_to admin_custom_emojis_path(params[:page])
     end
 
     def enable
diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb
index aac3c31ff..223db96ff 100644
--- a/app/controllers/auth/registrations_controller.rb
+++ b/app/controllers/auth/registrations_controller.rb
@@ -6,7 +6,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
   before_action :check_enabled_registrations, only: [:new, :create]
   before_action :configure_sign_up_params, only: [:create]
   before_action :set_sessions, only: [:edit, :update]
-  before_action :set_instance_presenter, only: [:new, :update]
+  before_action :set_instance_presenter, only: [:new, :create, :update]
 
   def destroy
     not_found
diff --git a/app/controllers/concerns/user_tracking_concern.rb b/app/controllers/concerns/user_tracking_concern.rb
index 8a63af95d..8663c3086 100644
--- a/app/controllers/concerns/user_tracking_concern.rb
+++ b/app/controllers/concerns/user_tracking_concern.rb
@@ -7,12 +7,14 @@ module UserTrackingConcern
   UPDATE_SIGN_IN_HOURS = 24
 
   included do
-    before_action :set_user_activity, if: %i(user_signed_in? user_needs_sign_in_update?)
+    before_action :set_user_activity
   end
 
   private
 
   def set_user_activity
+    return unless user_needs_sign_in_update?
+
     # Mark as signed-in today
     current_user.update_tracked_fields!(request)
 
@@ -21,7 +23,7 @@ module UserTrackingConcern
   end
 
   def user_needs_sign_in_update?
-    current_user.current_sign_in_at.nil? || current_user.current_sign_in_at < UPDATE_SIGN_IN_HOURS.hours.ago
+    user_signed_in? && (current_user.current_sign_in_at.nil? || current_user.current_sign_in_at < UPDATE_SIGN_IN_HOURS.hours.ago)
   end
 
   def user_needs_feed_update?
diff --git a/app/javascript/mastodon/actions/accounts.js b/app/javascript/mastodon/actions/accounts.js
index fc47110e3..fbaebf786 100644
--- a/app/javascript/mastodon/actions/accounts.js
+++ b/app/javascript/mastodon/actions/accounts.js
@@ -122,7 +122,7 @@ export function unfollowAccount(id) {
     dispatch(unfollowAccountRequest(id));
 
     api(getState).post(`/api/v1/accounts/${id}/unfollow`).then(response => {
-      dispatch(unfollowAccountSuccess(response.data));
+      dispatch(unfollowAccountSuccess(response.data, getState().get('statuses')));
     }).catch(error => {
       dispatch(unfollowAccountFail(error));
     });
@@ -157,10 +157,11 @@ export function unfollowAccountRequest(id) {
   };
 };
 
-export function unfollowAccountSuccess(relationship) {
+export function unfollowAccountSuccess(relationship, statuses) {
   return {
     type: ACCOUNT_UNFOLLOW_SUCCESS,
     relationship,
+    statuses,
   };
 };
 
diff --git a/app/javascript/mastodon/components/relative_timestamp.js b/app/javascript/mastodon/components/relative_timestamp.js
index 534d83fac..51588e78c 100644
--- a/app/javascript/mastodon/components/relative_timestamp.js
+++ b/app/javascript/mastodon/components/relative_timestamp.js
@@ -94,6 +94,10 @@ export default class RelativeTimestamp extends React.Component {
     this._scheduleNextUpdate(nextProps, nextState);
   }
 
+  componentWillUnmount () {
+    clearTimeout(this._timer);
+  }
+
   _scheduleNextUpdate (props, state) {
     clearTimeout(this._timer);
 
diff --git a/app/javascript/mastodon/features/compose/util/counter.js b/app/javascript/mastodon/features/compose/util/counter.js
index 588a372c6..e6d2487c5 100644
--- a/app/javascript/mastodon/features/compose/util/counter.js
+++ b/app/javascript/mastodon/features/compose/util/counter.js
@@ -5,5 +5,5 @@ const urlPlaceholder = 'xxxxxxxxxxxxxxxxxxxxxxx';
 export function countableText(inputText) {
   return inputText
     .replace(urlRegex, urlPlaceholder)
-    .replace(/(?:^|[^\/\w])@(([a-z0-9_]+)@[a-z0-9\.\-]+)/ig, '@$2');
+    .replace(/(?:^|[^\/\w])@(([a-z0-9_]+)@[a-z0-9\.\-]+[a-z0-9]+)/ig, '@$2');
 };
diff --git a/app/javascript/mastodon/features/home_timeline/index.js b/app/javascript/mastodon/features/home_timeline/index.js
index 8d74783a3..b35347ba6 100644
--- a/app/javascript/mastodon/features/home_timeline/index.js
+++ b/app/javascript/mastodon/features/home_timeline/index.js
@@ -16,7 +16,6 @@ const messages = defineMessages({
 
 const mapStateToProps = state => ({
   hasUnread: state.getIn(['timelines', 'home', 'unread']) > 0,
-  hasFollows: state.getIn(['accounts_counters', state.getIn(['meta', 'me']), 'following_count']) > 0,
 });
 
 @connect(mapStateToProps)
@@ -27,7 +26,6 @@ export default class HomeTimeline extends React.PureComponent {
     dispatch: PropTypes.func.isRequired,
     intl: PropTypes.object.isRequired,
     hasUnread: PropTypes.bool,
-    hasFollows: PropTypes.bool,
     columnId: PropTypes.string,
     multiColumn: PropTypes.bool,
   };
@@ -60,17 +58,9 @@ export default class HomeTimeline extends React.PureComponent {
   }
 
   render () {
-    const { intl, hasUnread, hasFollows, columnId, multiColumn } = this.props;
+    const { intl, hasUnread, columnId, multiColumn } = this.props;
     const pinned = !!columnId;
 
-    let emptyMessage;
-
-    if (hasFollows) {
-      emptyMessage = <FormattedMessage id='empty_column.home.inactivity' defaultMessage='Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.' />;
-    } else {
-      emptyMessage = <FormattedMessage id='empty_column.home' defaultMessage="You aren't following anyone yet. Visit {public} or use search to get started and meet other users." values={{ public: <Link to='/timelines/public'><FormattedMessage id='empty_column.home.public_timeline' defaultMessage='the public timeline' /></Link> }} />;
-    }
-
     return (
       <Column ref={this.setRef} name='home'>
         <ColumnHeader
@@ -91,7 +81,7 @@ export default class HomeTimeline extends React.PureComponent {
           scrollKey={`home_timeline-${columnId}`}
           loadMore={this.handleLoadMore}
           timelineId='home'
-          emptyMessage={emptyMessage}
+          emptyMessage={<FormattedMessage id='empty_column.home' defaultMessage='Your home timeline is empty! Visit {public} or use search to get started and meet other users.' values={{ public: <Link to='/timelines/public'><FormattedMessage id='empty_column.home.public_timeline' defaultMessage='the public timeline' /></Link> }} />}
         />
       </Column>
     );
diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json
index 240e3725e..eeded31b7 100644
--- a/app/javascript/mastodon/locales/bg.json
+++ b/app/javascript/mastodon/locales/bg.json
@@ -81,7 +81,7 @@
   "emoji_button.travel": "Travel & Places",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
-  "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.",
+  "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.",
   "empty_column.home.public_timeline": "the public timeline",
   "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json
index b5051a32d..fe2433591 100644
--- a/app/javascript/mastodon/locales/ca.json
+++ b/app/javascript/mastodon/locales/ca.json
@@ -1,7 +1,7 @@
 {
   "account.block": "Bloquejar @{name}",
   "account.block_domain": "Amagar tot de {domain}",
-  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.disclaimer_full": "La informació següent pot reflectir incompleta el perfil de l'usuari.",
   "account.edit_profile": "Editar perfil",
   "account.follow": "Seguir",
   "account.followers": "Seguidors",
@@ -13,19 +13,19 @@
   "account.posts": "Publicacions",
   "account.report": "Informe @{name}",
   "account.requested": "Esperant aprovació",
-  "account.share": "Share @{name}'s profile",
+  "account.share": "Compartir el perfil de @{name}",
   "account.unblock": "Desbloquejar @{name}",
   "account.unblock_domain": "Mostra {domain}",
   "account.unfollow": "Deixar de seguir",
   "account.unmute": "Treure silenci de @{name}",
-  "account.view_full_profile": "View full profile",
+  "account.view_full_profile": "Veure el perfil complet",
   "boost_modal.combo": "Pots premer {combo} per saltar-te això el proper cop",
-  "bundle_column_error.body": "Something went wrong while loading this component.",
-  "bundle_column_error.retry": "Try again",
-  "bundle_column_error.title": "Network error",
-  "bundle_modal_error.close": "Close",
-  "bundle_modal_error.message": "Something went wrong while loading this component.",
-  "bundle_modal_error.retry": "Try again",
+  "bundle_column_error.body": "S'ha produït un error en carregar aquest component.",
+  "bundle_column_error.retry": "Torna-ho a provar",
+  "bundle_column_error.title": "Error de connexió",
+  "bundle_modal_error.close": "Tanca",
+  "bundle_modal_error.message": "S'ha produït un error en carregar aquest component.",
+  "bundle_modal_error.retry": "Torna-ho a provar",
   "column.blocks": "Usuaris bloquejats",
   "column.community": "Línia de temps local",
   "column.favourites": "Favorits",
@@ -33,15 +33,15 @@
   "column.home": "Inici",
   "column.mutes": "Usuaris silenciats",
   "column.notifications": "Notificacions",
-  "column.pins": "Pinned toot",
+  "column.pins": "Toot fixat",
   "column.public": "Línia de temps federada",
   "column_back_button.label": "Enrere",
-  "column_header.hide_settings": "Hide settings",
-  "column_header.moveLeft_settings": "Move column to the left",
-  "column_header.moveRight_settings": "Move column to the right",
-  "column_header.pin": "Pin",
-  "column_header.show_settings": "Show settings",
-  "column_header.unpin": "Unpin",
+  "column_header.hide_settings": "Amaga la configuració",
+  "column_header.moveLeft_settings": "Mou la columna cap a l'esquerra",
+  "column_header.moveRight_settings": "Mou la columna cap a la dreta",
+  "column_header.pin": "Fixar",
+  "column_header.show_settings": "Mostra la configuració",
+  "column_header.unpin": "Deslligar",
   "column_subheading.navigation": "Navegació",
   "column_subheading.settings": "Configuració",
   "compose_form.lock_disclaimer": "El teu compte no està bloquejat {locked}. Tothom pot seguir-te i veure els teus missatges a seguidors.",
@@ -61,22 +61,22 @@
   "confirmations.domain_block.message": "Estàs realment, realment segur que vols bloquejar totalment {domain}? En la majoria dels casos bloquejar o silenciar és suficient i preferible.",
   "confirmations.mute.confirm": "Silenciar",
   "confirmations.mute.message": "Estàs segur que vols silenciar {name}?",
-  "confirmations.unfollow.confirm": "Unfollow",
-  "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
-  "embed.instructions": "Embed this status on your website by copying the code below.",
-  "embed.preview": "Here is what it will look like:",
+  "confirmations.unfollow.confirm": "Deixar de seguir",
+  "confirmations.unfollow.message": "Estàs segur que vols deixar de seguir {name}?",
+  "embed.instructions": "Incrusta aquest estat al lloc web copiant el codi a continuació.",
+  "embed.preview": "A continuació s'explica com:",
   "emoji_button.activity": "Activitat",
-  "emoji_button.custom": "Custom",
+  "emoji_button.custom": "Personalitzat",
   "emoji_button.flags": "Flags",
   "emoji_button.food": "Menjar i Beure",
   "emoji_button.label": "Inserir emoji",
   "emoji_button.nature": "Natura",
-  "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
+  "emoji_button.not_found": "Emojos no!! (╯°□°)╯︵ ┻━┻",
   "emoji_button.objects": "Objectes",
   "emoji_button.people": "Gent",
-  "emoji_button.recent": "Frequently used",
+  "emoji_button.recent": "Freqüentment utilitzat",
   "emoji_button.search": "Cercar...",
-  "emoji_button.search_results": "Search results",
+  "emoji_button.search_results": "Resultats de la cerca",
   "emoji_button.symbols": "Símbols",
   "emoji_button.travel": "Viatges i Llocs",
   "empty_column.community": "La línia de temps local és buida. Escriu alguna cosa públicament per fer rodar la pilota!",
@@ -88,7 +88,7 @@
   "empty_column.public": "No hi ha res aquí! Escriu alguna cosa públicament o segueix manualment usuaris d'altres instàncies per omplir-ho",
   "follow_request.authorize": "Autoritzar",
   "follow_request.reject": "Rebutjar",
-  "getting_started.appsshort": "Apps",
+  "getting_started.appsshort": "Aplicacions",
   "getting_started.faq": "PMF",
   "getting_started.heading": "Començant",
   "getting_started.open_source_notice": "Mastodon és un programari de codi obert. Pots contribuir o informar de problemes a GitHub de {github}.",
@@ -100,8 +100,8 @@
   "home.column_settings.show_replies": "Mostrar respostes",
   "home.settings": "Ajustos de columna",
   "lightbox.close": "Tancar",
-  "lightbox.next": "Next",
-  "lightbox.previous": "Previous",
+  "lightbox.next": "Següent",
+  "lightbox.previous": "Anterior",
   "loading_indicator.label": "Carregant...",
   "media_gallery.toggle_visible": "Alternar visibilitat",
   "missing_indicator.label": "No trobat",
@@ -113,7 +113,7 @@
   "navigation_bar.info": "Informació addicional",
   "navigation_bar.logout": "Tancar sessió",
   "navigation_bar.mutes": "Usuaris silenciats",
-  "navigation_bar.pins": "Pinned toots",
+  "navigation_bar.pins": "Toots fixats",
   "navigation_bar.preferences": "Preferències",
   "navigation_bar.public_timeline": "Línia de temps federada",
   "notification.favourite": "{name} ha afavorit el teu estat",
@@ -126,8 +126,8 @@
   "notifications.column_settings.favourite": "Favorits:",
   "notifications.column_settings.follow": "Nous seguidors:",
   "notifications.column_settings.mention": "Mencions:",
-  "notifications.column_settings.push": "Push notifications",
-  "notifications.column_settings.push_meta": "This device",
+  "notifications.column_settings.push": "Push notificacions",
+  "notifications.column_settings.push_meta": "Aquest dispositiu",
   "notifications.column_settings.reblog": "Boosts:",
   "notifications.column_settings.show": "Mostrar en la columna",
   "notifications.column_settings.sound": "Reproduïr so",
@@ -160,28 +160,33 @@
   "privacy.public.short": "Públic",
   "privacy.unlisted.long": "No publicar en línies de temps públiques",
   "privacy.unlisted.short": "No llistat",
+  "relative_time.days": "{number}d",
+  "relative_time.hours": "{number}h",
+  "relative_time.just_now": "now",
+  "relative_time.minutes": "{number}m",
+  "relative_time.seconds": "{number}s",
   "reply_indicator.cancel": "Cancel·lar",
   "report.placeholder": "Comentaris addicionals",
   "report.submit": "Enviar",
   "report.target": "Informes",
   "search.placeholder": "Cercar",
-  "search_popout.search_format": "Advanced search format",
+  "search_popout.search_format": "Format de cerca avançada",
   "search_popout.tips.hashtag": "hashtag",
   "search_popout.tips.status": "status",
-  "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
-  "search_popout.tips.user": "user",
-  "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
-  "standalone.public_title": "A look inside...",
+  "search_popout.tips.text": "El text simple retorna coincidències amb els noms de visualització, els noms d'usuari i els hashtags",
+  "search_popout.tips.user": "usuari",
+  "search_results.total": "{count, number} {count, plural, un {result} altres {results}}",
+  "standalone.public_title": "Una mirada a l'interior ...",
   "status.cannot_reblog": "Aquesta publicació no pot ser retootejada",
   "status.delete": "Esborrar",
-  "status.embed": "Embed",
+  "status.embed": "Incrustar",
   "status.favourite": "Favorit",
   "status.load_more": "Carrega més",
   "status.media_hidden": "Multimèdia amagat",
   "status.mention": "Esmentar @{name}",
   "status.mute_conversation": "Silenciar conversació",
   "status.open": "Ampliar aquest estat",
-  "status.pin": "Pin on profile",
+  "status.pin": "Fixat en el perfil",
   "status.reblog": "Boost",
   "status.reblogged_by": "{name} ha retootejat",
   "status.reply": "Respondre",
@@ -189,11 +194,11 @@
   "status.report": "Informar sobre @{name}",
   "status.sensitive_toggle": "Clic per veure",
   "status.sensitive_warning": "Contingut sensible",
-  "status.share": "Share",
+  "status.share": "Compartir",
   "status.show_less": "Mostra menys",
   "status.show_more": "Mostra més",
   "status.unmute_conversation": "Activar conversació",
-  "status.unpin": "Unpin from profile",
+  "status.unpin": "Deslliga del perfil",
   "tabs_bar.compose": "Compondre",
   "tabs_bar.federated_timeline": "Federada",
   "tabs_bar.home": "Inici",
@@ -201,16 +206,16 @@
   "tabs_bar.notifications": "Notificacions",
   "upload_area.title": "Arrossega i deixa anar per carregar",
   "upload_button.label": "Afegir multimèdia",
-  "upload_form.description": "Describe for the visually impaired",
+  "upload_form.description": "Descriure els problemes visuals",
   "upload_form.undo": "Desfer",
   "upload_progress.label": "Pujant...",
-  "video.close": "Close video",
-  "video.exit_fullscreen": "Exit full screen",
-  "video.expand": "Expand video",
-  "video.fullscreen": "Full screen",
-  "video.hide": "Hide video",
-  "video.mute": "Mute sound",
-  "video.pause": "Pause",
-  "video.play": "Play",
-  "video.unmute": "Unmute sound"
+  "video.close": "Tancar el vídeo",
+  "video.exit_fullscreen": "Surt de pantalla completa",
+  "video.expand": "Ampliar el vídeo",
+  "video.fullscreen": "Pantalla completa",
+  "video.hide": "Amaga vídeo",
+  "video.mute": "Silenciar el so",
+  "video.pause": "Pausa",
+  "video.play": "Reproduir",
+  "video.unmute": "Activar so"
 }
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index b79b1b2f0..9d9853236 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -81,7 +81,7 @@
   "emoji_button.travel": "Reisen und Orte",
   "empty_column.community": "Die lokale Zeitleiste ist leer. Schreibe einen öffentlichen Beitrag, um den Ball ins Rollen zu bringen!",
   "empty_column.hashtag": "Unter diesem Hashtag gibt es noch nichts.",
-  "empty_column.home": "Du folgst noch niemandem. Besuche {public} oder nutze die Suche, um loszulegen und andere Leute zu finden.",
+  "empty_column.home": "Deine Startseite ist leer! Besuche {public} oder nutze die Suche, um loszulegen und andere Leute zu finden.",
   "empty_column.home.inactivity": "Deine Zeitleiste ist leer. Falls du eine längere Zeit inaktiv warst, wird sie für dich so schnell wie möglich neu erstellt.",
   "empty_column.home.public_timeline": "die öffentliche Zeitleiste",
   "empty_column.notifications": "Du hast noch keine Mitteilungen. Interagiere mit anderen, um ins Gespräch zu kommen.",
@@ -143,7 +143,7 @@
   "onboarding.page_six.almost_done": "Fast fertig …",
   "onboarding.page_six.appetoot": "Guten Appetröt!",
   "onboarding.page_six.apps_available": "Es gibt verschiedene {apps} für iOS, Android und weitere Plattformen.",
-  "onboarding.page_six.github": "Mastodon ist freie, quelloffene Software. Du kannst auf GitHub unter {github} dazu beitragen, Probleme melden und Wünsche äußern.",
+  "onboarding.page_six.github": "Mastodon ist freie, quelloffene Software. Du kannst auf {github} dazu beitragen, Probleme melden und Wünsche äußern.",
   "onboarding.page_six.guidelines": "Richtlinien",
   "onboarding.page_six.read_guidelines": "Bitte mach dich mit den {guidelines} von {domain} vertraut.",
   "onboarding.page_six.various_app": "Apps",
diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json
index 8fdb8c44c..99ff3b35b 100644
--- a/app/javascript/mastodon/locales/defaultMessages.json
+++ b/app/javascript/mastodon/locales/defaultMessages.json
@@ -912,7 +912,7 @@
         "id": "empty_column.home.inactivity"
       },
       {
-        "defaultMessage": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.",
+        "defaultMessage": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
         "id": "empty_column.home"
       },
       {
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index b0dbc46bd..12efe0e0c 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -81,7 +81,7 @@
   "emoji_button.travel": "Travel & Places",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
-  "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.",
+  "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.",
   "empty_column.home.public_timeline": "the public timeline",
   "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
index 1ccd2b817..8f90bdf78 100644
--- a/app/javascript/mastodon/locales/eo.json
+++ b/app/javascript/mastodon/locales/eo.json
@@ -81,7 +81,7 @@
   "emoji_button.travel": "Travel & Places",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
-  "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.",
+  "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.",
   "empty_column.home.public_timeline": "the public timeline",
   "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json
index 13fb91278..9df0dec42 100644
--- a/app/javascript/mastodon/locales/fa.json
+++ b/app/javascript/mastodon/locales/fa.json
@@ -66,17 +66,17 @@
   "embed.instructions": "برای جاگذاری این نوشته در سایت خودتان، کد زیر را کپی کنید.",
   "embed.preview": "نوشتهٔ جاگذاری‌شده این گونه به نظر خواهد رسید:",
   "emoji_button.activity": "فعالیت",
-  "emoji_button.custom": "Custom",
+  "emoji_button.custom": "سفارشی",
   "emoji_button.flags": "پرچم‌ها",
   "emoji_button.food": "غذا و نوشیدنی",
   "emoji_button.label": "افزودن شکلک",
   "emoji_button.nature": "طبیعت",
-  "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
+  "emoji_button.not_found": "این‌جا شکلکی نیست!! (╯°□°)╯︵ ┻━┻",
   "emoji_button.objects": "اشیا",
   "emoji_button.people": "مردم",
-  "emoji_button.recent": "Frequently used",
+  "emoji_button.recent": "زیاد به‌کاررفته",
   "emoji_button.search": "جستجو...",
-  "emoji_button.search_results": "Search results",
+  "emoji_button.search_results": "نتایج جستجو",
   "emoji_button.symbols": "نمادها",
   "emoji_button.travel": "سفر و مکان",
   "empty_column.community": "فهرست نوشته‌های محلی خالی است. چیزی بنویسید تا چرخش بچرخد!",
@@ -165,11 +165,11 @@
   "report.submit": "بفرست",
   "report.target": "گزارش‌دادن",
   "search.placeholder": "جستجو",
-  "search_popout.search_format": "Advanced search format",
-  "search_popout.tips.hashtag": "hashtag",
-  "search_popout.tips.status": "status",
-  "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
-  "search_popout.tips.user": "user",
+  "search_popout.search_format": "راهنمای جستجوی پیشرفته",
+  "search_popout.tips.hashtag": "هشتگ",
+  "search_popout.tips.status": "نوشته",
+  "search_popout.tips.text": "جستجوی متنی ساده برای نام‌ها، نام‌های کاربری، و هشتگ‌ها",
+  "search_popout.tips.user": "کاربر",
   "search_results.total": "{count, number} {count, plural, one {نتیجه} other {نتیجه}}",
   "standalone.public_title": "نگاهی به کاربران این سرور...",
   "status.cannot_reblog": "این نوشته را نمی‌شود بازبوقید",
@@ -201,7 +201,7 @@
   "tabs_bar.notifications": "اعلان‌ها",
   "upload_area.title": "برای بارگذاری به این‌جا بکشید",
   "upload_button.label": "افزودن تصویر",
-  "upload_form.description": "Describe for the visually impaired",
+  "upload_form.description": "نوشتهٔ توضیحی برای کم‌بینایان و نابینایان",
   "upload_form.undo": "واگردانی",
   "upload_progress.label": "بارگذاری...",
   "video.close": "بستن ویدیو",
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
index 425b3d82a..0f6554595 100644
--- a/app/javascript/mastodon/locales/fi.json
+++ b/app/javascript/mastodon/locales/fi.json
@@ -81,7 +81,7 @@
   "emoji_button.travel": "Travel & Places",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
-  "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.",
+  "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.",
   "empty_column.home.public_timeline": "the public timeline",
   "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json
index 4eca47b60..145b683f3 100644
--- a/app/javascript/mastodon/locales/fr.json
+++ b/app/javascript/mastodon/locales/fr.json
@@ -54,13 +54,13 @@
   "compose_form.spoiler_placeholder": "Écrivez ici votre avertissement",
   "confirmation_modal.cancel": "Annuler",
   "confirmations.block.confirm": "Bloquer",
-  "confirmations.block.message": "Confirmez vous le blocage de {name} ?",
+  "confirmations.block.message": "Confirmez-vous le blocage de {name} ?",
   "confirmations.delete.confirm": "Supprimer",
-  "confirmations.delete.message": "Confirmez vous la suppression de ce pouet ?",
+  "confirmations.delete.message": "Confirmez-vous la suppression de ce pouet ?",
   "confirmations.domain_block.confirm": "Masquer le domaine entier",
   "confirmations.domain_block.message": "Êtes-vous vraiment, vraiment sûr⋅e de vouloir bloquer {domain} en entier ? Dans la plupart des cas, quelques blocages ou masquages ciblés sont suffisants et préférables.",
   "confirmations.mute.confirm": "Masquer",
-  "confirmations.mute.message": "Confirmez vous le masquage de {name} ?",
+  "confirmations.mute.message": "Confirmez-vous le masquage de {name} ?",
   "confirmations.unfollow.confirm": "Ne plus suivre",
   "confirmations.unfollow.message": "Voulez-vous arrêter de suivre {name} ?",
   "embed.instructions": "Intégrez ce statut à votre site en copiant ce code ci-dessous.",
@@ -69,7 +69,7 @@
   "emoji_button.custom": "Personnalisés",
   "emoji_button.flags": "Drapeaux",
   "emoji_button.food": "Boire et manger",
-  "emoji_button.label": "Insérer un emoji",
+  "emoji_button.label": "Insérer un émoji",
   "emoji_button.nature": "Nature",
   "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
   "emoji_button.objects": "Objets",
@@ -79,7 +79,7 @@
   "emoji_button.search_results": "Résultats de la recherche",
   "emoji_button.symbols": "Symboles",
   "emoji_button.travel": "Lieux et voyages",
-  "empty_column.community": "Le fil public local est vide. Écrivez-donc quelque chose pour le remplir !",
+  "empty_column.community": "Le fil public local est vide. Écrivez donc quelque chose pour le remplir !",
   "empty_column.hashtag": "Il n’y a encore aucun contenu associé à ce hashtag",
   "empty_column.home": "Vous ne suivez encore personne. Visitez {public} ou bien utilisez la recherche pour vous connecter à d’autres utilisateur⋅ice⋅s.",
   "empty_column.home.inactivity": "Votre accueil est vide. Si vous ne vous êtes pas connecté⋅e depuis un moment, il se remplira automatiquement très bientôt.",
@@ -124,7 +124,7 @@
   "notifications.clear_confirmation": "Voulez-vous vraiment supprimer toutes vos notifications ?",
   "notifications.column_settings.alert": "Notifications locales",
   "notifications.column_settings.favourite": "Favoris :",
-  "notifications.column_settings.follow": "Nouveaux⋅elles abonn⋅é⋅s :",
+  "notifications.column_settings.follow": "Nouveaux⋅elles abonné⋅e⋅s :",
   "notifications.column_settings.mention": "Mentions :",
   "notifications.column_settings.push": "Notifications push",
   "notifications.column_settings.push_meta": "Cet appareil",
@@ -139,7 +139,7 @@
   "onboarding.page_one.federation": "Mastodon est un réseau social qui appartient à tou⋅te⋅s.",
   "onboarding.page_one.handle": "Vous êtes sur {domain}, une des nombreuses instances indépendantes de Mastodon. Votre nom d’utilisateur⋅ice complet est {handle}",
   "onboarding.page_one.welcome": "Bienvenue sur Mastodon !",
-  "onboarding.page_six.admin": "L’administrateur⋅trice de votre instance est {admin}",
+  "onboarding.page_six.admin": "L’administrateur⋅ice de votre instance est {admin}",
   "onboarding.page_six.almost_done": "Nous y sommes presque…",
   "onboarding.page_six.appetoot": "Bon appouétit !",
   "onboarding.page_six.apps_available": "De nombreuses {apps} sont disponibles pour iOS, Android et autres. Et maintenant… Bon appouétit !",
diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json
index 7b9c1b293..2296ea71e 100644
--- a/app/javascript/mastodon/locales/hu.json
+++ b/app/javascript/mastodon/locales/hu.json
@@ -81,7 +81,7 @@
   "emoji_button.travel": "Travel & Places",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
-  "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.",
+  "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.",
   "empty_column.home.public_timeline": "the public timeline",
   "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json
index a3e237f6b..04b88da34 100644
--- a/app/javascript/mastodon/locales/nl.json
+++ b/app/javascript/mastodon/locales/nl.json
@@ -151,8 +151,8 @@
   "onboarding.page_three.search": "Gebruik de zoekbalk linksboven om andere mensen op Mastodon te vinden en om te zoeken op hashtags, zoals {illustration} en {introductions}. Om iemand te vinden die niet op deze Mastodon-server zit, moet je het volledige Mastodon-adres van deze persoon invoeren.",
   "onboarding.page_two.compose": "Schrijf berichten (wij noemen dit toots) in het tekstvak in de linkerkolom. Je kan met de pictogrammen daaronder afbeeldingen uploaden, privacy-instellingen veranderen en je tekst een waarschuwing meegeven.",
   "onboarding.skip": "Overslaan",
-  "privacy.change": "Privacy toot aanpassen",
-  "privacy.direct.long": "Toot alleen naar vermelde gebruikers",
+  "privacy.change": "Zichtbaarheid toot aanpassen",
+  "privacy.direct.long": "Alleen aan vermelde gebruikers tonen",
   "privacy.direct.short": "Direct",
   "privacy.private.long": "Alleen aan volgers tonen",
   "privacy.private.short": "Alleen volgers",
diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json
index d730b47f4..4715f60ef 100644
--- a/app/javascript/mastodon/locales/oc.json
+++ b/app/javascript/mastodon/locales/oc.json
@@ -81,7 +81,7 @@
   "emoji_button.travel": "Viatges & lòcs",
   "empty_column.community": "Lo flux public local es void. Escrivètz quicòm per lo garnir !",
   "empty_column.hashtag": "I a pas encara de contengut ligat a aqueste hashtag",
-  "empty_column.home": "Pel moment seguètz pas degun. Visitatz {public} o utilizatz la recèrca per vos connectar a d’autras personas.",
+  "empty_column.home": "Vòstre flux d’acuèlh es void. Visitatz {public} o utilizatz la recèrca per vos connectar a d’autras personas.",
   "empty_column.home.inactivity": "Vòstra pagina d’acuèlh es voida. Se sètz estat inactiu per un moment, serà tornada generar per vos dins una estona.",
   "empty_column.home.public_timeline": "lo flux public",
   "empty_column.notifications": "Avètz pas encara de notificacions. Respondètz a qualqu’un per començar una conversacion.",
@@ -201,7 +201,7 @@
   "tabs_bar.notifications": "Notificacions",
   "upload_area.title": "Lisatz e depausatz per mandar",
   "upload_button.label": "Ajustar un mèdia",
-  "upload_form.description": "Descripcion se per cas i aja un problèma",
+  "upload_form.description": "Descripcion pels mal vesents",
   "upload_form.undo": "Anullar",
   "upload_progress.label": "Mandadís…",
   "video.close": "Tampar la vidèo",
diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json
index 61674b37e..2c79a7509 100644
--- a/app/javascript/mastodon/locales/pt-BR.json
+++ b/app/javascript/mastodon/locales/pt-BR.json
@@ -45,7 +45,7 @@
   "column_subheading.navigation": "Navegação",
   "column_subheading.settings": "Configurações",
   "compose_form.lock_disclaimer": "A sua conta não está {locked}. Qualquer pessoa pode te seguir e visualizar postagens direcionadas a apenas seguidores.",
-  "compose_form.lock_disclaimer.lock": "trancado",
+  "compose_form.lock_disclaimer.lock": "trancada",
   "compose_form.placeholder": "No que você está pensando?",
   "compose_form.publish": "Publicar",
   "compose_form.publish_loud": "{publish}!",
@@ -66,17 +66,17 @@
   "embed.instructions": "Incorpore esta postagem em seu site copiando o código abaixo:",
   "embed.preview": "Aqui está uma previsão de como ficará:",
   "emoji_button.activity": "Atividades",
-  "emoji_button.custom": "Custom",
+  "emoji_button.custom": "Customizados",
   "emoji_button.flags": "Bandeiras",
   "emoji_button.food": "Comidas & Bebidas",
   "emoji_button.label": "Inserir Emoji",
   "emoji_button.nature": "Natureza",
-  "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
+  "emoji_button.not_found": "Não tem emojos! (╯°□°)╯︵ ┻━┻",
   "emoji_button.objects": "Objetos",
   "emoji_button.people": "Pessoas",
-  "emoji_button.recent": "Frequently used",
+  "emoji_button.recent": "Usados frequentemente",
   "emoji_button.search": "Buscar...",
-  "emoji_button.search_results": "Search results",
+  "emoji_button.search_results": "Resultados da busca",
   "emoji_button.symbols": "Símbolos",
   "emoji_button.travel": "Viagens & Lugares",
   "empty_column.community": "A timeline local está vazia. Escreva algo publicamente para começar!",
@@ -148,7 +148,7 @@
   "onboarding.page_six.read_guidelines": "Por favor, leia as {guidelines} do {domain}!",
   "onboarding.page_six.various_app": "aplicativos móveis",
   "onboarding.page_three.profile": "Edite o seu perfil para mudar o seu o seu avatar, bio e nome de exibição. No menu de configurações, você também encontrará outras preferências.",
-  "onboarding.page_three.search": "Use a barra de buscas para encontrar pessoas e consultar hashtahs, como #illustrations e #introductions. Para procurar por uma pessoa que não estiver nesta instância, use o nome de usuário completo dela.",
+  "onboarding.page_three.search": "Use a barra de buscas para encontrar pessoas e consultar hashtags, como #illustrations e #introductions. Para procurar por uma pessoa que não estiver nesta instância, use o nome de usuário completo dela.",
   "onboarding.page_two.compose": "Escreva postagens na coluna de escrita. Você pode hospedar imagens, mudar as configurações de privacidade e adicionar alertas de conteúdo através dos ícones abaixo.",
   "onboarding.skip": "Pular",
   "privacy.change": "Ajustar a privacidade da mensagem",
@@ -165,15 +165,15 @@
   "report.submit": "Enviar",
   "report.target": "Denunciar",
   "search.placeholder": "Pesquisar",
-  "search_popout.search_format": "Advanced search format",
-  "search_popout.tips.hashtag": "hashtag",
-  "search_popout.tips.status": "status",
-  "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
+"search_popout.search_format": "Advanced search format",
+"search_popout.tips.hashtag": "hashtag",
+"search_popout.tips.status": "status",
+  "search_popout.tips.text": "Texto simples retorna nomes de exibição, usuários e hashtags correspondentes",
   "search_popout.tips.user": "user",
   "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}",
   "standalone.public_title": "Dê uma espiada...",
   "status.cannot_reblog": "Esta postagem não pode ser compartilhada",
-  "status.delete": "Eliminar",
+  "status.delete": "Excluir",
   "status.embed": "Incorporar",
   "status.favourite": "Adicionar aos favoritos",
   "status.load_more": "Carregar mais",
@@ -201,15 +201,15 @@
   "tabs_bar.notifications": "Notificações",
   "upload_area.title": "Arraste e solte para enviar",
   "upload_button.label": "Adicionar mídia",
-  "upload_form.description": "Describe for the visually impaired",
-  "upload_form.undo": "Anular",
+  "upload_form.description": "Descreva a imagem para deficientes visuais",
+  "upload_form.undo": "Desfazer",
   "upload_progress.label": "Salvando...",
   "video.close": "Fechar vídeo",
   "video.exit_fullscreen": "Sair da tela cheia",
   "video.expand": "Expandir vídeo",
   "video.fullscreen": "Tela cheia",
   "video.hide": "Esconder vídeo",
-  "video.mute": "Silenciar vídeo",
+  "video.mute": "Silenciar",
   "video.pause": "Parar",
   "video.play": "Reproduzir",
   "video.unmute": "Retirar silêncio"
diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json
index f3ec9c532..4339d1497 100644
--- a/app/javascript/mastodon/locales/th.json
+++ b/app/javascript/mastodon/locales/th.json
@@ -81,7 +81,7 @@
   "emoji_button.travel": "Travel & Places",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
-  "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.",
+  "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
   "empty_column.home.inactivity": "Your home feed is empty. If you have been inactive for a while, it will be regenerated for you soon.",
   "empty_column.home.public_timeline": "the public timeline",
   "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
diff --git a/app/javascript/mastodon/reducers/timelines.js b/app/javascript/mastodon/reducers/timelines.js
index 065e89f96..b17d74ef3 100644
--- a/app/javascript/mastodon/reducers/timelines.js
+++ b/app/javascript/mastodon/reducers/timelines.js
@@ -14,6 +14,7 @@ import {
 import {
   ACCOUNT_BLOCK_SUCCESS,
   ACCOUNT_MUTE_SUCCESS,
+  ACCOUNT_UNFOLLOW_SUCCESS,
 } from '../actions/accounts';
 import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
 
@@ -108,6 +109,12 @@ const filterTimelines = (state, relationship, statuses) => {
   return state;
 };
 
+const filterTimeline = (timeline, state, relationship, statuses) =>
+  state.updateIn([timeline, 'items'], ImmutableList(), list =>
+    list.filterNot(statusId =>
+      statuses.getIn([statusId, 'account']) === relationship.id
+    ));
+
 const updateTop = (state, timeline, top) => {
   return state.update(timeline, initialTimeline, map => map.withMutations(mMap => {
     if (top) mMap.set('unread', 0);
@@ -134,6 +141,8 @@ export default function timelines(state = initialState, action) {
   case ACCOUNT_BLOCK_SUCCESS:
   case ACCOUNT_MUTE_SUCCESS:
     return filterTimelines(state, action.relationship, action.statuses);
+  case ACCOUNT_UNFOLLOW_SUCCESS:
+    return filterTimeline('home', state, action.relationship, action.statuses);
   case TIMELINE_SCROLL_TOP:
     return updateTop(state, action.timeline, action.top);
   case TIMELINE_CONNECT:
diff --git a/app/javascript/styles/about.scss b/app/javascript/styles/about.scss
index 8c37310e8..4ec689427 100644
--- a/app/javascript/styles/about.scss
+++ b/app/javascript/styles/about.scss
@@ -702,7 +702,6 @@
     .features #mastodon-timeline {
       height: 70vh;
       width: 100%;
-      min-width: 330px;
       margin-bottom: 50px;
 
       .column {
@@ -716,85 +715,96 @@
   }
 
   &.tag-page {
-    .brand {
-      padding-top: 20px;
-      margin-bottom: 20px;
+    .features {
+      padding: 30px 0;
 
-      img {
-        height: 48px;
-        width: auto;
-      }
-    }
+      .container {
+        max-width: 820px;
 
-    .container {
-      max-width: 690px;
-    }
+        #mastodon-timeline {
+          margin-right: 0;
+          border-top-right-radius: 0;
+        }
 
-    .cta {
-      margin: 40px 0;
-      margin-bottom: 80px;
+        .about-mastodon {
+          .about-hashtag {
+            background: darken($ui-base-color, 4%);
+            padding: 0 20px 20px 30px;
+            border-radius: 0 5px 5px 0;
 
-      .button {
-        margin-right: 4px;
-      }
-    }
+            .brand {
+              padding-top: 20px;
+              margin-bottom: 20px;
 
-    .about-mastodon {
-      max-width: 330px;
+              img {
+                height: 48px;
+                width: auto;
+              }
+            }
 
-      p {
-        strong {
-          color: $ui-secondary-color;
-          font-weight: 700;
+            p {
+              strong {
+                color: $ui-secondary-color;
+                font-weight: 700;
+              }
+            }
+
+            .cta {
+              margin: 0;
+
+              .button {
+                margin-right: 4px;
+              }
+            }
+          }
+
+          .features-list {
+            margin-left: 30px;
+            margin-right: 10px;
+          }
         }
       }
     }
 
     @media screen and (max-width: 675px) {
-      .container {
-        display: flex;
-        flex-direction: column;
-      }
-
       .features {
-        padding: 20px 0;
-      }
+        padding: 10px 0;
 
-      .about-mastodon {
-        order: 1;
-        flex: 0 0 auto;
-        max-width: 100%;
-      }
+        .container {
+          display: flex;
+          flex-direction: column;
 
-      #mastodon-timeline {
-        order: 2;
-        flex: 0 0 auto;
-        height: 60vh;
-      }
+          #mastodon-timeline {
+            order: 2;
+            flex: 0 0 auto;
+            height: 60vh;
+            margin-bottom: 20px;
+            border-top-right-radius: 4px;
+          }
 
-      .cta {
-        margin: 20px 0;
-        margin-bottom: 30px;
-      }
+          .about-mastodon {
+            order: 1;
+            flex: 0 0 auto;
+            max-width: 100%;
 
-      .features-list {
-        display: none;
-      }
+            .about-hashtag {
+              background: unset;
+              padding: 0;
+              border-radius: 0;
+
+              .cta {
+                margin: 20px 0;
+              }
+            }
 
-      .stripe {
-        display: none;
+            .features-list {
+              display: none;
+            }
+          }
+        }
       }
     }
   }
-
-  .stripe {
-    width: 100%;
-    height: 360px;
-    overflow: hidden;
-    background: darken($ui-base-color, 4%);
-    position: absolute;
-    z-index: -1;
-  }
 }
 
 @keyframes floating {
diff --git a/app/lib/activitypub/activity/undo.rb b/app/lib/activitypub/activity/undo.rb
index 4b0905de2..cbed417c4 100644
--- a/app/lib/activitypub/activity/undo.rb
+++ b/app/lib/activitypub/activity/undo.rb
@@ -17,7 +17,8 @@ class ActivityPub::Activity::Undo < ActivityPub::Activity
   private
 
   def undo_announce
-    status = Status.find_by(uri: object_uri, account: @account)
+    status   = Status.find_by(uri: object_uri, account: @account)
+    status ||= Status.find_by(uri: @object['atomUri'], account: @account) if @object.is_a?(Hash) && @object['atomUri'].present?
 
     if status.nil?
       delete_later!(object_uri)
diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb
index f6a694135..5a3af7206 100644
--- a/app/lib/feed_manager.rb
+++ b/app/lib/feed_manager.rb
@@ -85,7 +85,7 @@ class FeedManager
     oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true)&.first&.last&.to_i || 0
 
     from_account.statuses.select('id, reblog_of_id').where('id > ?', oldest_home_score).reorder(nil).find_each do |status|
-      unpush(:home, into_account, status)
+      remove_from_feed(:home, into_account, status)
     end
   end
 
@@ -121,7 +121,8 @@ class FeedManager
     #return true if reggie === status.content || reggie === status.spoiler_text
     # extremely violent filtering code END
 
-    return true if status.reply? && (status.in_reply_to_id.nil? || status.in_reply_to_account_id.nil?)
+    return false if receiver_id == status.account_id
+    return true  if status.reply? && (status.in_reply_to_id.nil? || status.in_reply_to_account_id.nil?)
 
     check_for_mutes = [status.account_id]
     check_for_mutes.concat([status.reblog.account_id]) if status.reblog?
diff --git a/app/lib/ostatus/atom_serializer.rb b/app/lib/ostatus/atom_serializer.rb
index a1ac11a51..3ca6c5943 100644
--- a/app/lib/ostatus/atom_serializer.rb
+++ b/app/lib/ostatus/atom_serializer.rb
@@ -23,7 +23,7 @@ class OStatus::AtomSerializer
     append_element(author, 'name', account.username)
     append_element(author, 'email', account.local? ? account.local_username_and_domain : account.acct)
     append_element(author, 'summary', Formatter.instance.simplified_format(account).to_str, type: :html) if account.note?
-    append_element(author, 'link', nil, rel: :alternate, type: 'text/html', href: TagManager.instance.url_for(account))
+    append_element(author, 'link', nil, rel: :alternate, type: 'text/html', href: ::TagManager.instance.url_for(account))
     append_element(author, 'link', nil, rel: :avatar, type: account.avatar_content_type, 'media:width': 120, 'media:height': 120, href: full_asset_url(account.avatar.url(:original))) if account.avatar?
     append_element(author, 'link', nil, rel: :header, type: account.header_content_type, 'media:width': 700, 'media:height': 335, href: full_asset_url(account.header.url(:original))) if account.header?
     append_element(author, 'poco:preferredUsername', account.username)
@@ -47,7 +47,7 @@ class OStatus::AtomSerializer
 
     feed << author(account)
 
-    append_element(feed, 'link', nil, rel: :alternate, type: 'text/html', href: TagManager.instance.url_for(account))
+    append_element(feed, 'link', nil, rel: :alternate, type: 'text/html', href: ::TagManager.instance.url_for(account))
     append_element(feed, 'link', nil, rel: :self, type: 'application/atom+xml', href: account_url(account, format: 'atom'))
     append_element(feed, 'link', nil, rel: :next, type: 'application/atom+xml', href: account_url(account, format: 'atom', max_id: stream_entries.last.id)) if stream_entries.size == 20
     append_element(feed, 'link', nil, rel: :hub, href: api_push_url)
@@ -86,9 +86,9 @@ class OStatus::AtomSerializer
       serialize_status_attributes(entry, stream_entry.status)
     end
 
-    append_element(entry, 'link', nil, rel: :alternate, type: 'text/html', href: TagManager.instance.url_for(stream_entry.status))
+    append_element(entry, 'link', nil, rel: :alternate, type: 'text/html', href: ::TagManager.instance.url_for(stream_entry.status))
     append_element(entry, 'link', nil, rel: :self, type: 'application/atom+xml', href: account_stream_entry_url(stream_entry.account, stream_entry, format: 'atom'))
-    append_element(entry, 'thr:in-reply-to', nil, ref: OStatus::TagManager.instance.uri_for(stream_entry.thread), href: TagManager.instance.url_for(stream_entry.thread)) if stream_entry.threaded?
+    append_element(entry, 'thr:in-reply-to', nil, ref: OStatus::TagManager.instance.uri_for(stream_entry.thread), href: ::TagManager.instance.url_for(stream_entry.thread)) if stream_entry.threaded?
     append_element(entry, 'ostatus:conversation', nil, ref: conversation_uri(stream_entry.status.conversation)) unless stream_entry&.status&.conversation_id.nil?
 
     entry
@@ -109,8 +109,8 @@ class OStatus::AtomSerializer
 
     serialize_status_attributes(object, status)
 
-    append_element(object, 'link', nil, rel: :alternate, type: 'text/html', href: TagManager.instance.url_for(status))
-    append_element(object, 'thr:in-reply-to', nil, ref: OStatus::TagManager.instance.uri_for(status.thread), href: TagManager.instance.url_for(status.thread)) unless status.thread.nil?
+    append_element(object, 'link', nil, rel: :alternate, type: 'text/html', href: ::TagManager.instance.url_for(status))
+    append_element(object, 'thr:in-reply-to', nil, ref: OStatus::TagManager.instance.uri_for(status.thread), href: ::TagManager.instance.url_for(status.thread)) unless status.thread.nil?
     append_element(object, 'ostatus:conversation', nil, ref: conversation_uri(status.conversation)) unless status.conversation_id.nil?
 
     object
@@ -290,7 +290,7 @@ class OStatus::AtomSerializer
 
     entry << object(favourite.status)
 
-    append_element(entry, 'thr:in-reply-to', nil, ref: OStatus::TagManager.instance.uri_for(favourite.status), href: TagManager.instance.url_for(favourite.status))
+    append_element(entry, 'thr:in-reply-to', nil, ref: OStatus::TagManager.instance.uri_for(favourite.status), href: ::TagManager.instance.url_for(favourite.status))
 
     entry
   end
@@ -312,7 +312,7 @@ class OStatus::AtomSerializer
 
     entry << object(favourite.status)
 
-    append_element(entry, 'thr:in-reply-to', nil, ref: OStatus::TagManager.instance.uri_for(favourite.status), href: TagManager.instance.url_for(favourite.status))
+    append_element(entry, 'thr:in-reply-to', nil, ref: OStatus::TagManager.instance.uri_for(favourite.status), href: ::TagManager.instance.url_for(favourite.status))
 
     entry
   end
diff --git a/app/models/notification.rb b/app/models/notification.rb
index 1e64d1ae9..0a5d987cf 100644
--- a/app/models/notification.rb
+++ b/app/models/notification.rb
@@ -68,7 +68,10 @@ class Notification < ApplicationRecord
   class << self
     def reload_stale_associations!(cached_items)
       account_ids = cached_items.map(&:from_account_id).uniq
-      accounts    = Account.where(id: account_ids).map { |a| [a.id, a] }.to_h
+
+      return if account_ids.empty?
+
+      accounts = Account.where(id: account_ids).map { |a| [a.id, a] }.to_h
 
       cached_items.each do |item|
         item.from_account = accounts[item.from_account_id]
diff --git a/app/models/status.rb b/app/models/status.rb
index 107ccface..30d53f298 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -134,7 +134,7 @@ class Status < ApplicationRecord
     CustomEmoji.from_text([spoiler_text, text].join(' '), account.domain)
   end
 
-  after_create :store_uri, if: :local?
+  after_create_commit :store_uri, if: :local?
 
   around_create Mastodon::Snowflake::Callbacks
 
@@ -194,7 +194,11 @@ class Status < ApplicationRecord
         account_ids << item.reblog.account_id if item.reblog?
       end
 
-      accounts = Account.where(id: account_ids.uniq).map { |a| [a.id, a] }.to_h
+      account_ids.uniq!
+
+      return if account_ids.empty?
+
+      accounts = Account.where(id: account_ids).map { |a| [a.id, a] }.to_h
 
       cached_items.each do |item|
         item.account = accounts[item.account_id]
@@ -216,9 +220,7 @@ class Status < ApplicationRecord
         # non-followers can see everything that isn't private/direct, but can see stuff they are mentioned in.
         visibility.push(:private) if account.following?(target_account)
 
-        joins("LEFT OUTER JOIN mentions ON statuses.id = mentions.status_id AND mentions.account_id = #{account.id}")
-          .where(arel_table[:visibility].in(visibility).or(Mention.arel_table[:id].not_eq(nil)))
-          .order(visibility: :desc)
+        where(visibility: visibility).or(where(id: account.mentions.select(:status_id)))
       end
     end
 
diff --git a/app/serializers/activitypub/activity_serializer.rb b/app/serializers/activitypub/activity_serializer.rb
index df399211c..50c4f6a04 100644
--- a/app/serializers/activitypub/activity_serializer.rb
+++ b/app/serializers/activitypub/activity_serializer.rb
@@ -5,6 +5,7 @@ class ActivityPub::ActivitySerializer < ActiveModel::Serializer
 
   has_one :proper, key: :object, serializer: ActivityPub::NoteSerializer, unless: :announce?
   attribute :proper_uri, key: :object, if: :announce?
+  attribute :atom_uri, if: :announce?
 
   def id
     ActivityPub::TagManager.instance.activity_uri_for(object)
@@ -34,6 +35,10 @@ class ActivityPub::ActivitySerializer < ActiveModel::Serializer
     ActivityPub::TagManager.instance.uri_for(object.proper)
   end
 
+  def atom_uri
+    OStatus::TagManager.instance.uri_for(object)
+  end
+
   def announce?
     object.reblog?
   end
diff --git a/app/services/fetch_link_card_service.rb b/app/services/fetch_link_card_service.rb
index cf3d78683..7029c4d75 100644
--- a/app/services/fetch_link_card_service.rb
+++ b/app/services/fetch_link_card_service.rb
@@ -72,6 +72,8 @@ class FetchLinkCardService < BaseService
   def attempt_oembed
     response = OEmbed::Providers.get(@url)
 
+    return false unless response.respond_to?(:type)
+
     @card.type          = response.type
     @card.title         = response.respond_to?(:title)         ? response.title         : ''
     @card.author_name   = response.respond_to?(:author_name)   ? response.author_name   : ''
@@ -113,7 +115,7 @@ class FetchLinkCardService < BaseService
     detector.strip_tags = true
 
     guess = detector.detect(html, response.charset)
-    page  = Nokogiri::HTML(html, nil, guess&.fetch(:encoding))
+    page  = Nokogiri::HTML(html, nil, guess&.fetch(:encoding, nil))
 
     if meta_property(page, 'twitter:player')
       @card.type   = :video
diff --git a/app/services/fetch_remote_status_service.rb b/app/services/fetch_remote_status_service.rb
index cacf6ba51..9c009335b 100644
--- a/app/services/fetch_remote_status_service.rb
+++ b/app/services/fetch_remote_status_service.rb
@@ -40,6 +40,6 @@ class FetchRemoteStatusService < BaseService
   end
 
   def confirmed_domain?(domain, account)
-    account.domain.nil? || domain.casecmp(account.domain).zero? || domain.casecmp(Addressable::URI.parse(account.remote_url).normalized_host).zero?
+    account.domain.nil? || domain.casecmp(account.domain).zero? || domain.casecmp(Addressable::URI.parse(account.remote_url || account.uri).normalized_host).zero?
   end
 end
diff --git a/app/services/send_interaction_service.rb b/app/services/send_interaction_service.rb
index af205c9c9..fabba8a3e 100644
--- a/app/services/send_interaction_service.rb
+++ b/app/services/send_interaction_service.rb
@@ -12,9 +12,11 @@ class SendInteractionService < BaseService
 
     return if !target_account.ostatus? || block_notification?
 
-    delivery = build_request.perform.flush
+    delivery = build_request.perform
 
     raise Mastodon::UnexpectedResponseError, delivery unless delivery.code > 199 && delivery.code < 300
+
+    delivery.connection&.close
   end
 
   private
diff --git a/app/services/subscribe_service.rb b/app/services/subscribe_service.rb
index 2d8af0203..2f725e2ec 100644
--- a/app/services/subscribe_service.rb
+++ b/app/services/subscribe_service.rb
@@ -6,7 +6,7 @@ class SubscribeService < BaseService
 
     @account        = account
     @account.secret = SecureRandom.hex
-    @response       = build_request.perform.flush
+    @response       = build_request.perform
 
     if response_failed_permanently?
       # We're not allowed to subscribe. Fail and move on.
@@ -20,6 +20,7 @@ class SubscribeService < BaseService
       # We need to retry at a later time. Fail loudly!
       raise Mastodon::UnexpectedResponseError, @response
     end
+    @response.connection&.close
   end
 
   private
diff --git a/app/services/unsubscribe_service.rb b/app/services/unsubscribe_service.rb
index d84a5a530..01f5c6b7a 100644
--- a/app/services/unsubscribe_service.rb
+++ b/app/services/unsubscribe_service.rb
@@ -7,9 +7,10 @@ class UnsubscribeService < BaseService
     @account = account
 
     begin
-      @response = build_request.perform.flush
+      @response = build_request.perform
 
       Rails.logger.debug "PuSH unsubscribe for #{@account.acct} failed: #{@response.status}" unless @response.status.success?
+      @response.connection&.close
     rescue HTTP::Error, OpenSSL::SSL::SSLError => e
       Rails.logger.debug "PuSH unsubscribe for #{@account.acct} failed: #{e}"
     end
diff --git a/app/views/admin/custom_emojis/_custom_emoji.html.haml b/app/views/admin/custom_emojis/_custom_emoji.html.haml
index 53263c43f..1fa64908c 100644
--- a/app/views/admin/custom_emojis/_custom_emoji.html.haml
+++ b/app/views/admin/custom_emojis/_custom_emoji.html.haml
@@ -10,7 +10,7 @@
       = custom_emoji.domain
   %td
     - unless custom_emoji.local?
-      = table_link_to 'copy', t('admin.custom_emojis.copy'), copy_admin_custom_emoji_path(custom_emoji), method: :post
+      = table_link_to 'copy', t('admin.custom_emojis.copy'), copy_admin_custom_emoji_path(custom_emoji, page: params[:page]), method: :post
   %td
     - if custom_emoji.disabled?
       = table_link_to 'power-off', t('admin.custom_emojis.enable'), enable_admin_custom_emoji_path(custom_emoji), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }
diff --git a/app/views/tags/show.html.haml b/app/views/tags/show.html.haml
index 6266d3c0c..ea8b0faa3 100644
--- a/app/views/tags/show.html.haml
+++ b/app/views/tags/show.html.haml
@@ -7,31 +7,43 @@
   = render 'og'
 
 .landing-page.tag-page
-  .stripe
   .features
     .container
       #mastodon-timeline{ data: { props: Oj.dump(default_props.merge(hashtag: @tag.name)) } }
 
       .about-mastodon
-        .brand
-          = link_to root_url do
-            = image_tag asset_pack_path('logo_full.svg'), alt: 'Mastodon'
+        .about-hashtag
+          .brand
+            = link_to root_url do
+              = image_tag asset_pack_path('logo_full.svg'), alt: 'Mastodon'
 
-        %p= t 'about.about_hashtag_html', hashtag: @tag.name
+          %p= t 'about.about_hashtag_html', hashtag: @tag.name
 
-        .cta
-          = link_to t('auth.login'), new_user_session_path, class: 'button button-secondary'
-          = link_to t('about.learn_more'), root_url, class: 'button button-alternative'
+          .cta
+            = link_to t('auth.login'), new_user_session_path, class: 'button button-secondary'
+            = link_to t('about.learn_more'), root_url, class: 'button button-alternative'
 
         .features-list
           .features-list__row
             .text
+              %h6= t 'about.features.real_conversation_title'
+              = t 'about.features.real_conversation_body'
+            .visual
+              = fa_icon 'fw comments'
+          .features-list__row
+            .text
               %h6= t 'about.features.not_a_product_title'
               = t 'about.features.not_a_product_body'
             .visual
               = fa_icon 'fw users'
           .features-list__row
             .text
+              %h6= t 'about.features.within_reach_title'
+              = t 'about.features.within_reach_body'
+            .visual
+              = fa_icon 'fw mobile'
+          .features-list__row
+            .text
               %h6= t 'about.features.humane_approach_title'
               = t 'about.features.humane_approach_body'
             .visual
diff --git a/app/workers/activitypub/delivery_worker.rb b/app/workers/activitypub/delivery_worker.rb
index 7b1e06a70..ae86e3dd2 100644
--- a/app/workers/activitypub/delivery_worker.rb
+++ b/app/workers/activitypub/delivery_worker.rb
@@ -16,6 +16,7 @@ class ActivityPub::DeliveryWorker
 
     raise Mastodon::UnexpectedResponseError, @response unless response_successful?
 
+    @response.connection&.close
     failure_tracker.track_success!
   rescue => e
     failure_tracker.track_failure!
@@ -31,7 +32,7 @@ class ActivityPub::DeliveryWorker
   end
 
   def perform_request
-    @response = build_request.perform.flush
+    @response = build_request.perform
   end
 
   def response_successful?
diff --git a/app/workers/pubsubhubbub/delivery_worker.rb b/app/workers/pubsubhubbub/delivery_worker.rb
index c3506727b..a9174edd2 100644
--- a/app/workers/pubsubhubbub/delivery_worker.rb
+++ b/app/workers/pubsubhubbub/delivery_worker.rb
@@ -27,6 +27,7 @@ class Pubsubhubbub::DeliveryWorker
 
     raise Mastodon::UnexpectedResponseError, payload_delivery unless response_successful?
 
+    payload_delivery.connection&.close
     subscription.touch(:last_successful_delivery_at)
   end
 
@@ -37,7 +38,7 @@ class Pubsubhubbub::DeliveryWorker
   def callback_post_payload
     request = Request.new(:post, subscription.callback_url, body: payload)
     request.add_headers(headers)
-    request.perform.flush
+    request.perform
   end
 
   def blocked_domain?
diff --git a/config/locales/activerecord.pt-BR.yml b/config/locales/activerecord.pt-BR.yml
new file mode 100644
index 000000000..d2519fe90
--- /dev/null
+++ b/config/locales/activerecord.pt-BR.yml
@@ -0,0 +1,13 @@
+---
+pt-BR:
+  activerecord:
+    errors:
+      models:
+        account:
+          attributes:
+            username:
+              invalid: apenas letras, números e underscores
+        status:
+          attributes:
+            reblog:
+              taken: do status já existe
diff --git a/config/locales/ca.yml b/config/locales/ca.yml
index 6a92b7f1b..1dc6c492b 100644
--- a/config/locales/ca.yml
+++ b/config/locales/ca.yml
@@ -357,11 +357,11 @@ ca:
     truncate: "&hellip;"
   push_notifications:
     favourite:
-      title: "%{name} favourited your status"
+      title: "%{name} ha afavorit el teu estat"
     follow:
-      title: "%{name} is now following you"
+      title: "%{name} ara et segueix"
     group:
-      title: "%{count} notifications"
+      title: "%{count} notificacions"
     mention:
       action_boost: Boost
       action_expand: Mostra més
@@ -450,13 +450,13 @@ ca:
     description_html: Si habilites la <strong>autenticació de dos factors</strong>, et caldrà tenir el teu telèfon, que generarà tokens per a que puguis iniciar sessió.
     disable: Deshabilitarr
     enable: Habilitar
-    enabled: Two-factor authentication is enabled
+    enabled: Autenticació de dos factors activada
     enabled_success: Autenticació de dos factors activada amb èxit
     generate_recovery_codes: Generar codis de recuperació
     instructions_html: "<strong>Escaneja aquest codi QR desde Google Authenticator o una aplicació similar del teu telèfon</strong>. Desde ara, aquesta aplicació generarà tokens que tens que ingresar quan volguis iniciar sessió."
     lost_recovery_codes: Els codis de recuperació et permeten recuperar l'accés al teu compte si perds el telèfon. Si has perdut els teus codis de recuperació els pots regenerar aquí. Els codis de recuperació anteriors seran anul·lats.
     manual_instructions: 'Si no pots escanejar el codi QR code i necessites introduir-lo manualment, aquí tens el secret en text plà:'
-    recovery_codes: Backup recovery codes
+    recovery_codes: Codis de recuperació de còpia de seguretat
     recovery_codes_regenerated: Codis de recuperació regenerats amb èxit
     recovery_instructions_html: Si alguna vegada perds l'accéss al telèfon pots utilitzar un dels codis de recuperació a continuació per recuperar l'accés al teu compte. Cal mantenir els codis de recuperació en lloc segur, per exemple imprimint-los i guardar-los amb altres documents importants.
     setup: Establir
diff --git a/config/locales/devise.fr.yml b/config/locales/devise.fr.yml
index 237ae8f6f..cb4d5d5f6 100644
--- a/config/locales/devise.fr.yml
+++ b/config/locales/devise.fr.yml
@@ -3,8 +3,8 @@ fr:
   devise:
     confirmations:
       confirmed: Votre compte a été validé.
-      send_instructions: Vous allez recevoir les instructions nécessaires à la confirmation de votre compte dans quelques minutes. S’il vous plaît, dans le cas où vous ne recevriez pas ce message, vérifiez votre dossier d’indésirables.
-      send_paranoid_instructions: Si votre adresse électronique existe dans notre base de données, vous allez bientôt recevoir un courriel contenant les instructions de confirmation de votre compte. S’il vous plaît, dans le cas où vous ne recevriez pas ce message, vérifiez votre dossier d’indésirables.
+      send_instructions: Vous allez recevoir les instructions nécessaires à la confirmation de votre compte dans quelques minutes. Veuillez, dans le cas où vous ne recevriez pas ce message, vérifier votre dossier d’indésirables.
+      send_paranoid_instructions: Si votre adresse électronique existe dans notre base de données, vous allez bientôt recevoir un courriel contenant les instructions de confirmation de votre compte. Veuillez, dans le cas où vous ne recevriez pas ce message, vérifier votre dossier d’indésirables.
     failure:
       already_authenticated: Vous êtes déjà connecté⋅e
       inactive: Votre compte n’est pas encore activé.
@@ -29,8 +29,8 @@ fr:
       success: Authentifié avec succès via %{kind}.
     passwords:
       no_token: Vous ne pouvez accéder à cette page sans passer par un courriel de réinitialisation de mot de passe. Si vous êtes passé⋅e par un courriel de ce type, assurez-vous d’utiliser l’URL complète.
-      send_instructions: Vous allez recevoir les instructions de réinitialisation du mot de passe dans quelques instants. S’il vous plaît, dans le cas où vous ne recevriez pas ce message, vérifiez votre dossier d’indésirables.
-      send_paranoid_instructions: Si votre addresse électronique existe dans notre base de données, vous allez recevoir un lien de réinitialisation par courriel. S’il vous plaît, dans le cas où vous ne recevriez pas ce message, vérifiez votre dossier d’indésirables.
+      send_instructions: Vous allez recevoir les instructions de réinitialisation du mot de passe dans quelques instants. Veuillez, dans le cas où vous ne recevriez pas ce message, vérifier votre dossier d’indésirables.
+      send_paranoid_instructions: Si votre adresse électronique existe dans notre base de données, vous allez recevoir un lien de réinitialisation par courriel. Veuillez, dans le cas où vous ne recevriez pas ce message, vérifier votre dossier d’indésirables.
       updated: Votre mot de passe a été modifié avec succès, vous êtes maintenant connecté⋅e
       updated_not_active: Votre mot de passe a été modifié avec succès.
     registrations:
@@ -46,8 +46,8 @@ fr:
       signed_in: Connecté.
       signed_out: Déconnecté.
     unlocks:
-      send_instructions: Vous allez recevoir les instructions nécessaires au déverrouillage de votre compte dans quelques instants. S’il vous plaît, dans le cas où vous ne recevriez pas ce message, vérifiez votre dossier d’indésirables.
-      send_paranoid_instructions: Si votre compte existe, vous allez bientôt recevoir un courriel contenant les instructions pour le déverrouiller. S’il vous plaît, dans le cas où vous ne recevriez pas ce message, vérifiez votre dossier d’indésirables.
+      send_instructions: Vous allez recevoir les instructions nécessaires au déverrouillage de votre compte dans quelques instants. Veuillez, dans le cas où vous ne recevriez pas ce message, vérifier votre dossier d’indésirables.
+      send_paranoid_instructions: Si votre compte existe, vous allez bientôt recevoir un courriel contenant les instructions pour le déverrouiller. Veuillez, dans le cas où vous ne recevriez pas ce message, vérifier votre dossier d’indésirables.
       unlocked: Votre compte a été déverrouillé avec succès, vous êtes maintenant connecté⋅e.
   errors:
     messages:
diff --git a/config/locales/doorkeeper.fr.yml b/config/locales/doorkeeper.fr.yml
index 88a8ec12f..1dcc0da18 100644
--- a/config/locales/doorkeeper.fr.yml
+++ b/config/locales/doorkeeper.fr.yml
@@ -6,7 +6,7 @@ fr:
         name: Nom
         redirect_uri: L’URL de redirection
         scope: Portée
-        website: Site Web de l'application
+        website: Site web de l'application
     errors:
       models:
         doorkeeper/application:
diff --git a/config/locales/fa.yml b/config/locales/fa.yml
index f7921b1cf..8cb84c7ae 100644
--- a/config/locales/fa.yml
+++ b/config/locales/fa.yml
@@ -2,6 +2,7 @@
 fa:
   about:
     about_mastodon_html: ماستدون (Mastodon) یک شبکهٔ اجتماعی است که بر اساس پروتکل‌های آزاد وب و نرم‌افزارهای آزاد و کدباز ساخته شده است. این شبکه مانند ایمیل غیرمتمرکز است.
+    about_hashtag_html: این‌ها نوشته‌های عمومی هستند که برچسب (هشتگ) <strong>#%{hashtag}</strong> را دارند. اگر شما روی هر سروری حساب داشته باشید می‌توانید به این نوشته‌ها واکنش نشان دهید.
     about_this: درباره
     closed_registrations: ثبت‌نام روی این سرور هم‌اینک فعال نیست. اما شما می‌توانید سرور دیگری بیابید و با حسابی که آن‌جا می‌سازید دقیقاً به همین شبکه دسترسی داشته باشید.
     contact: تماس
@@ -75,6 +76,7 @@ fa:
         silenced: بی‌صدا شده
         suspended: معلق شده
         title: مدیریت
+      moderation_notes: یادداشت مدیر
       most_recent_activity: آخرین فعالیت‌ها
       most_recent_ip: آخرین IP ها
       not_subscribed: عضو نیست
@@ -108,6 +110,34 @@ fa:
       unsubscribe: لغو اشتراک
       username: نام کاربری
       web: وب
+
+    account_moderation_notes:
+      account: مدیر
+      created_at: تاریخ
+      create: نوشتن
+      created_msg: یادداشت مدیر با موفقیت ساخته شد!
+      delete: پاک کردن
+      destroyed_msg: یادداشت مدیر با موفقیت پاک شد!
+
+    custom_emojis:
+      copied_msg: نسخهٔ محلی شکلک با موفقیت ساخته شد
+      copy: نسخه‌برداری
+      copy_failed_msg: نشد که نسخهٔ محلی این شکلک ساخته شود
+      created_msg: این شکلک با موفقیت ساخته شد!
+      delete: پاک کردن
+      destroyed_msg: این شکلک با موفقیت پاک شد!
+      disable: غیرفعال‌سازی
+      disabled_msg: این شکلک با موفقیت غیرفعال شد
+      emoji: شکلک
+      enable: فعال‌سازی
+      enabled_msg: این شکلک با موفقیت فعال شد
+      image_hint: پروندهٔ PNG حداکثر 50KB
+      new:
+        title: افزودن شکلک سفارشی
+      shortcode: کد کوتاه
+      shortcode_hint: دست‌کم ۲ نویسه و تنها شامل حروف، اعداد و زیرخط
+      title: شکلک‌های سفارشی
+      upload: بارگذاری
     domain_blocks:
       add_new: افزودن تازه
       created_msg: مسدودکردن دامین در حال انجام است
@@ -140,9 +170,21 @@ fa:
         undo: واگردانی
       title: دامین‌های مسدودشده
       undo: واگردانی
+    email_domain_blocks:
+      add_new: افزودن تازه
+      created_msg: مسدودسازی دامین ایمیل با موفقیت ساخته شد
+      delete: Delete
+      destroyed_msg: مسدودسازی دامین ایمیل با موفقیت پاک شد
+      domain: دامین
+      new:
+        create: ساختن مسدودسازی
+        title: مسدودسازی دامین ایمیل تازه
+      title: مسدودسازی دامین‌های ایمیل
     instances:
       account_count: حساب‌های شناخته‌شده
       domain_name: دامین
+      reset: بازنشانی
+      search: جستجو
       title: سرورهای شناخته‌شده
     reports:
       action_taken_by: انجام‌دهنده
@@ -294,8 +336,11 @@ fa:
     '410': صفحه‌ای که به دنبالش بودید دیگر وجود ندارد.
     '422':
       content: تأیید امنیتی انجام نشد. آیا مرورگر شما کوکی‌ها را مسدود می‌کند؟
-      title: تأیید امنیتی شکست خورد
+      title: تأیید امنیتی کار نکرد
     '429': درخواست‌های بیش از حد
+    '500':
+      content: شرمنده، یک چیزی از سمت ما اشتباه شده.
+      title: این صفحه درست نیست
     noscript_html: برای استفاده از نسخهٔ تحت وب ماستدون، لطفاً جاوااسکریپت را فعال کنید. یا به جایش می‌توانید یک اپ ماستدون را به‌کار ببرید.
   exports:
     blocks: حساب‌های مسدودشده
@@ -376,6 +421,11 @@ fa:
     next: بعدی
     prev: قبلی
     truncate: "&hellip;"
+  preferences:
+    languages: زبان‌ها
+    other: سایر
+    publishing: انتشار
+    web: وب
   push_notifications:
     favourite:
       title: "%{name} نوشتهٔ شما را پسندید"
@@ -443,6 +493,7 @@ fa:
     export: برون‌سپاری داده‌ها
     followers: پیگیران مورد تأیید
     import: درون‌ریزی
+    notifications: اعلان‌ها
     preferences: ترجیحات
     settings: تنظیمات
     two_factor_authentication: ورود دومرحله‌ای
@@ -538,6 +589,8 @@ fa:
 
       <p>این نوشته اقتباسی است از <a href="https://github.com/discourse/discourse">سیاست رازداری Discourse</a>.</p>
     title: شرایط استفاده و سیاست رازداری %{instance}
+  themes:
+    default: Mastodon
   time:
     formats:
       default: "%d %b %Y, %H:%M"
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index 07df89e9b..712724ffa 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -10,7 +10,7 @@ fr:
     contact_unavailable: Non disponible
     description_headline: Qu’est-ce que %{domain} ?
     domain_count_after: autres instances
-    domain_count_before: Connectés à
+    domain_count_before: Connecté⋅e⋅s à
     extended_description_html: |
       <h3>Un bon endroit pour les règles</h3>
       <p>La description étendue n’a pas été remplie.</p>
@@ -36,7 +36,7 @@ fr:
     what_is_mastodon: Qu’est-ce que Mastodon ?
   accounts:
     follow: Suivre
-    followers: Abonné⋅es
+    followers: Abonné⋅e⋅s
     following: Abonnements
     media: Médias
     nothing_here: Rien à voir ici !
@@ -60,7 +60,7 @@ fr:
       edit: Éditer
       email: Courriel
       feed_url: URL du flux
-      followers: Abonné⋅es
+      followers: Abonné⋅e⋅s
       followers_url: URL des abonné·e·s
       follows: Abonnements
       inbox_url: URL d’entrée
@@ -110,22 +110,24 @@ fr:
       unsubscribe: Se désabonner
       username: Nom d’utilisateur⋅ice
       web: Web
+
     account_moderation_notes:
-      account: Modérateur·rice
+      account: Modérateur·ice
       created_at: Date
       create: Créer
       created_msg: Note de modération créée avec succès !
       delete: Supprimer
       destroyed_msg: Note de modération supprimée avec succès !
+
     custom_emojis:
-      copied_msg: Copie locale de l’émoji créée avec succès
+      copied_msg: Copie locale de l’émoji créée avec succès !
       copy: Copier
       copy_failed_msg: Impossible de faire une copie locale de cet émoji
       created_msg: Émoji créé avec succès !
       delete: Supprimer
-      destroyed_msg: Emojo successfully destroyed!
+      destroyed_msg: Émoji supprimé avec succès !
       disable: Désactiver
-      disabled_msg: Émoji désactivé avec succès
+      disabled_msg: Émoji désactivé avec succès !
       emoji: Émoji
       enable: Activer
       enabled_msg: Émoji activé avec succès
@@ -181,12 +183,12 @@ fr:
     instances:
       account_count: Comptes connus
       domain_name: Domaine
-      reset: Réinitialisation
+      reset: Réinitialiser
       search: Rechercher
       title: Instances connues
     reports:
       action_taken_by: Intervention de
-      are_you_sure: Êtes vous certain⋅e?
+      are_you_sure: Êtes vous certain⋅e ?
       comment:
         label: Commentaire
         none: Aucun
@@ -210,8 +212,8 @@ fr:
       view: Voir
     settings:
       bootstrap_timeline_accounts:
-        desc_html: Séparez les noms d’utilisateur·rice par des virgules. Ne fonctionne qu’avec des comptes locaux et non-verrouillés. Si laissé vide, les administrateur·rice·s locaux·les sont utilisé·e·s.
-        title: Abonnements par défaut pour les nouveaux·elles utilisateur·rice·s
+        desc_html: Séparez les noms d’utilisateur·ice par des virgules. Ne fonctionne qu’avec des comptes locaux et non-verrouillés. Si laissé vide, tous les administrateur⋅ice⋅s locaux sont sélectionné⋅e⋅s.
+        title: Abonnements par défaut pour les nouveaux·elles utilisateur·ice·s
       contact_information:
         email: Entrez une adresse courriel publique
         username: Entrez un nom d’utilisateur⋅ice
@@ -220,7 +222,7 @@ fr:
           desc_html: Affiché sur la page d’accueil lorsque les inscriptions sont fermées<br>Vous pouvez utiliser des balises HTML
           title: Message de fermeture des inscriptions
         deletion:
-          desc_html: Permettre à tou·te·s les utilisateur·rice·s de supprimer leur compte
+          desc_html: Permettre à tou·te·s les utilisateur·ice·s de supprimer leur compte
           title: Autoriser les suppressions de compte
         open:
           desc_html: Autoriser tout le monde à créer un compte
@@ -236,7 +238,7 @@ fr:
         title: Politique de confidentialité
       site_title: Titre du site
       thumbnail:
-        desc_html: Utilisée pour les prévisualisation via OpenGraph et l’API. 1200x630px recommandé
+        desc_html: Utilisée pour les prévisualisations via OpenGraph et l’API. 1200x630px recommandé
         title: Vignette de l’instance
       timeline_preview:
         desc_html: Afficher le fil public sur la page d’accueil
@@ -270,6 +272,7 @@ fr:
       body: "%{reporter} a signalé %{target}"
       subject: Nouveau signalement sur %{instance} (#%{id})
   application_mailer:
+    salutation: "%{name},"
     settings: 'Changer les préférences courriel : %{link}'
     signature: Notifications de Mastodon depuis %{instance}
     view: 'Voir :'
@@ -302,29 +305,23 @@ fr:
     following: 'Youpi ! Vous suivez :'
     post_follow:
       close: Ou bien, vous pouvez fermer cette fenêtre.
-      return: Retour au profil de l’utilisateur⋅trice
+      return: Retour au profil de l’utilisateur⋅ice
       web: Retour à l’interface web
     title: Suivre %{acct}
   datetime:
     distance_in_words:
-      about_x_hours: "%{count}h"
-      about_x_months: "%{count}mois"
-      about_x_years:
-        one: un an
-        other: "%{count} ans"
-      almost_x_years:
-        one: un an
-        other: "%{count} ans"
+      about_x_hours: "%{count} h"
+      about_x_months: "%{count} mois"
+      about_x_years: "%{count} an(s)"
+      almost_x_years: "%{count} an(s)"
       half_a_minute: À l’instant
-      less_than_x_minutes: "%{count}min"
+      less_than_x_minutes: "%{count} min"
       less_than_x_seconds: À l’instant
-      over_x_years:
-        one: un an
-        other: "%{count} ans"
-      x_days: "%{count}j"
-      x_minutes: "%{count}min"
-      x_months: "%{count}mois"
-      x_seconds: "%{count}s"
+      over_x_years: "%{count}an(s)"
+      x_days: "%{count} j"
+      x_minutes: "%{count} min"
+      x_months: "%{count} mois"
+      x_seconds: "%{count} s"
   deletes:
     bad_password_msg: Bien essayé ! Mot de passe incorrect
     confirm_password: Entrez votre mot de passe pour vérifier votre identité
@@ -354,14 +351,14 @@ fr:
   followers:
     domain: Domaine
     explanation_html: Si vous voulez être sûr⋅e que vos statuts restent privés, vous devez savoir qui vous suit. <strong>Vos statuts privés seront diffusés à toutes les instances des utilisateur⋅ice⋅s qui vous suivent</strong>. Vous voudrez peut-être les passer en revue et les supprimer si vous n’êtes pas sûr⋅e que votre vie privée sera respectée par l’administration ou le logiciel de ces instances.
-    followers_count: Nombre d’abonné⋅es
+    followers_count: Nombre d’abonné⋅e⋅s
     lock_link: Rendez votre compte privé
-    purge: Retirer de la liste d’abonné⋅es
+    purge: Retirer de la liste d’abonné⋅e⋅s
     success:
-      one: Suppression des abonné⋅es venant d’un domaine en cours…
-      other: Suppression des abonné⋅es venant de %{count} domaines en cours…
-    true_privacy_html: Soyez conscient⋅es <strong>qu’une vraie confidentialité ne peut être atteinte que par un chiffrement de bout-en-bout</strong>.
-    unlocked_warning_html: N’importe qui peut vous suivre et voir vos statuts privés. %{lock_link} afin de pouvoir vérifier et rejeter des abonné⋅es.
+      one: Suppression des abonné⋅e⋅s venant d’un domaine en cours…
+      other: Suppression des abonné⋅e⋅s venant de %{count} domaines en cours…
+    true_privacy_html: Soyez conscient⋅e⋅s <strong>qu’une vraie confidentialité ne peut être atteinte que par un chiffrement de bout-en-bout</strong>.
+    unlocked_warning_html: N’importe qui peut vous suivre et voir vos statuts privés. %{lock_link} afin de pouvoir vérifier et rejeter des abonné⋅e⋅s.
     unlocked_warning_title: Votre compte n’est pas privé
   generic:
     changes_saved_msg: Les modifications ont été enregistrées avec succès !
@@ -374,11 +371,11 @@ fr:
     preface: Vous pouvez importer certaines données comme les personnes que vous suivez ou bloquez sur votre compte sur cette instance à partir de fichiers créés sur une autre instance.
     success: Vos données ont été importées avec succès et seront traitées en temps et en heure
     types:
-      blocking: Liste d’utilisateur⋅ice⋅s bloqué⋅es
-      following: Liste d’utilisateur⋅ice⋅s suivi⋅es
+      blocking: Liste d’utilisateur⋅ice⋅s bloqué⋅e⋅s
+      following: Liste d’utilisateur⋅ice⋅s suivi⋅e⋅s
       muting: Liste d’utilisateur⋅ice⋅s que vous masquez
     upload: Importer
-  landing_strip_html: <strong>%{name}</strong> utilise %{link_to_root_path}. Vous pouvez le/la suivre et interagir si vous possédez un compte quelque part dans le "fediverse".
+  landing_strip_html: <strong>%{name}</strong> utilise %{link_to_root_path}. Vous pouvez læ suivre et interagir si vous possédez un compte quelque part dans le "fediverse".
   landing_strip_signup_html: Si ce n’est pas le cas, vous pouvez <a href="%{sign_up_path}">en créer un ici</a>.
   media_attachments:
     validations:
@@ -414,15 +411,16 @@ fr:
       decimal_units:
         format: "%n%u"
         units:
-          billion: B
+          billion: G
           million: M
-          quadrillion: Q
+          quadrillion: P
           thousand: K
           trillion: T
           unit: ''
   pagination:
     next: Suivant
     prev: Précédent
+    truncate: "&hellip;"
   preferences:
     languages: Langues
     other: Autre
@@ -430,7 +428,7 @@ fr:
     web: Web
   push_notifications:
     favourite:
-      title: "%{name} à mis votre statut en favori"
+      title: "%{name} a mis votre statut en favori"
     follow:
       title: "%{name} vous suit"
     group:
@@ -510,8 +508,8 @@ fr:
       reblog: Un partage ne peut pas être épinglé
     show_more: Afficher plus
     visibilities:
-      private: Abonné⋅es uniquement
-      private_long: Seul⋅es vos abonné⋅es verront vos statuts
+      private: Abonné⋅e⋅s uniquement
+      private_long: Seul⋅e⋅s vos abonné⋅e⋅s verront vos statuts
       public: Public
       public_long: Tout le monde peut voir vos statuts
       unlisted: Public sans être affiché sur le fil public
@@ -522,7 +520,74 @@ fr:
     reblogged: partagé
     sensitive_content: Contenu sensible
   terms:
-    body_html: "<h2>Politique de confidentialité</h2>\n\n<h3 id=\"collect\">Quelles données collectons-nous ?</h3>\n\n<p>Nous collectons des données lorsque vous vous enregistrez sur notre site et les récoltons lorsque vous participez dans le forum en lisant, écrivant, et évaluant le contenu partagé ici.</p>\n\n<p>Lors de l’enregistrement sur notre site, il peut vous être demandé de renseigner votre nom et adresse électronique. Vous pouvez, cependant, visiter notre site sans inscription. Votre adresse électronique devra être vérifiée grâce à un courriel contenant un lien unique. Si ce lien est visité, nous savons que vous contrôlez cette adresse.</p>\n\n<p>Lors de l’inscription et de la publication de statuts, nous enregistrons l’adresse IP de laquelle les statuts proviennent. Nous pouvons également conserver des historiques serveurs qui contiendront l’adresse IP de chaque requête adressée à notre serveur.</p>\n\n<h3 id=\"use\">Que faisons-nous avec vos données ?</h3>\n\n<p>Toute information que nous collectons pourra être utilisée d’une des manières suivantes :</p>\n\n<ul>\n  <li>Pour personnaliser votre expérience &mdash; vos données nous aident à mieux répondre à vos besoins individuels.</li>\n  <li>Pour améliorer notre site &mdash; nous faisons tout notre possible pour améliorer notre site en fonction des données, retours et suggestions que nous recevons.</li>\n  <li>Afin d’améliorer le support client &mdash; vos données nous aident à mieux répondre à vos requêtes et demandes de support.</li>\n  <li>Afin d’envoyer des courriels à intervalles réguliers &mdash; l’adresse électronique que vous renseignez peut être utilisée pour vous envoyer des données et notifications concernant des changements ou en réponse à votre nom d’utilisateur⋅trice, en réponse à vos demandes et/ou autres requêtes ou questions</li>\n</ul>\n\n<h3 id=\"protect\">Comment protégeons-nous vos données ?</h3>\n  \n<p>Nous appliquons une multitude de mesures afin de maintenir la sécurité de vos données personnelles lorsque vous entrez, soumettez, ou accédez à ces dernières.</p>\n\n<h3 id=\"data-retention\">Quelle est notre politique de conservation des données ?</h3>\n\n<p>Nous nous efforçons de :</p>\n\n<ul>\n  <li>ne pas garder les historiques serveurs contenant l’adresse IP de chaque requête adressée à ce serveur plus de 90 jours ;</li>\n  <li>ne pas conserver les adresses IP associées aux utilisateur⋅trices et leur contenu plus de 5 ans.</li>\n</ul>\n\n<h3 id=\"cookies\">Utilisons nous des « cookies » ?</h3>\n\n<p>Oui. Les cookies sont de petits fichiers qu’un site ou prestataires de services transfèrent sur le disque dur de votre ordinateur par le biais de votre navigateur Web (si ce dernier le permet). Ces cookies permettent au site de reconnaître votre navigateur et, si vous disposez d’un compte, de l’associer à celui-ci.</p>\n\n<p>Nous utilisons les cookies pour enregistrer vos préférences pour de futures visites, compiler des données agrégées à propos du trafic et des interactions effectuées sur le site afin de proposer une meilleure expérience dans le futur. Nous pouvons contracter les services d’acteurs tiers afin de nous aider à mieux comprendre les visiteurs de notre site. Ces acteurs ont l’autorisation d’utiliser ces données seulement à des fins d’améliorations.</p>\n\n<h3 id=\"disclose\">Divulguons-nous des données à des acteurs tiers ?</h3>\n\n<p>Nous n’échangeons pas, ne vendons pas ni effectuons de quelconques transferts avec des acteurs tiers d’informations permettant de vous identifier personnellement. Cela n’inclut pas les acteurs de confiance qui nous aident à gérer notre entreprise et à vous servir tant que ces acteurs s’accordent à garder lesdites informations confidentielles. Nous pouvons être amenés à délivrer vos informations lorsque jugé adéquat afin de respecter la loi, d’appliquer la politique de notre site, ou afin de protéger nos droits, ceux des autres, notre propriété ou sécurité. Cependant, aucune information permettant l’identification de nos visiteurs ne sera divulguée à des fins publicitaires, commerciales ou tout autre usage.</p>\n\n<h3 id=\"third-party\">Liens vers des acteurs tiers</h3>\n\n<p>Nous pouvons être amenés à inclure ou offrir les services ou produits d’acteurs tiers sur notre site. Ces acteurs tiers possèdent leur propre politique de confidentialité. Nous ne sommes donc pas responsables du contenu ou activités desdits acteurs. Néanmoins, nous cherchons à protéger l’intégrité de notre site et sommes ouverts à toute remarque concernant ces acteurs.</p>\n\n<h3 id=\"coppa\" lang=\"en\">Children's Online Privacy Protection Act</h3>\n\n<p>Notre site, nos produits et services sont tous destinés à l’usage de personnes âgées de 13 ans ou plus. Si ce serveur est hébergé aux États-Unis et que vous êtes âgé⋅e de moins de 13 ans, au vu du COPPA (<a href=\"https://fr.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act\"><i lang=\"en\">Children's Online Privacy Protection Act</i></a>) n’utilisez pas ce site.</p>\n\n<h3 id=\"consent\">Votre consentement</h3>\n\n<p>En utilisant notre site, vous consentez à la présente politique de confidentialité.</p>\n\n<h3 id=\"changes\">Changements de notre politique de confidentialité</h3>\n\n<p>Si nous décidons d’apporter des changements à notre politique de confidentialité, nous les publierons sur cette page.</p>\n\n<p>Ce document est distribué sous licence CC-BY-SA. Il a été mis à jour pour la dernière fois le 31 mai 2013. Il a été traduit en français en juillet 2017.</p>\n\n<p>Originellement adapté à partir de la politique de confidentialité de <a href=\"https://github.com/discourse/discourse\">Discourse</a>.</p>\n"
+    body_html: |
+      <h2>Politique de confidentialité</h2>
+
+      <h3 id=\"collect\">Quelles données collectons-nous ?</h3>
+
+      <p>Nous collectons des données lorsque vous vous enregistrez sur notre site et les récoltons lorsque vous participez dans le forum en lisant, écrivant, et évaluant le contenu partagé ici.</p>
+
+      <p>Lors de l’enregistrement sur notre site, il peut vous être demandé de renseigner votre nom et adresse électronique. Vous pouvez, cependant, visiter notre site sans inscription. Votre adresse électronique devra être vérifiée grâce à un courriel contenant un lien unique. Si ce lien est visité, nous savons que vous contrôlez cette adresse.</p>
+
+      <p>Lors de l’inscription et de la publication de statuts, nous enregistrons l’adresse IP de laquelle les statuts proviennent. Nous pouvons également conserver des historiques serveurs qui contiendront l’adresse IP de chaque requête adressée à notre serveur.</p>
+
+      <h3 id=\"use\">Que faisons-nous avec vos données ?</h3>
+
+      <p>Toute information que nous collectons pourra être utilisée d’une des manières suivantes :</p>
+
+      <ul>
+        <li>Pour personnaliser votre expérience &mdash; vos données nous aident à mieux répondre à vos besoins individuels.</li>
+        <li>Pour améliorer notre site &mdash; nous faisons tout notre possible pour améliorer notre site en fonction des données, retours et suggestions que nous recevons.</li>
+        <li>Afin d’améliorer le support client &mdash; vos données nous aident à mieux répondre à vos requêtes et demandes de support.</li>
+        <li>Afin d’envoyer des courriels à intervalles réguliers &mdash; l’adresse électronique que vous renseignez peut être utilisée pour vous envoyer des données et notifications concernant des changements ou en réponse à votre nom d’utilisateur⋅ice, en réponse à vos demandes et/ou autres requêtes ou questions</li>
+      </ul>
+
+      <h3 id=\"protect\">Comment protégeons-nous vos données ?</h3>
+
+      <p>Nous appliquons une multitude de mesures afin de maintenir la sécurité de vos données personnelles lorsque vous entrez, soumettez, ou accédez à ces dernières.</p>
+
+      <h3 id=\"data-retention\">Quelle est notre politique de conservation des données ?</h3>
+
+      <p>Nous nous efforçons de :</p>
+
+      <ul>
+        <li>ne pas garder les historiques serveurs contenant l’adresse IP de chaque requête adressée à ce serveur plus de 90 jours ;</li>
+        <li>ne pas conserver les adresses IP associées aux utilisateur⋅trices et leur contenu plus de 5 ans.</li>
+      </ul>
+
+      <h3 id=\"cookies\">Utilisons-nous des « cookies » ?</h3>
+
+      <p>Oui. Les cookies sont de petits fichiers qu’un site ou prestataires de services transfèrent sur le disque dur de votre ordinateur par le biais de votre navigateur Web (si ce dernier le permet). Ces cookies permettent au site de reconnaître votre navigateur et, si vous disposez d’un compte, de l’associer à celui-ci.</p>
+
+      <p>Nous utilisons les cookies pour enregistrer vos préférences pour de futures visites, compiler des données agrégées à propos du trafic et des interactions effectuées sur le site afin de proposer une meilleure expérience dans le futur. Nous pouvons contracter les services de tiers afin de nous aider à mieux comprendre les visiteurs de notre site. Ces tiers ont l’autorisation d’utiliser ces données seulement à des fins d’améliorations.</p>
+
+      <h3 id=\"disclose\">Divulguons-nous des données à des tiers ?</h3>
+
+      <p>Nous n’échangeons pas, ne vendons pas ni effectuons de quelconques transferts avec des tiers d’informations permettant de vous identifier personnellement. Cela n’inclut pas les tiers de confiance qui nous aident à gérer notre entreprise et à vous servir tant que ces tiers s’accordent à garder lesdites informations confidentielles. Nous pouvons être amenés à délivrer vos informations lorsque jugé adéquat afin de respecter la loi, d’appliquer la politique de notre site, ou afin de protéger nos droits, ceux des autres, notre propriété ou sécurité. Cependant, aucune information permettant l’identification de nos visiteur⋅euse⋅s ne sera divulguée à des fins publicitaires, commerciales ou tout autre usage.</p>
+
+      <h3 id=\"third-party\">Liens vers des tiers</h3>
+
+      <p>Nous pouvons être amenés à inclure ou offrir les services ou produits de tiers sur notre site. Ces tiers possèdent leur propre politique de confidentialité. Nous ne sommes donc pas responsables du contenu ou activités desdits tiers. Néanmoins, nous cherchons à protéger l’intégrité de notre site et sommes ouverts à toute remarque concernant ces tiers.</p>
+
+      <h3 id=\"coppa\" lang=\"en\">Children's Online Privacy Protection Act</h3>
+
+      <p>Notre site, nos produits et services sont tous destinés à l’usage de personnes âgées de 13 ans ou plus. Si ce serveur est hébergé aux États-Unis et que vous êtes âgé⋅e de moins de 13 ans, au vu du COPPA (<a href=\"https://fr.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act\"><i lang=\"en\">Children's Online Privacy Protection Act</i></a>) n’utilisez pas ce site.</p>
+
+      <h3 id="online">Politique de confidentialité en ligne</h3>
+
+      <p>Cette politique de confidentialité en ligne s'applique uniquement aux informations collectées par le biais de notre site et non aux informations collectées hors ligne.</p>
+
+      <h3 id=\"consent\">Votre consentement</h3>
+
+      <p>En utilisant notre site, vous consentez à la présente politique de confidentialité.</p>
+
+      <h3 id=\"changes\">Changements de notre politique de confidentialité</h3>
+
+      <p>Si nous décidons d’apporter des changements à notre politique de confidentialité, nous les publierons sur cette page.</p>
+
+      <p>Ce document est distribué sous licence CC-BY-SA. Il a été mis à jour pour la dernière fois le 31 mai 2013. Il a été traduit en français en juillet 2017.</p>
+
+      <p>Originellement adapté à partir de la politique de confidentialité de <a href=\"https://github.com/discourse/discourse\">Discourse</a>.</p>
     title: "%{instance} Conditions d’utilisations et politique de confidentialité"
   themes:
     default: Mastodon
@@ -537,7 +602,7 @@ fr:
     enabled: L’authentification à deux facteurs est activée
     enabled_success: Identification à deux facteurs activée avec succès
     generate_recovery_codes: Générer les codes de récupération
-    instructions_html: "<strong>Scannez ce QR code grâce à Google Authenticator, Authy ou une application similaire sur votre téléphone</strong>. Désormais, cette application générera des jetons que vous devrez saisir à chaque connexion."
+    instructions_html: "<strong>Scannez ce QR code grâce à Google Authenticator, Authy ou une application similaire sur votre téléphone</strong>. Désormais, cette application génèrera des jetons que vous devrez saisir à chaque connexion."
     lost_recovery_codes: Les codes de récupération vous permettent de retrouver les accès à votre comptre si vous perdez votre téléphone. Si vous perdez vos codes de récupération, vous pouvez les générer à nouveau ici. Vos anciens codes de récupération seront invalidés.
     manual_instructions: 'Si vous ne pouvez pas scanner ce QR code et devez l’entrer manuellement, voici le secret en clair :'
     recovery_codes: Codes de récupération
diff --git a/config/locales/ja.yml b/config/locales/ja.yml
index 3d6f2fd0b..391556040 100644
--- a/config/locales/ja.yml
+++ b/config/locales/ja.yml
@@ -1,6 +1,7 @@
 ---
 ja:
   about:
+    about_hashtag_html: ハッシュタグ <strong>#%{hashtag}</strong> の付いた公開トゥートです。どこでもいいので、連合に参加しているSNS上にアカウントを作れば会話に参加することができます。
     about_mastodon_html: Mastodon は、オープンなウェブプロトコルを採用した、自由でオープンソースなソーシャルネットワークです。電子メールのような分散型の仕組みを採っています。
     about_this: 詳細情報
     closed_registrations: 現在このインスタンスでの新規登録は受け付けていません。しかし、他のインスタンスにアカウントを作成しても全く同じネットワークに参加することができます。
@@ -49,6 +50,13 @@ ja:
       admin: Admin
     unfollow: フォロー解除
   admin:
+    account_moderation_notes:
+      account: モデレータ
+      create: 書き込む
+      created_at: 日付
+      created_msg: モデレーションメモを書き込みました
+      delete: 削除
+      destroyed_msg: モデレーションメモを削除しました
     accounts:
       are_you_sure: 本当に実行しますか?
       confirm: 確認
@@ -75,6 +83,7 @@ ja:
         silenced: サイレンス中
         suspended: 停止中
         title: モデレーション
+      moderation_notes: モデレーションメモ
       most_recent_activity: 直近の活動
       most_recent_ip: 直近のIP
       not_subscribed: 購読していない
@@ -109,10 +118,17 @@ ja:
       username: ユーザー名
       web: Web
     custom_emojis:
+      copied_msg: 絵文字のコピーをローカルに作成しました
+      copy: コピー
+      copy_failed_msg: 絵文字のコピーをローカルに作成できませんでした
       created_msg: 絵文字の追加に成功しました
       delete: 削除
       destroyed_msg: 絵文字の削除に成功しました
+      disable: 無効化
+      disabled_msg: 絵文字を無効化しました
       emoji: 絵文字
+      enable: 有効化
+      enabled_msg: 絵文字を有効化しました
       image_hint: 50KBまでのPNG画像を利用できます。
       new:
         title: 新規カスタム絵文字の追加
diff --git a/config/locales/ko.yml b/config/locales/ko.yml
index a77271b82..35cec6b15 100644
--- a/config/locales/ko.yml
+++ b/config/locales/ko.yml
@@ -1,7 +1,8 @@
 ---
 ko:
   about:
-    about_mastodon_html: Mastodon은 <em>오픈 소스 기반의</em> 소셜 네트워크 서비스 입니다. 상용 플랫폼의 대체로서 <em>분산형 구조</em>를 채택해, 여러분의 대화가 한 회사에 독점되는 것을 방지합니다. 신뢰할 수 있는 인스턴스를 선택하세요 &mdash; 어떤 인스턴스를 고르더라도, 누구와도 대화할 수 있습니다. 누구나 자신만의 Mastodon 인스턴스를 만들 수 있으며, Seamless하게 <em>소셜 네트워크</em>에 참가할 수 있습니다.
+    about_hashtag_html: <strong>#%{hashtag}</strong> 라는 해시태그가 붙은 공개 툿 입니다. 같은 연합에 속한 임의의 인스턴스에 계정을 생성하면 당신도 대화에 참여할 수 있습니다.
+    about_mastodon_html: Mastodon은 <em>오픈 소스 기반의</em> 소셜 네트워크 서비스 입니다. 상용 플랫폼의 대체로서 <em>분산형 구조</em>를 채택해, 여러분의 대화가 한 회사에 독점되는 것을 방지합니다. 신뢰할 수 있는 인스턴스를 선택하세요 &mdash; 어떤 인스턴스를 고르더라도, 누구와도 대화할 수 있습니다. 누구나 자신만의 Mastodon 인스턴스를 만들 수 있으며, 아주 매끄럽게 <em>소셜 네트워크</em>에 참가할 수 있습니다.
     about_this: 이 인스턴스에 대해서
     closed_registrations: 현재 이 인스턴스에서는 신규 등록을 받고 있지 않습니다.
     contact: 연락처
@@ -49,6 +50,13 @@ ko:
       admin: Admin
     unfollow: 팔로우 해제
   admin:
+    account_moderation_notes:
+      account: 모더레이터
+      create: 작성하기
+      created_at: 작성 날짜
+      created_msg: 모더레이션 기록이 성공적으로 작성되었습니다.
+      delete: 삭제
+      destroyed_msg: 모더레이션 기록이 성공적으로 삭제되었습니다.
     accounts:
       are_you_sure: 정말로 실행하시겠습니까?
       confirm: 확인
@@ -75,6 +83,7 @@ ko:
         silenced: 침묵 중
         suspended: 정지 중
         title: 모더레이션
+      moderation_notes: 모더레이션 기록
       most_recent_activity: 최근 활동
       most_recent_ip: 최근 IP
       not_subscribed: 구독하지 않음
@@ -109,13 +118,13 @@ ko:
       username: 아이디
       web: Web
     custom_emojis:
-      copied_msg: 성공적으로 emoji의 로컬 복사본을 생성했습니다
+      copied_msg: 성공적으로 에모지의 로컬 복사본을 생성했습니다
       copy: 복사
-      copy_failed_msg: Emoji의 로컬 복사본을 만드는 데 실패하였습니다
+      copy_failed_msg: 에모지의 로컬 복사본을 만드는 데 실패하였습니다
       created_msg: 에모지가 성공적으로 생성되었습니다!
       delete: 삭제
       destroyed_msg: 에모지가 성공적으로 삭제되었습니다!
-      disable: Disable
+      disable: 비활성화
       disabled_msg: 성공적으로 비활성화하였습니다
       emoji: 에모지
       enable: 활성화
@@ -265,8 +274,8 @@ ko:
     signature: Mastodon %{instance} 인스턴스로에서 알림
     view: 'View:'
   applications:
-    created: 어플리케이션이 작성되었습니다.
-    destroyed: 어플리케이션이 삭제되었습니다.
+    created: 애플리케이션이 작성되었습니다.
+    destroyed: 애플리케이션이 삭제되었습니다.
     invalid_url: 올바르지 않은 URL입니다
     regenerate_token: 토큰 재생성
     token_regenerated: 액세스 토큰이 재생성되었습니다.
@@ -473,7 +482,7 @@ ko:
     revoke_success: 세션이 삭제되었습니다.
     title: 세션
   settings:
-    authorized_apps: 인증된 어플리케이션
+    authorized_apps: 인증된 애플리케이션
     back: 돌아가기
     delete: 계정 삭제
     development: 개발
@@ -500,7 +509,7 @@ ko:
       private_long: 팔로워에게만 공개됩니다
       public: 공개
       public_long: 누구나 볼 수 있으며, 공개 타임라인에 표시됩니다
-      unlisted: Unlisted
+      unlisted: 공개 타임라인 비공개
       unlisted_long: 누구나 볼 수 있지만, 공개 타임라인에는 표시되지 않습니다
   stream_entries:
     click_to_show: 클릭해서 표시
@@ -515,14 +524,14 @@ ko:
     formats:
       default: "%Y년 %m월 %d일 %H:%M"
   two_factor_authentication:
-    code_hint: 확인하기 위해서 인증 어플리케이션에서 표시된 코드를 입력해 주십시오
+    code_hint: 확인하기 위해서 인증 애플리케이션에서 표시된 코드를 입력해 주십시오
     description_html: "<strong>2단계 인증</strong>을 활성화 하면 로그인 시 전화로 인증 코드를 받을 필요가 있습니다."
     disable: 비활성화
     enable: 활성화
     enabled: 2단계 인증이 활성화 되어 있습니다
     enabled_success: 2단계 인증이 활성화 되었습니다
     generate_recovery_codes: 복구 코드 생성
-    instructions_html: "<strong>Google Authenticator, 또는 타 TOTP 어플리케이션에서 이 QR 코드를 스캔해 주십시오.</strong> 이후 로그인 시에는 이 어플리케이션에서 생성되는 코드가 필요합니다."
+    instructions_html: "<strong>Google Authenticator, 또는 타 TOTP 애플리케이션에서 이 QR 코드를 스캔해 주십시오.</strong> 이후 로그인 시에는 이 애플리케이션에서 생성되는 코드가 필요합니다."
     lost_recovery_codes: 복구 코드를 사용하면 휴대전화를 분실한 경우에도 계정에 접근할 수 있게 됩니다. 복구 코드를 분실한 경우에도 여기서 다시 생성할 수 있지만, 예전 복구 코드는 비활성화 됩니다.
     manual_instructions: 'QR 코드를 스캔할 수 없어 수동으로 등록을 원하시는 경우 이 비밀 코드를 사용해 주십시오: '
     recovery_codes: 복구 코드
diff --git a/config/locales/nl.yml b/config/locales/nl.yml
index 06035b6c5..fa66d14ef 100644
--- a/config/locales/nl.yml
+++ b/config/locales/nl.yml
@@ -2,6 +2,7 @@
 nl:
   about:
     about_mastodon_html: Mastodon is een <em>vrij, gratis en open-source</em> sociaal netwerk. Een <em>gedecentraliseerd</em> alternatief voor commerciële platforms. Het voorkomt de risico's van een enkel bedrijf dat jouw communicatie monopoliseert. Kies een server die je vertrouwt &mdash; welke je ook kiest, je kunt met elke andere server communiceren. Iedereen kan een eigen Mastodon-server draaien en naadloos deelnemen in het <em>sociale netwerk</em>.
+    about_hashtag_html: Dit zijn openbare toots die getagged zijn met <strong>#%{hashtag}</strong>. Je kunt er op reageren of iets anders mee doen als je op Mastodon (of ergens anders in de fediverse) een account hebt.
     about_this: Over deze server
     closed_registrations: Registreren op deze server is momenteel uitgeschakeld.
     contact: Contact
@@ -37,6 +38,7 @@ nl:
     follow: Volgen
     followers: Volgers
     following: Volgend
+    media: Media
     nothing_here: Hier is niets!
     people_followed_by: Mensen die %{name} volgt
     people_who_follow: Mensen die %{name} volgen
@@ -74,15 +76,18 @@ nl:
         silenced: Genegeerd
         suspended: Opgeschort
         title: Moderatie
+      moderation_notes: Opmerkingen voor moderatoren
       most_recent_activity: Laatst actief
       most_recent_ip: Laatst gebruikt IP-adres
       not_subscribed: Niet geabonneerd
       order:
         alphabetic: Alfabetisch
         most_recent: Meest recent
-        title: 
+        title: Sorteren
+      outbox_url: Outbox-URL
       perform_full_suspension: Volledig opschorten
       profile_url: Profiel-URL
+      protocol: Protocol
       public: Openbaar
       push_subscription_expires: PuSH-abonnement verloopt op
       redownload: Avatar vernieuwen
@@ -105,6 +110,34 @@ nl:
       unsubscribe: Opzeggen
       username: Gebruikersnaam
       web: Webapp
+
+    account_moderation_notes:
+      account: Moderator
+      created_at: Datum
+      create: Aanmaken
+      created_msg: Aanmaken van opmerking voor moderatoren geslaagd!
+      delete: Verwijderen
+      destroyed_msg: Verwijderen van opmerking voor moderatoren geslaagd!
+
+    custom_emojis:
+      copied_msg: Lokale kopie van emoji maken geslaagd
+      copy: Kopiëren
+      copy_failed_msg: Kan geen lokale kopie van deze emoji maken
+      created_msg: Aanmaken van emoji geslaagd!
+      delete: Verwijderen
+      destroyed_msg: Verwijderen van emoji geslaagd!
+      disable: Uitschakelen
+      disabled_msg: Uitschakelen van deze emoji geslaagd
+      emoji: Emoji
+      enable: Inschakelen
+      enabled_msg: Inschakelen van deze emoji geslaagd
+      image_hint: PNG van max. 50KB
+      new:
+        title: Lokale emoji toevoegen
+      shortcode: Shortcode
+      shortcode_hint: Tenminste 2 tekens (alleen alfanumeriek en underscores)
+      title: Lokale emoji’s
+      upload: Uploaden
     domain_blocks:
       add_new: Nieuwe toevoegen
       created_msg: Domeinblokkade wordt nu verwerkt
@@ -137,9 +170,21 @@ nl:
         undo: Ongedaan maken
       title: Domeinblokkades
       undo: Ongedaan maken
+    email_domain_blocks:
+      add_new: Nieuwe toevoegen
+      created_msg: Blokkeren e-maildomein geslaagd
+      delete: Verwijderen
+      destroyed_msg: Deblokkeren e-maildomein geslaagd
+      domain: Domein
+      new:
+        create: Blokkeren
+        title: Nieuw e-maildomein blokkeren
+      title: E-maildomeinen blokkeren
     instances:
       account_count: Bekende accounts
       domain_name: Domein
+      reset: Opnieuw
+      search: Zoeken
       title: Bekende servers
     reports:
       action_taken_by: Actie uitgevoerd door
@@ -213,6 +258,7 @@ nl:
         title: Media
       no_media: Geen media
       title: Toots van account
+      with_media: Met media
     subscriptions:
       callback_url: Callback-URL
       confirmed: Bevestigd
@@ -226,11 +272,18 @@ nl:
       body: "%{reporter} heeft %{target} gerapporteerd"
       subject: Nieuwe toots gerapporteerd op %{instance} (#%{id})
   application_mailer:
+    salutation: "%{name},"
     settings: 'E-mailvoorkeuren wijzigen: %{link}'
     signature: Mastodon-meldingen van %{instance}
     view: 'Bekijk:'
   applications:
+    created: Aanmaken toepassing geslaagd
+    destroyed: Verwijderen toepassing geslaagd
     invalid_url: De opgegeven URL is ongeldig
+    regenerate_token: Toegangscode opnieuw aanmaken
+    token_regenerated: Opnieuw aanmaken toegangscode geslaagd
+    warning: Wees voorzichtig met deze gegevens. Deel het nooit met iemand anders!
+    your_token: Jouw toegangscode
   auth:
     agreement_html: Wanneer je op registreren klikt ga je akkoord met <a href="%{rules_path}">onze gebruikersvoorwaarden</a> en <a href="%{terms_path}">ons privacybeleid</a>.
     change_password: Beveiliging
@@ -238,6 +291,7 @@ nl:
     delete_account_html: Wanneer je jouw account graag wilt verwijderen, kan je dat <a href="%{path}">hier doen</a>. We vragen jou daar om een bevestiging.
     didnt_get_confirmation: Geen bevestigingsinstructies ontvangen?
     forgot_password: Wachtwoord vergeten?
+    invalid_reset_password_token: De code om jouw wachtwoord opnieuw in te stellen is verlopen. Vraag een nieuwe aan.
     login: Aanmelden
     logout: Afmelden
     register: Registreren
@@ -284,6 +338,9 @@ nl:
       content: Veiligheidsverificatie mislukt. Blokkeer je toevallig cookies?
       title: Veiligheidsverificatie mislukt
     '429': Te veel verbindingsaanvragen.
+    '500':
+      content: Het spijt ons, er is aan onze kant iets fout gegaan.
+      title: Er is iets mis
     noscript_html: Schakel JavaScript in om de webapp van Mastodon te kunnen gebruiken. Als alternatief kan je een <a href="https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md">Mastodon-app</a> zoeken voor jouw platform.
   exports:
     blocks: Jij blokkeert
@@ -318,7 +375,7 @@ nl:
       following: Volglijst
       muting: Negeerlijst
     upload: Uploaden
-  landing_strip_html: "<strong>%{name}</strong> is een gebruiker op %{link_to_root_path}. Je kunt deze volgen en ermee communiceren als je ergens in deze fediverse een account hebt."
+  landing_strip_html: "<strong>%{name}</strong> is een gebruiker op %{link_to_root_path}. Je kunt deze volgen en ermee communiceren als je op Mastodon (of ergens anders in de fediverse) een account hebt."
   landing_strip_signup_html: Als je dat niet hebt, kun je je <a href="%{sign_up_path}">hier registreren</a>.
   media_attachments:
     validations:
@@ -364,11 +421,18 @@ nl:
     next: Volgende
     prev: Vorige
     truncate: "&hellip;"
+  preferences:
+    languages: Talen
+    other: Overig
+    publishing: Publiceren
+    web: Webapp
   push_notifications:
     favourite:
       title: "%{name} markeerde jouw toot als favoriet"
     follow:
       title: "%{name} volgt jou nu"
+    group:
+      title: "%{count} meldingen"
     mention:
       action_boost: Boost
       action_expand: Meer tonen
@@ -423,12 +487,13 @@ nl:
   settings:
     authorized_apps: Geautoriseerde apps
     back: Terug naar Mastodon
-    delete: Account deletion
+    delete: Account verwijderen
     development: Ontwikkelaars
     edit_profile: Profiel bewerken
-    export: Export
+    export: Exporteren
     followers: Geautoriseerde volgers
-    import: Import
+    import: Importeren
+    notifications: Meldingen
     preferences: Voorkeuren
     settings: Instellingen
     two_factor_authentication: Tweestapsverificatie
@@ -524,6 +589,8 @@ nl:
 
       <p>Originally adapted from the <a href="https://github.com/discourse/discourse">Discourse privacy policy</a>.</p>
     title: "%{instance} Terms of Service and Privacy Policy"
+  themes:
+    default: Mastodon
   time:
     formats:
       default: "%d %B %Y om %H:%M"
@@ -546,3 +613,4 @@ nl:
   users:
     invalid_email: E-mailadres is ongeldig
     invalid_otp_token: Ongeldige tweestaps-aanmeldcode
+    signed_in_as: 'Ingelogd als:'
diff --git a/config/locales/oc.yml b/config/locales/oc.yml
index 608ee0a09..2d72d247f 100644
--- a/config/locales/oc.yml
+++ b/config/locales/oc.yml
@@ -66,9 +66,9 @@ oc:
       inbox_url: URL de recepcion
       ip: IP
       location:
-        all: Tot
-        local: Local
-        remote: Alonhat
+        all: Totes
+        local: Locals
+        remote: Alonhats
         title: Emplaçament
       media_attachments: Mèdias ajustats
       moderation:
@@ -76,6 +76,7 @@ oc:
         silenced: Rescondut
         suspended: Suspendut
         title: Moderacion
+      moderation_notes: Nòtas de moderacion
       most_recent_activity: Activitat mai recenta
       most_recent_ip: IP mai recenta
       not_subscribed: Pas seguidor
@@ -109,6 +110,15 @@ oc:
       unsubscribe: Se desabonar
       username: Nom d’utilizaire
       web: Web
+
+    account_moderation_notes:
+      account: Moderator
+      created_at: Data
+      create: Crear
+      created_msg: Nòta de moderacion ben creada !
+      delete: Suprimir
+      destroyed_msg: Nòta de moderacion ben suprimida !
+
     custom_emojis:
       copied_msg: Còpia locale de l’emoji ben creada
       copy: Copiar
@@ -331,8 +341,8 @@ oc:
     - dissabte
     formats:
       default: "%e/%m/%Y"
-      long: Lo %-d %b de %Y
-      short: "%e %B"
+      long: Lo %e %B de %Y
+      short: "%e %b. de %Y"
     month_names:
     - 
     - de genièr
diff --git a/config/locales/pl.yml b/config/locales/pl.yml
index 5176ca88b..8e2a9780c 100644
--- a/config/locales/pl.yml
+++ b/config/locales/pl.yml
@@ -84,8 +84,10 @@ pl:
         alphabetic: Alfabetycznie
         most_recent: Najnowsze
         title: Kolejność
+      outbox_url: Adres skrzynki nadawczej
       perform_full_suspension: Całkowicie zawieś
       profile_url: Adres profilu
+      protocol: Protokół
       public: Publiczne
       push_subscription_expires: Subskrypcja PuSH wygasa
       redownload: Odśwież awatar
@@ -180,6 +182,8 @@ pl:
     instances:
       account_count: Znane konta
       domain_name: Domena
+      reset: Przywróć
+      search: Szukaj
       title: Znane instancje
     reports:
       action_taken_by: Działanie podjęte przez
diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml
index f2b46927b..060fd3112 100644
--- a/config/locales/pt-BR.yml
+++ b/config/locales/pt-BR.yml
@@ -2,6 +2,7 @@
 pt-BR:
   about:
     about_mastodon_html: Mastodon é uma rede social baseada em protocolos abertos e software gratuito e de código aberto. É descentralizada como e-mail.
+    about_hashtag_html: Estes são toots públicos com a hashtag <strong>#%{hashtag}</strong>. Voce pode interagir com eles se tiver uma conta em qualquer lugar no fediverso.
     about_this: Sobre
     closed_registrations: Cadastros estão atualmente fechados nesta instância. No entanto! Você pode procurar uma instância diferente na qual possa criar uma conta e acessar a mesma rede por lá.
     contact: Contato
@@ -10,7 +11,9 @@ pt-BR:
     description_headline: O que é %{domain}?
     domain_count_after: outras instâncias
     domain_count_before: Conectado a
-    extended_description_html: "<h3>Um bom lugar para as regras</h3> <p>A descrição extendida ainda não foi definida.</p>"
+    extended_description_html:
+      <h3>Um bom lugar para regras</h3>
+      <p>A descrição da instância ainda não foi feita.</p>
     features:
       humane_approach_body: Aprendendo com erros de outras redes, Mastodon tem como objetivo fazer decisões éticas de design para combater o desuso de redes sociais.
       humane_approach_title: Uma abordagem mais humana
@@ -56,8 +59,9 @@ pt-BR:
       domain: Domínio
       edit: Editar
       email: E-mail
-      feed_url: Feed URL
+      feed_url: URL do feed
       followers: Seguidores
+      followers_url: URL de seguidores
       follows: Segue
       inbox_url: Inbox URL
       ip: IP
@@ -72,6 +76,7 @@ pt-BR:
         silenced: Silenciados
         suspended: Suspensos
         title: Moderação
+      moderation_notes: Notas de moderação
       most_recent_activity: Atividade mais recente
       most_recent_ip: IP mais recente
       not_subscribed: Não inscrito
@@ -79,7 +84,7 @@ pt-BR:
         alphabetic: Alfabética
         most_recent: Mais recente
         title: Ordem
-      outbox_url: Outbox URL
+      outbox_url: URL da Outbox
       perform_full_suspension: Efetue suspensão total
       profile_url: URL do perfil
       protocol: Protocolo
@@ -91,6 +96,7 @@ pt-BR:
       resubscribe: Reinscrever-se
       salmon_url: Salmon URL
       search: Pesquisar
+      shared_inbox_url: URL da Inbox Compartilhada
       show:
         created_reports: Relatórios criados por esta conta
         report: relatórios
@@ -104,6 +110,34 @@ pt-BR:
       unsubscribe: Desinscrever-se
       username: Nome de usuário
       web: Web
+
+    account_moderation_notes:
+      account: Moderador
+      created_at: Data
+      create: Criar
+      created_msg: Nota de moderação criada com sucesso!
+      delete: Excluir
+      destroyed_msg: Nota de moderação excluída com sucesso!
+
+    custom_emojis:
+      copied_msg: Cópia local do emoji criada com sucesso!
+      copy: Copiar
+      copy_failed_msg: Não foi possível criar uma cópia local deste emoji
+      created_msg: Emoji criado com sucesso!
+      delete: Excluir
+      destroyed_msg: Emoji deletado com sucesso!
+      disable: Desabilitar
+      disabled_msg: Emoji desabilitado com sucesso!
+      emoji: Emoji
+      enable: Habilitar
+      enabled_msg: Emoji habilitado com sucesso!
+      image_hint: PNG de até 50KB
+      new:
+        title: Adicionar novo emoji customizado
+      shortcode: Atalho
+      shortcode_hint: Pelo menos 2 caracteres, apenas caracteres alfanuméricos e underscores
+      title: Emojis customizados
+      upload: Enviar
     domain_blocks:
       add_new: Adicionar novo
       created_msg: Bloqueio de domínio está sendo processado
@@ -136,9 +170,21 @@ pt-BR:
         undo: Retirar
       title: Bloqueios de domínio
       undo: Retirar
+    email_domain_blocks:
+      add_new: Adicionar novo
+      created_msg: Bloqueio de domínio de e-mail criado com sucesso
+      delete: Excluir
+      destroyed_msg: Bloqueio de domínio de e-mail excluído com sucesso
+      domain: Domínio
+      new:
+        create: Criar bloqueio
+        title: Novo bloqueio de domínio de e-mail
+      title: Bloqueio de Domínio de E-mail
     instances:
       account_count: Contas conhecidas
       domain_name: Domínio
+      reset: Resetar
+      search: Buscar
       title: Instâncias conhecidas
     reports:
       action_taken_by: Ação realizada por
@@ -191,6 +237,9 @@ pt-BR:
         desc_html: Você pode escrever a sua própria política de privacidade, termos de serviço, entre outras coisas.Você pode usar tags HTML.
         title: Termos de serviço customizados
       site_title: Nome da instância
+      thumbnail:
+        desc_html: Usada para prévias via OpenGraph e API. Recomenda-se 1200x630px
+        title: Thumbnail da instância
       timeline_preview:
         desc_html: Exibir a timeline pública na página inicial
         title: Prévia da timeline
@@ -199,8 +248,8 @@ pt-BR:
       back_to_account: Voltar para página da conta
       batch:
         delete: Deletar
-        nsfw_off: NSFW OFF
-        nsfw_on: NSFW ON
+        nsfw_off: NSFW ATIVADO
+        nsfw_on: NSFW DESATIVADO
       execute: Executar
       failed_to_execute: Falha em executar
       media:
@@ -224,7 +273,7 @@ pt-BR:
       subject: Nova denúncia sobre %{instance} (#%{id})
   application_mailer:
     salutation: "%{name},"
-    settings: 'Change e-mail preferences: %{link}'
+    settings: 'Mudar e-mail de preferência: %{link}'
     signature: Notificações do Mastodon de %{instance}
     view: 'Visualizar:'
   applications:
@@ -289,7 +338,10 @@ pt-BR:
       content: A verificação de segurança falhou. Você desativou o uso de cookies?
       title: Falha na verificação de segurança
     '429': Muitas requisições
-    noscript_html: Para usar o aplicativo web do Mastodon, por favor ative o JavaScript. Alternativamente, experimente um dos <a href="https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md">apps nativos</a> para o Mastodon para a sua plataforma.
+    '500':
+      content: Desculpe, algo deu errado.
+      title: Esta página não está certa
+    noscript_html: Para usar o aplicativo web do Mastodon, por favor ative o JavaScript. Ou, se quiser, experimente um dos <a href="https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md">apps nativos</a> para o Mastodon em sua plataforma.
   exports:
     blocks: Você bloqueou
     csv: CSV
@@ -369,6 +421,11 @@ pt-BR:
     next: Próximo
     prev: Anterior
     truncate: "&hellip;"
+  preferences:
+    languages: Idiomas
+    other: Outro
+    publishing: Publicação
+    web: Web
   push_notifications:
     favourite:
       title: "%{name} favoritou a sua postagem"
@@ -420,7 +477,7 @@ pt-BR:
       ios: iOS
       linux: Linux
       mac: Mac
-      other: unknown platform
+      other: Plataforma desconhecida
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -436,6 +493,7 @@ pt-BR:
     export: Exportar dados
     followers: Seguidores autorizados
     import: Importar
+    notifications: Notificações
     preferences: Preferências
     settings: Configurações
     two_factor_authentication: Autenticação em dois passos
@@ -444,6 +502,7 @@ pt-BR:
     open_in_web: Abrir na web
     over_character_limit: limite de caracteres de %{max} excedido
     pin_errors:
+      limit: Muitos toots fixados
       ownership: Toots de outras pessoas não podem ser fixados
       private: Toot não-público não pode ser fixado
       reblog: Um compartilhamento não pode ser fixado
@@ -461,7 +520,7 @@ pt-BR:
     reblogged: compartilhado
     sensitive_content: Conteúdo sensível
   terms:
-    body_html: |
+    body_html:
       <h2>Política de privacidade</h2>
 
       <h3 id="collect">Que informações nós coletamos?</h3>
@@ -530,6 +589,8 @@ pt-BR:
 
       <p>Originalmente adaptado da <a href="https://github.com/discourse/discourse">política de privacidade do Discourse</a>.</p>
     title: "%{instance} Termos de Serviço e Política de Privacidade"
+  themes:
+    default: Mastodon
   time:
     formats:
       default: "%b %d, %Y, %H:%M"
diff --git a/config/locales/simple_form.fa.yml b/config/locales/simple_form.fa.yml
index bdc4f32bf..a19d69ff1 100644
--- a/config/locales/simple_form.fa.yml
+++ b/config/locales/simple_form.fa.yml
@@ -4,6 +4,7 @@ fa:
     hints:
       defaults:
         avatar: یکی از قالب‌های PNG یا  GIF یا JPG. بیشترین اندازه ۲ مگابایت. تصویر به اندازهٔ ۱۲۰×۱۲۰ پیکسل تبدیل خواهد شد.
+        digest: پس از مدت طولانی عدم فعالیت فرستاده می‌شود، شامل خلاصه‌ای از مواردی که در نبودتان از شما نام برده شده
         display_name:
           one: <span class="name-counter">1</span> حرف باقی مانده
           other: <span class="name-counter">%{count}</span> حرف باقی مانده
@@ -13,6 +14,7 @@ fa:
           one: <span class="note-counter">1</span> حرف باقی مانده
           other: <span class="note-counter">%{count}</span> حرف باقی مانده
         setting_noindex: روی نمایهٔ عمومی و صفحهٔ نوشته‌های شما تأثیر می‌گذارد
+        setting_theme: ظاهر ماستدون را وقتی که از هر دستگاهی به آن وارد می‌شوید تعیین می‌کند.
       imports:
         data: پروندهٔ CSV که از سرور ماستدون دیگری برون‌سپاری شده
       sessions:
@@ -43,6 +45,7 @@ fa:
         setting_delete_modal: پیش از پاک کردن یک نوشته پیغام تأیید نشان بده
         setting_noindex: درخواست از موتورهای جستجو برای لغو فهرست‌سازی
         setting_system_font_ui: به‌کاربردن قلم پیش‌فرض سیستم
+        setting_theme: تم سایت
         setting_unfollow_modal: نمایش پیغام تأیید پیش از لغو پیگیری دیگران
         severity: شدت
         type: نوع درون‌ریزی
diff --git a/config/locales/simple_form.fr.yml b/config/locales/simple_form.fr.yml
index 37fc86b0e..40fed9675 100644
--- a/config/locales/simple_form.fr.yml
+++ b/config/locales/simple_form.fr.yml
@@ -3,12 +3,12 @@ fr:
   simple_form:
     hints:
       defaults:
-        avatar: Au format PNG, GIF ou JPG. 2Mo maximum. Sera réduit à 120x120px
+        avatar: Au format PNG, GIF ou JPG. 2 Mo maximum. Sera réduit à 120x120px
         digest: Envoyé après une longue période d’inactivité et contient un résumé des notifications que vous avez reçues pendant votre absence
         display_name:
           one: <span class="name-counter">1</span> caractère restant
           other: <span class="name-counter">%{count}</span> caractères restants
-        header: Au format PNG, GIF ou JPG. 2Mo maximum. Sera réduit à 700x335px
+        header: Au format PNG, GIF ou JPG. 2 Mo maximum. Sera réduit à 700x335px
         locked: Vous devrez approuver chaque abonné⋅e et vos statuts ne s’afficheront qu’à vos abonné⋅es
         note:
           one: <span class="note-counter">1</span> caractère restant
@@ -39,14 +39,14 @@ fr:
         otp_attempt: Code d’identification à deux facteurs
         password: Mot de passe
         setting_auto_play_gif: Lire automatiquement les GIFs animés
-        setting_boost_modal: Afficher un dialogue de confirmation avant de partager
+        setting_boost_modal: Afficher une fenêtre de confirmation avant de partager
         setting_default_privacy: Confidentialité des statuts
         setting_default_sensitive: Toujours marquer les médias comme sensibles
-        setting_delete_modal: Afficher un dialogue de confirmation avant de supprimer un pouet
+        setting_delete_modal: Afficher une fenêtre de confirmation avant de supprimer un pouet
         setting_noindex: Demander aux moteurs de recherche de ne pas indexer vos informations personnelles
         setting_system_font_ui: Utiliser la police par défaut du système
         setting_theme: Thème du site
-        setting_unfollow_modal: Afficher un dialogue de confirmation avant de vous désabonner d’un compte
+        setting_unfollow_modal: Afficher une fenêtre de confirmation avant de vous désabonner d’un compte
         severity: Séverité
         type: Type d’import
         username: Identifiant
diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml
index 17b35ba8f..c889a882e 100644
--- a/config/locales/simple_form.ja.yml
+++ b/config/locales/simple_form.ja.yml
@@ -4,6 +4,7 @@ ja:
     hints:
       defaults:
         avatar: 2MBまでのPNGやGIF、JPGが利用可能です。120x120pxまで縮小されます。
+        digest: 長期間ログインしなかった際、その期間に受け取った返信の要約を受け取ることができます。
         display_name: あと<span class="name-counter">%{count}</span>文字入力できます。
         header: 2MBまでのPNGやGIF、JPGが利用可能です。 700x335pxまで縮小されます。
         locked: フォロワーを手動で承認する必要があります
diff --git a/config/locales/simple_form.ko.yml b/config/locales/simple_form.ko.yml
index abbad7430..e2a678121 100644
--- a/config/locales/simple_form.ko.yml
+++ b/config/locales/simple_form.ko.yml
@@ -8,10 +8,12 @@ ko:
           one: <span class="name-counter">1</span> 글자 남음
           other: <span class="name-counter">%{count}</span> 글자 남음
         header: PNG, GIF 혹은 JPG. 최대 2MB. 700x335px로 다운스케일 됨
-        locked: 수동으로 팔로워를 승인하고, 기본 Toot 프라이버시 설정을 팔로워 전용으로 변경
+        locked: 수동으로 팔로워를 승인하고, 기본 툿 프라이버시 설정을 팔로워 전용으로 변경
         note:
           one: <span class="note-counter">1</span> 글자 남음
           other: <span class="note-counter">%{count}</span> 글자 남음
+        setting_noindex: 공개 프로필 및 각 툿페이지에 영향을 미칩니다
+        setting_theme: 로그인중인 모든 디바이스에 적용되는 디자인입니다.
       imports:
         data: 다른 마스토돈 인스턴스에서 추출된 CSV 파일
       sessions:
@@ -35,9 +37,14 @@ ko:
         otp_attempt: 2단계 인증 코드
         password: 비밀번호
         setting_auto_play_gif: 애니메이션 GIF를 자동 재생
-        setting_boost_modal: 부스트 전 확인 창을 보여주기
-        setting_default_privacy: Toot 프라이버시
-        setting_delete_modal: Toot 삭제 전 확인 창을 보여주기
+        setting_boost_modal: 부스트 전 확인 창을 표시
+        setting_default_privacy: 툿 프라이버시
+        setting_default_sensitive: 미디어를 언제나 민감한 컨텐츠로 설정
+        setting_delete_modal: 툿 삭제 전 확인 창을 표시
+        setting_noindex: 검색엔진의 인덱싱을 거절
+        setting_system_font_ui: 시스템의 초기 설정 폰트를 사용
+        setting_theme: 사이트 테마
+        setting_unfollow_modal: 언팔로우 전 언팔로우 확인 표시
         severity: 심각도
         type: 불러오기 종류
         username: 유저 이름
@@ -50,7 +57,7 @@ ko:
         follow: 누군가 나를 팔로우 했을 때 이메일 보내기
         follow_request: 누군가 나를 팔로우 하길 원할 때 이메일 보내기
         mention: 누군가 나에게 답장했을 때 이메일 보내기
-        reblog: 누군가 내 Toot을 부스트 했을 때 이메일 보내기
+        reblog: 누군가 내 툿을 부스트 했을 때 이메일 보내기
     'no': 아니오
     required:
       mark: "*"
diff --git a/spec/controllers/concerns/user_tracking_concern_spec.rb b/spec/controllers/concerns/user_tracking_concern_spec.rb
index 6bd29f887..168d44ba6 100644
--- a/spec/controllers/concerns/user_tracking_concern_spec.rb
+++ b/spec/controllers/concerns/user_tracking_concern_spec.rb
@@ -5,6 +5,7 @@ require 'rails_helper'
 describe ApplicationController, type: :controller do
   controller do
     include UserTrackingConcern
+
     def show
       render plain: 'show'
     end
@@ -49,6 +50,7 @@ describe ApplicationController, type: :controller do
       get :show
 
       expect_updated_sign_in_at(user)
+      expect(Redis.current.get("account:#{user.account_id}:regeneration")).to eq 'true'
       expect(RegenerationWorker).to have_received(:perform_async)
     end
 
diff --git a/spec/lib/activitypub/activity/undo_spec.rb b/spec/lib/activitypub/activity/undo_spec.rb
index 14c68efe5..e01c5e03e 100644
--- a/spec/lib/activitypub/activity/undo_spec.rb
+++ b/spec/lib/activitypub/activity/undo_spec.rb
@@ -25,16 +25,30 @@ RSpec.describe ActivityPub::Activity::Undo do
           type: 'Announce',
           actor: ActivityPub::TagManager.instance.uri_for(sender),
           object: ActivityPub::TagManager.instance.uri_for(status),
+          atomUri: 'barbar',
         }
       end
 
-      before do
-        Fabricate(:status, reblog: status, account: sender, uri: 'bar')
+      context do
+        before do
+          Fabricate(:status, reblog: status, account: sender, uri: 'bar')
+        end
+
+        it 'deletes the reblog' do
+          subject.perform
+          expect(sender.reblogged?(status)).to be false
+        end
       end
 
-      it 'deletes the reblog' do
-        subject.perform
-        expect(sender.reblogged?(status)).to be false
+      context 'with atomUri' do
+        before do
+          Fabricate(:status, reblog: status, account: sender, uri: 'barbar')
+        end
+
+        it 'deletes the reblog by atomUri' do
+          subject.perform
+          expect(sender.reblogged?(status)).to be false
+        end
       end
     end
 
diff --git a/spec/models/feed_spec.rb b/spec/models/feed_spec.rb
index 5433f44bd..8719369db 100644
--- a/spec/models/feed_spec.rb
+++ b/spec/models/feed_spec.rb
@@ -1,21 +1,45 @@
 require 'rails_helper'
 
 RSpec.describe Feed, type: :model do
+  let(:account) { Fabricate(:account) }
+
+  subject { described_class.new(:home, account) }
+
   describe '#get' do
-    it 'gets statuses with ids in the range' do
-      account = Fabricate(:account)
+    before do
       Fabricate(:status, account: account, id: 1)
       Fabricate(:status, account: account, id: 2)
       Fabricate(:status, account: account, id: 3)
       Fabricate(:status, account: account, id: 10)
-      Redis.current.zadd(FeedManager.instance.key(:home, account.id),
-                        [[4, 4], [3, 3], [2, 2], [1, 1]])
+    end
+
+    context 'when feed is generated' do
+      before do
+        Redis.current.zadd(
+          FeedManager.instance.key(:home, account.id),
+          [[4, 4], [3, 3], [2, 2], [1, 1]]
+        )
+      end
+
+      it 'gets statuses with ids in the range from redis' do
+        results = subject.get(3)
+
+        expect(results.map(&:id)).to eq [3, 2]
+        expect(results.first.attributes.keys).to eq %w(id updated_at)
+      end
+    end
+
+    context 'when feed is being generated' do
+      before do
+        Redis.current.set("account:#{account.id}:regeneration", true)
+      end
 
-      feed = Feed.new(:home, account)
-      results = feed.get(3)
+      it 'gets statuses with ids in the range from database' do
+        results = subject.get(3)
 
-      expect(results.map(&:id)).to eq [3, 2]
-      expect(results.first.attributes.keys).to eq %w(id updated_at)
+        expect(results.map(&:id)).to eq [10, 3, 2]
+        expect(results.first.attributes.keys).to include('id', 'updated_at')
+      end
     end
   end
 end
diff --git a/spec/services/reblog_service_spec.rb b/spec/services/reblog_service_spec.rb
index 0ad5c5f6b..19d3bb6cb 100644
--- a/spec/services/reblog_service_spec.rb
+++ b/spec/services/reblog_service_spec.rb
@@ -39,6 +39,12 @@ RSpec.describe ReblogService do
       expect(status.reblogs.count).to eq 1
     end
 
+    describe 'after_create_commit :store_uri' do
+      it 'keeps consistent reblog count' do
+        expect(status.reblogs.count).to eq 1
+      end
+    end
+
     it 'distributes to followers' do
       expect(ActivityPub::DistributionWorker).to have_received(:perform_async)
     end