From ef7d64c80109074b39983b50cc8cf701c337cdcc Mon Sep 17 00:00:00 2001
From: ThibG <thib@sitedethib.com>
Date: Fri, 4 May 2018 22:13:26 +0200
Subject: [PATCH 01/40] Dropdowns accessibility (#7318)

* Mark currently selected privacy setting in privacy dropdown

* Prevent Enter keypresses from triggering dropdown display toggle twice

* Give focus to first/selected item of dropdown menus

* Implement keyboard navigation in privacy dropdown

* Implement keyboard navigation in generic dropdown menus
---
 .../mastodon/components/dropdown_menu.js      | 46 ++++++++++--
 .../compose/components/privacy_dropdown.js    | 70 +++++++++++++++----
 2 files changed, 99 insertions(+), 17 deletions(-)

diff --git a/app/javascript/mastodon/components/dropdown_menu.js b/app/javascript/mastodon/components/dropdown_menu.js
index 982d34718e..0a6e7c6272 100644
--- a/app/javascript/mastodon/components/dropdown_menu.js
+++ b/app/javascript/mastodon/components/dropdown_menu.js
@@ -43,6 +43,7 @@ class DropdownMenu extends React.PureComponent {
   componentDidMount () {
     document.addEventListener('click', this.handleDocumentClick, false);
     document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
+    if (this.focusedItem) this.focusedItem.focus();
     this.setState({ mounted: true });
   }
 
@@ -55,6 +56,46 @@ class DropdownMenu extends React.PureComponent {
     this.node = c;
   }
 
+  setFocusRef = c => {
+    this.focusedItem = c;
+  }
+
+  handleKeyDown = e => {
+    const items = Array.from(this.node.getElementsByTagName('a'));
+    const index = items.indexOf(e.currentTarget);
+    let element;
+
+    switch(e.key) {
+    case 'Enter':
+      this.handleClick(e);
+      break;
+    case 'ArrowDown':
+      element = items[index+1];
+      if (element) {
+        element.focus();
+      }
+      break;
+    case 'ArrowUp':
+      element = items[index-1];
+      if (element) {
+        element.focus();
+      }
+      break;
+    case 'Home':
+      element = items[0];
+      if (element) {
+        element.focus();
+      }
+      break;
+    case 'End':
+      element = items[items.length-1];
+      if (element) {
+        element.focus();
+      }
+      break;
+    }
+  }
+
   handleClick = e => {
     const i = Number(e.currentTarget.getAttribute('data-index'));
     const { action, to } = this.props.items[i];
@@ -79,7 +120,7 @@ class DropdownMenu extends React.PureComponent {
 
     return (
       <li className='dropdown-menu__item' key={`${text}-${i}`}>
-        <a href={href} target='_blank' rel='noopener' role='button' tabIndex='0' autoFocus={i === 0} onClick={this.handleClick} data-index={i}>
+        <a href={href} target='_blank' rel='noopener' role='button' tabIndex='0' ref={i === 0 ? this.setFocusRef : null} onClick={this.handleClick} onKeyDown={this.handleKeyDown} data-index={i}>
           {text}
         </a>
       </li>
@@ -156,9 +197,6 @@ export default class Dropdown extends React.PureComponent {
 
   handleKeyDown = e => {
     switch(e.key) {
-    case 'Enter':
-      this.handleClick(e);
-      break;
     case 'Escape':
       this.handleClose();
       break;
diff --git a/app/javascript/mastodon/features/compose/components/privacy_dropdown.js b/app/javascript/mastodon/features/compose/components/privacy_dropdown.js
index 6b22ba84a5..a772c1c953 100644
--- a/app/javascript/mastodon/features/compose/components/privacy_dropdown.js
+++ b/app/javascript/mastodon/features/compose/components/privacy_dropdown.js
@@ -42,22 +42,65 @@ class PrivacyDropdownMenu extends React.PureComponent {
     }
   }
 
-  handleClick = e => {
-    if (e.key === 'Escape') {
-      this.props.onClose();
-    } else if (!e.key || e.key === 'Enter') {
-      const value = e.currentTarget.getAttribute('data-index');
-
-      e.preventDefault();
+  handleKeyDown = e => {
+    const { items } = this.props;
+    const value = e.currentTarget.getAttribute('data-index');
+    const index = items.findIndex(item => {
+      return (item.value === value);
+    });
+    let element;
 
+    switch(e.key) {
+    case 'Escape':
       this.props.onClose();
-      this.props.onChange(value);
+      break;
+    case 'Enter':
+      this.handleClick(e);
+      break;
+    case 'ArrowDown':
+      element = this.node.childNodes[index + 1];
+      if (element) {
+        element.focus();
+        this.props.onChange(element.getAttribute('data-index'));
+      }
+      break;
+    case 'ArrowUp':
+      element = this.node.childNodes[index - 1];
+      if (element) {
+        element.focus();
+        this.props.onChange(element.getAttribute('data-index'));
+      }
+      break;
+    case 'Home':
+      element = this.node.firstChild;
+      if (element) {
+        element.focus();
+        this.props.onChange(element.getAttribute('data-index'));
+      }
+      break;
+    case 'End':
+      element = this.node.lastChild;
+      if (element) {
+        element.focus();
+        this.props.onChange(element.getAttribute('data-index'));
+      }
+      break;
     }
   }
 
+  handleClick = e => {
+    const value = e.currentTarget.getAttribute('data-index');
+
+    e.preventDefault();
+
+    this.props.onClose();
+    this.props.onChange(value);
+  }
+
   componentDidMount () {
     document.addEventListener('click', this.handleDocumentClick, false);
     document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
+    if (this.focusedItem) this.focusedItem.focus();
     this.setState({ mounted: true });
   }
 
@@ -70,6 +113,10 @@ class PrivacyDropdownMenu extends React.PureComponent {
     this.node = c;
   }
 
+  setFocusRef = c => {
+    this.focusedItem = c;
+  }
+
   render () {
     const { mounted } = this.state;
     const { style, items, value } = this.props;
@@ -80,9 +127,9 @@ class PrivacyDropdownMenu extends React.PureComponent {
           // It should not be transformed when mounting because the resulting
           // size will be used to determine the coordinate of the menu by
           // react-overlays
-          <div className='privacy-dropdown__dropdown' style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} ref={this.setRef}>
+          <div className='privacy-dropdown__dropdown' style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} role='listbox' ref={this.setRef}>
             {items.map(item => (
-              <div role='button' tabIndex='0' key={item.value} data-index={item.value} onKeyDown={this.handleClick} onClick={this.handleClick} className={classNames('privacy-dropdown__option', { active: item.value === value })}>
+              <div role='option' tabIndex='0' key={item.value} data-index={item.value} onKeyDown={this.handleKeyDown} onClick={this.handleClick} className={classNames('privacy-dropdown__option', { active: item.value === value })} aria-selected={item.value === value} ref={item.value === value ? this.setFocusRef : null}>
                 <div className='privacy-dropdown__option__icon'>
                   <i className={`fa fa-fw fa-${item.icon}`} />
                 </div>
@@ -147,9 +194,6 @@ export default class PrivacyDropdown extends React.PureComponent {
 
   handleKeyDown = e => {
     switch(e.key) {
-    case 'Enter':
-      this.handleToggle(e);
-      break;
     case 'Escape':
       this.handleClose();
       break;

From c73ce7b695aef1bbfe36bf674173d5a9fb988df5 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Sat, 5 May 2018 00:54:24 +0200
Subject: [PATCH 02/40] Store home feeds for 7 days instead of 14 (#7354)

* Store home feeds for 7 days instead of 14

Reduces workload for status fan-out to active followers

* Fix test for user model
---
 app/models/user.rb                       | 2 +-
 app/services/fan_out_on_write_service.rb | 4 ++--
 spec/models/user_spec.rb                 | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/app/models/user.rb b/app/models/user.rb
index a9f3e1da26..f5f542f07d 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -41,7 +41,7 @@ class User < ApplicationRecord
   include Settings::Extend
   include Omniauthable
 
-  ACTIVE_DURATION = 14.days
+  ACTIVE_DURATION = 7.days
 
   devise :two_factor_authenticatable,
          otp_secret_encryption_key: Rails.configuration.x.otp_secret
diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb
index 510b80c823..cb82a79ed3 100644
--- a/app/services/fan_out_on_write_service.rb
+++ b/app/services/fan_out_on_write_service.rb
@@ -37,7 +37,7 @@ class FanOutOnWriteService < BaseService
   def deliver_to_followers(status)
     Rails.logger.debug "Delivering status #{status.id} to followers"
 
-    status.account.followers.where(domain: nil).joins(:user).where('users.current_sign_in_at > ?', 14.days.ago).select(:id).reorder(nil).find_in_batches do |followers|
+    status.account.followers.where(domain: nil).joins(:user).where('users.current_sign_in_at > ?', User::ACTIVE_DURATION.ago).select(:id).reorder(nil).find_in_batches do |followers|
       FeedInsertWorker.push_bulk(followers) do |follower|
         [status.id, follower.id, :home]
       end
@@ -47,7 +47,7 @@ class FanOutOnWriteService < BaseService
   def deliver_to_lists(status)
     Rails.logger.debug "Delivering status #{status.id} to lists"
 
-    status.account.lists.joins(account: :user).where('users.current_sign_in_at > ?', 14.days.ago).select(:id).reorder(nil).find_in_batches do |lists|
+    status.account.lists.joins(account: :user).where('users.current_sign_in_at > ?', User::ACTIVE_DURATION.ago).select(:id).reorder(nil).find_in_batches do |lists|
       FeedInsertWorker.push_bulk(lists) do |list|
         [status.id, list.id, :list]
       end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 760214dede..cc8d88cc85 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -75,7 +75,7 @@ RSpec.describe User, type: :model do
     describe 'inactive' do
       it 'returns a relation of inactive users' do
         specified = Fabricate(:user, current_sign_in_at: 15.days.ago)
-        Fabricate(:user, current_sign_in_at: 13.days.ago)
+        Fabricate(:user, current_sign_in_at: 6.days.ago)
 
         expect(User.inactive).to match_array([specified])
       end

From 66359ec522e687020ea853652ad36d4476f2720d Mon Sep 17 00:00:00 2001
From: Akihiko Odaki <akihiko.odaki.4i@stu.hosei.ac.jp>
Date: Sat, 5 May 2018 07:54:56 +0900
Subject: [PATCH 03/40] Fix gap insertion for timeline disconnection (#7363)

---
 app/javascript/mastodon/reducers/timelines.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/javascript/mastodon/reducers/timelines.js b/app/javascript/mastodon/reducers/timelines.js
index dd675d78fb..b09d78b0fb 100644
--- a/app/javascript/mastodon/reducers/timelines.js
+++ b/app/javascript/mastodon/reducers/timelines.js
@@ -134,7 +134,7 @@ export default function timelines(state = initialState, action) {
       initialTimeline,
       map => map.update(
         'items',
-        items => items.first() ? items : items.unshift(null)
+        items => items.first() ? items.unshift(null) : items
       )
     );
   default:

From 2ef9d650520f95e0b1b4b292bce7b80b9967f146 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Sat, 5 May 2018 00:55:09 +0200
Subject: [PATCH 04/40] Improve rendering of bio fields (#7353)

Fix #7335
---
 .../features/account/components/header.js     | 18 ++++----
 app/javascript/styles/mastodon/accounts.scss  | 44 +++++++++----------
 .../styles/mastodon/components.scss           | 23 +++++-----
 app/views/accounts/_header.html.haml          | 11 +++--
 4 files changed, 47 insertions(+), 49 deletions(-)

diff --git a/app/javascript/mastodon/features/account/components/header.js b/app/javascript/mastodon/features/account/components/header.js
index bbf886dca8..def6257956 100644
--- a/app/javascript/mastodon/features/account/components/header.js
+++ b/app/javascript/mastodon/features/account/components/header.js
@@ -142,16 +142,14 @@ export default class Header extends ImmutablePureComponent {
           <div className='account__header__content' dangerouslySetInnerHTML={content} />
 
           {fields.size > 0 && (
-            <table className='account__header__fields'>
-              <tbody>
-                {fields.map((pair, i) => (
-                  <tr key={i}>
-                    <th dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} />
-                    <td dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} />
-                  </tr>
-                ))}
-              </tbody>
-            </table>
+            <div className='account__header__fields'>
+              {fields.map((pair, i) => (
+                <dl key={i}>
+                  <dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} />
+                  <dd dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} title={pair.get('value')} />
+                </dl>
+              ))}
+            </div>
           )}
 
           {info}
diff --git a/app/javascript/styles/mastodon/accounts.scss b/app/javascript/styles/mastodon/accounts.scss
index c2d0de4b99..b063ca52dd 100644
--- a/app/javascript/styles/mastodon/accounts.scss
+++ b/app/javascript/styles/mastodon/accounts.scss
@@ -565,36 +565,41 @@
 }
 
 .account__header__fields {
-  border-collapse: collapse;
   padding: 0;
   margin: 15px -15px -15px;
   border: 0 none;
   border-top: 1px solid lighten($ui-base-color, 4%);
   border-bottom: 1px solid lighten($ui-base-color, 4%);
+  font-size: 14px;
+  line-height: 20px;
 
-  th,
-  td {
-    padding: 15px;
-    padding-left: 15px;
-    border: 0 none;
+  dl {
+    display: flex;
     border-bottom: 1px solid lighten($ui-base-color, 4%);
-    vertical-align: middle;
   }
 
-  th {
-    padding-left: 15px;
-    font-weight: 500;
+  dt,
+  dd {
+    box-sizing: border-box;
+    padding: 14px;
     text-align: center;
-    width: 94px;
+    max-height: 48px;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+  }
+
+  dt {
+    font-weight: 500;
+    width: 120px;
+    flex: 0 0 auto;
     color: $secondary-text-color;
     background: rgba(darken($ui-base-color, 8%), 0.5);
   }
 
-  td {
+  dd {
+    flex: 1 1 auto;
     color: $darker-text-color;
-    text-align: center;
-    width: 100%;
-    padding-left: 0;
   }
 
   a {
@@ -608,12 +613,7 @@
     }
   }
 
-  tr {
-    &:last-child {
-      th,
-      td {
-        border-bottom: 0;
-      }
-    }
+  dl:last-child {
+    border-bottom: 0;
   }
 }
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index f1284b3884..7cf6f4b769 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -5163,34 +5163,35 @@ noscript {
   font-size: 14px;
   line-height: 20px;
   overflow: hidden;
-  border-collapse: collapse;
   margin: 20px -10px -20px;
   border-bottom: 0;
 
-  tr {
+  dl {
     border-top: 1px solid lighten($ui-base-color, 8%);
-    text-align: center;
+    display: flex;
   }
 
-  th,
-  td {
+  dt,
+  dd {
+    box-sizing: border-box;
     padding: 14px 20px;
-    vertical-align: middle;
-    max-height: 40px;
+    text-align: center;
+    max-height: 48px;
     overflow: hidden;
     white-space: nowrap;
     text-overflow: ellipsis;
   }
 
-  th {
+  dt {
     color: $darker-text-color;
     background: darken($ui-base-color, 4%);
-    max-width: 120px;
+    width: 120px;
+    flex: 0 0 auto;
     font-weight: 500;
   }
 
-  td {
-    flex: auto;
+  dd {
+    flex: 1 1 auto;
     color: $primary-text-color;
     background: $ui-base-color;
   }
diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml
index f246f53262..41315f0393 100644
--- a/app/views/accounts/_header.html.haml
+++ b/app/views/accounts/_header.html.haml
@@ -24,12 +24,11 @@
       .account__header__content.p-note.emojify= Formatter.instance.simplified_format(account, custom_emojify: true)
 
       - unless account.fields.empty?
-        %table.account__header__fields
-          %tbody
-            - account.fields.each do |field|
-              %tr
-                %th.emojify= field.name
-                %td.emojify= Formatter.instance.format_field(account, field.value)
+        .account__header__fields
+          - account.fields.each do |field|
+            %dl
+              %dt.emojify{ title: field.name }= field.name
+              %dd.emojify{ title: field.value }= Formatter.instance.format_field(account, field.value)
 
     .details-counters
       .counter{ class: active_nav_class(short_account_url(account)) }

From 661f7e6d9d589315728f26e5cc5b345ee769f4b0 Mon Sep 17 00:00:00 2001
From: Shuhei Kitagawa <shuheiktgw@users.noreply.github.com>
Date: Sat, 5 May 2018 22:53:59 +0900
Subject: [PATCH 05/40] Add tests for admin/custom_emojis_controller (#7350)

---
 .../admin/custom_emojis_controller_spec.rb    | 115 ++++++++++++++++++
 1 file changed, 115 insertions(+)
 create mode 100644 spec/controllers/admin/custom_emojis_controller_spec.rb

diff --git a/spec/controllers/admin/custom_emojis_controller_spec.rb b/spec/controllers/admin/custom_emojis_controller_spec.rb
new file mode 100644
index 0000000000..b7e2894e90
--- /dev/null
+++ b/spec/controllers/admin/custom_emojis_controller_spec.rb
@@ -0,0 +1,115 @@
+require 'rails_helper'
+
+describe Admin::CustomEmojisController do
+  render_views
+
+  let(:user) { Fabricate(:user, admin: true) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    subject { get :index }
+
+    before do
+      Fabricate(:custom_emoji)
+    end
+
+    it 'renders index page' do
+      expect(subject).to have_http_status 200
+      expect(subject).to render_template :index
+    end
+  end
+
+  describe 'GET #new' do
+    subject { get :new }
+
+    it 'renders new page' do
+      expect(subject).to have_http_status 200
+      expect(subject).to render_template :new
+    end
+  end
+
+  describe 'POST #create' do
+    subject { post :create, params: { custom_emoji: params } }
+
+    let(:image) { fixture_file_upload(Rails.root.join('spec', 'fixtures', 'files', 'emojo.png'), 'image/png') }
+
+    context 'when parameter is valid' do
+      let(:params) { { shortcode: 'test', image: image } }
+
+      it 'creates custom emoji' do
+        expect { subject }.to change { CustomEmoji.count }.by(1)
+      end
+    end
+
+    context 'when parameter is invalid' do
+      let(:params) { { shortcode: 't', image: image } }
+
+      it 'renders new' do
+        expect(subject).to render_template :new
+      end
+    end
+  end
+
+  describe 'PUT #update' do
+    let(:custom_emoji) { Fabricate(:custom_emoji, shortcode: 'test') }
+    let(:image) { fixture_file_upload(Rails.root.join('spec', 'fixtures', 'files', 'emojo.png'), 'image/png') }
+
+    before do
+      put :update, params: { id: custom_emoji.id, custom_emoji: params }
+    end
+
+    context 'when parameter is valid' do
+      let(:params) { { shortcode: 'updated', image: image } }
+
+      it 'succeeds in updating custom emoji' do
+        expect(flash[:notice]).to eq I18n.t('admin.custom_emojis.updated_msg')
+        expect(custom_emoji.reload).to have_attributes(shortcode: 'updated')
+      end
+    end
+
+    context 'when parameter is invalid' do
+      let(:params) { { shortcode: 'u', image: image } }
+
+      it 'fails to update custom emoji' do
+        expect(flash[:alert]).to eq I18n.t('admin.custom_emojis.update_failed_msg')
+        expect(custom_emoji.reload).to have_attributes(shortcode: 'test')
+      end
+    end
+  end
+
+  describe 'POST #copy' do
+    subject { post :copy, params: { id: custom_emoji.id } }
+
+    let(:custom_emoji) { Fabricate(:custom_emoji, shortcode: 'test') }
+
+    it 'copies custom emoji' do
+      expect { subject }.to change { CustomEmoji.where(shortcode: 'test').count }.by(1)
+      expect(flash[:notice]).to eq I18n.t('admin.custom_emojis.copied_msg')
+    end
+  end
+
+  describe 'POST #enable' do
+    let(:custom_emoji) { Fabricate(:custom_emoji, shortcode: 'test', disabled: true) }
+
+    before { post :enable, params: { id: custom_emoji.id } }
+
+    it 'enables custom emoji' do
+      expect(response).to redirect_to admin_custom_emojis_path
+      expect(custom_emoji.reload).to have_attributes(disabled: false)
+    end
+  end
+
+  describe 'POST #disable' do
+    let(:custom_emoji) { Fabricate(:custom_emoji, shortcode: 'test', disabled: false) }
+
+    before { post :disable, params: { id: custom_emoji.id } }
+
+    it 'enables custom emoji' do
+      expect(response).to redirect_to admin_custom_emojis_path
+      expect(custom_emoji.reload).to have_attributes(disabled: true)
+    end
+  end
+end

From c947e2e4c57dd6d351fd740c0070fefdc1e1298c Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Sat, 5 May 2018 18:22:34 +0200
Subject: [PATCH 06/40] Fix handling of malformed ActivityPub payloads when
 URIs are nil (#7370)

* Fix handling of malformed ActivityPub payloads when URIs are nil

* Gracefully handle JSON-LD canonicalization failures
---
 app/lib/activitypub/tag_manager.rb                      | 4 ++++
 app/services/activitypub/fetch_remote_status_service.rb | 1 +
 app/services/activitypub/process_collection_service.rb  | 3 +++
 3 files changed, 8 insertions(+)

diff --git a/app/lib/activitypub/tag_manager.rb b/app/lib/activitypub/tag_manager.rb
index 908ea96391..95d1cf9f35 100644
--- a/app/lib/activitypub/tag_manager.rb
+++ b/app/lib/activitypub/tag_manager.rb
@@ -86,6 +86,8 @@ class ActivityPub::TagManager
   end
 
   def local_uri?(uri)
+    return false if uri.nil?
+
     uri  = Addressable::URI.parse(uri)
     host = uri.normalized_host
     host = "#{host}:#{uri.port}" if uri.port
@@ -99,6 +101,8 @@ class ActivityPub::TagManager
   end
 
   def uri_to_resource(uri, klass)
+    return if uri.nil?
+
     if local_uri?(uri)
       case klass.name
       when 'Account'
diff --git a/app/services/activitypub/fetch_remote_status_service.rb b/app/services/activitypub/fetch_remote_status_service.rb
index 930fbad1f1..b6c00a9e7e 100644
--- a/app/services/activitypub/fetch_remote_status_service.rb
+++ b/app/services/activitypub/fetch_remote_status_service.rb
@@ -34,6 +34,7 @@ class ActivityPub::FetchRemoteStatusService < BaseService
   end
 
   def trustworthy_attribution?(uri, attributed_to)
+    return false if uri.nil? || attributed_to.nil?
     Addressable::URI.parse(uri).normalized_host.casecmp(Addressable::URI.parse(attributed_to).normalized_host).zero?
   end
 
diff --git a/app/services/activitypub/process_collection_service.rb b/app/services/activitypub/process_collection_service.rb
index eb93329e97..79cdca297b 100644
--- a/app/services/activitypub/process_collection_service.rb
+++ b/app/services/activitypub/process_collection_service.rb
@@ -45,5 +45,8 @@ class ActivityPub::ProcessCollectionService < BaseService
 
   def verify_account!
     @account = ActivityPub::LinkedDataSignature.new(@json).verify_account!
+  rescue JSON::LD::JsonLdError => e
+    Rails.logger.debug "Could not verify LD-Signature for #{value_or_id(@json['actor'])}: #{e.message}"
+    nil
   end
 end

From 80b23a6a85b4feeac732bc1612db4fe5ccdbac6d Mon Sep 17 00:00:00 2001
From: Reto Kromer <retokromer@users.noreply.github.com>
Date: Sat, 5 May 2018 21:03:21 +0200
Subject: [PATCH 07/40] uniform email rather than e-mail (#7373)

---
 config/locales/it.yml | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/config/locales/it.yml b/config/locales/it.yml
index 0518d20e61..e010be02d9 100644
--- a/config/locales/it.yml
+++ b/config/locales/it.yml
@@ -135,7 +135,7 @@ it:
       web: Web
     action_logs:
       actions:
-        change_email_user: "%{name} ha cambiato l'indirizzo e-mail per l'utente %{target}"
+        change_email_user: "%{name} ha cambiato l'indirizzo email per l'utente %{target}"
         confirm_user: "%{name} ha confermato l'indirizzo email per l'utente %{target}"
         create_custom_emoji: "%{name} ha caricato un nuovo emoji %{target}"
         create_domain_block: "%{name} ha bloccato il dominio %{target}"
@@ -152,7 +152,7 @@ it:
       emoji: Emoji
       enable: Abilita
       enabled_msg: Questa emoji è stata abilitata con successo
-      image_hint: PNG fino a 50KB
+      image_hint: PNG fino a 50 KB
       listed: Elencato
       new:
         title: Aggiungi nuovo emoji personalizzato
@@ -186,9 +186,9 @@ it:
       undo: Annulla
     email_domain_blocks:
       add_new: Aggiungi nuovo
-      created_msg: Dominio e-mail aggiunto con successo alla lista nera
+      created_msg: Dominio email aggiunto con successo alla lista nera
       delete: Elimina
-      destroyed_msg: Dominio e-mail cancellato con successo dalla lista nera
+      destroyed_msg: Dominio email cancellato con successo dalla lista nera
       domain: Dominio
       new:
         create: Aggiungi dominio
@@ -283,7 +283,7 @@ it:
   application_mailer:
     notification_preferences: Cambia preferenze email
     salutation: "%{name},"
-    settings: 'Cambia le impostazioni per le e-mail: %{link}'
+    settings: 'Cambia le impostazioni per le email: %{link}'
     view: 'Guarda:'
     view_profile: Mostra profilo
     view_status: Mostra stati
@@ -435,5 +435,5 @@ it:
       tips: Suggerimenti
       title: Benvenuto a bordo, %{name}!
   users:
-    invalid_email: L'indirizzo e-mail inserito non è valido
+    invalid_email: L'indirizzo email inserito non è valido
     invalid_otp_token: Codice d'accesso non valido

From 8da4bf0f901d7b20f3ca7c6975a0d21a56fb735c Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Sat, 5 May 2018 21:11:19 +0200
Subject: [PATCH 08/40] 4 profile fields max, store only 255 characters per
 name/value (#7348)

Fix #7303
---
 app/models/account.rb | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/app/models/account.rb b/app/models/account.rb
index a166fb4746..72ba0398ef 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -74,6 +74,7 @@ class Account < ApplicationRecord
   validates_with UnreservedUsernameValidator, if: -> { local? && will_save_change_to_username? }
   validates :display_name, length: { maximum: 30 }, if: -> { local? && will_save_change_to_display_name? }
   validates :note, length: { maximum: 160 }, if: -> { local? && will_save_change_to_note? }
+  validates :fields, length: { maximum: 4 }, if: -> { local? && will_save_change_to_fields? }
 
   # Timelines
   has_many :stream_entries, inverse_of: :account, dependent: :destroy
@@ -198,9 +199,11 @@ class Account < ApplicationRecord
   def fields_attributes=(attributes)
     fields = []
 
-    attributes.each_value do |attr|
-      next if attr[:name].blank?
-      fields << attr
+    if attributes.is_a?(Hash)
+      attributes.each_value do |attr|
+        next if attr[:name].blank?
+        fields << attr
+      end
     end
 
     self[:fields] = fields
@@ -269,8 +272,8 @@ class Account < ApplicationRecord
 
     def initialize(account, attr)
       @account = account
-      @name    = attr['name']
-      @value   = attr['value']
+      @name    = attr['name'].strip[0, 255]
+      @value   = attr['value'].strip[0, 255]
       @errors  = {}
     end
 

From 660db468c09146802bd4418599cf4103ef190c10 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Sat, 5 May 2018 23:05:43 +0200
Subject: [PATCH 09/40] Do not count search route as compose being mounted in
 web UI (#7372)

Fix #7144
---
 app/javascript/mastodon/features/compose/index.js | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/app/javascript/mastodon/features/compose/index.js b/app/javascript/mastodon/features/compose/index.js
index 67f0e79815..19aae03322 100644
--- a/app/javascript/mastodon/features/compose/index.js
+++ b/app/javascript/mastodon/features/compose/index.js
@@ -43,11 +43,19 @@ export default class Compose extends React.PureComponent {
   };
 
   componentDidMount () {
-    this.props.dispatch(mountCompose());
+    const { isSearchPage } = this.props;
+
+    if (!isSearchPage) {
+      this.props.dispatch(mountCompose());
+    }
   }
 
   componentWillUnmount () {
-    this.props.dispatch(unmountCompose());
+    const { isSearchPage } = this.props;
+
+    if (!isSearchPage) {
+      this.props.dispatch(unmountCompose());
+    }
   }
 
   onFocus = () => {

From c7d1a2e400cd6677057a8af90fff866207576098 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Sat, 5 May 2018 23:06:29 +0200
Subject: [PATCH 10/40] Improve admin UI for accounts (#7360)

* Improve design of account statuses admin UI (consistent with reports)

* Make account moderation notes look consistent with report notes

* i18n-tasks remove-unused

* Fix code style issues

* Fix tests
---
 .../admin/reported_statuses_controller.rb     | 19 -------
 app/controllers/admin/statuses_controller.rb  | 35 ++++---------
 app/javascript/styles/mastodon/admin.scss     |  3 +-
 .../_account_moderation_note.html.haml        | 15 +++---
 app/views/admin/accounts/show.html.haml       | 39 ++++++--------
 app/views/admin/reports/_status.html.haml     |  2 +-
 app/views/admin/statuses/index.html.haml      | 52 ++++++++-----------
 config/locales/ar.yml                         |  9 ----
 config/locales/ca.yml                         |  9 ----
 config/locales/de.yml                         |  9 ----
 config/locales/en.yml                         | 11 +---
 config/locales/eo.yml                         |  9 ----
 config/locales/es.yml                         |  9 ----
 config/locales/fa.yml                         |  9 ----
 config/locales/fi.yml                         |  9 ----
 config/locales/fr.yml                         |  9 ----
 config/locales/gl.yml                         |  9 ----
 config/locales/he.yml                         |  6 ---
 config/locales/hu.yml                         |  9 ----
 config/locales/id.yml                         |  1 -
 config/locales/io.yml                         |  1 -
 config/locales/it.yml                         |  9 ----
 config/locales/ja.yml                         |  9 ----
 config/locales/ko.yml                         |  9 ----
 config/locales/nl.yml                         |  9 ----
 config/locales/no.yml                         |  9 ----
 config/locales/oc.yml                         |  9 ----
 config/locales/pl.yml                         |  9 ----
 config/locales/pt-BR.yml                      |  9 ----
 config/locales/pt.yml                         |  9 ----
 config/locales/ru.yml                         |  9 ----
 config/locales/sk.yml                         |  9 ----
 config/locales/sr-Latn.yml                    |  9 ----
 config/locales/sr.yml                         |  9 ----
 config/locales/sv.yml                         |  9 ----
 config/locales/th.yml                         |  1 -
 config/locales/tr.yml                         |  1 -
 config/locales/uk.yml                         |  1 -
 config/locales/zh-CN.yml                      |  9 ----
 config/locales/zh-HK.yml                      |  9 ----
 config/locales/zh-TW.yml                      |  1 -
 config/routes.rb                              |  4 +-
 .../reported_statuses_controller_spec.rb      | 49 +++--------------
 .../admin/statuses_controller_spec.rb         | 48 ++++-------------
 44 files changed, 77 insertions(+), 446 deletions(-)

diff --git a/app/controllers/admin/reported_statuses_controller.rb b/app/controllers/admin/reported_statuses_controller.rb
index 522f68c98e..d3c2f5e9e9 100644
--- a/app/controllers/admin/reported_statuses_controller.rb
+++ b/app/controllers/admin/reported_statuses_controller.rb
@@ -3,7 +3,6 @@
 module Admin
   class ReportedStatusesController < BaseController
     before_action :set_report
-    before_action :set_status, only: [:update, :destroy]
 
     def create
       authorize :status, :update?
@@ -14,20 +13,6 @@ module Admin
       redirect_to admin_report_path(@report)
     end
 
-    def update
-      authorize @status, :update?
-      @status.update!(status_params)
-      log_action :update, @status
-      redirect_to admin_report_path(@report)
-    end
-
-    def destroy
-      authorize @status, :destroy?
-      RemovalWorker.perform_async(@status.id)
-      log_action :destroy, @status
-      render json: @status
-    end
-
     private
 
     def status_params
@@ -51,9 +36,5 @@ module Admin
     def set_report
       @report = Report.find(params[:report_id])
     end
-
-    def set_status
-      @status = @report.statuses.find(params[:id])
-    end
   end
 end
diff --git a/app/controllers/admin/statuses_controller.rb b/app/controllers/admin/statuses_controller.rb
index d5787acfb9..382bfc4a23 100644
--- a/app/controllers/admin/statuses_controller.rb
+++ b/app/controllers/admin/statuses_controller.rb
@@ -5,7 +5,6 @@ module Admin
     helper_method :current_params
 
     before_action :set_account
-    before_action :set_status, only: [:update, :destroy]
 
     PER_PAGE = 20
 
@@ -26,40 +25,18 @@ module Admin
     def create
       authorize :status, :update?
 
-      @form         = Form::StatusBatch.new(form_status_batch_params.merge(current_account: current_account))
+      @form         = Form::StatusBatch.new(form_status_batch_params.merge(current_account: current_account, action: action_from_button))
       flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save
 
       redirect_to admin_account_statuses_path(@account.id, current_params)
     end
 
-    def update
-      authorize @status, :update?
-      @status.update!(status_params)
-      log_action :update, @status
-      redirect_to admin_account_statuses_path(@account.id, current_params)
-    end
-
-    def destroy
-      authorize @status, :destroy?
-      RemovalWorker.perform_async(@status.id)
-      log_action :destroy, @status
-      render json: @status
-    end
-
     private
 
-    def status_params
-      params.require(:status).permit(:sensitive)
-    end
-
     def form_status_batch_params
       params.require(:form_status_batch).permit(:action, status_ids: [])
     end
 
-    def set_status
-      @status = @account.statuses.find(params[:id])
-    end
-
     def set_account
       @account = Account.find(params[:account_id])
     end
@@ -72,5 +49,15 @@ module Admin
         page: page > 1 && page,
       }.select { |_, value| value.present? }
     end
+
+    def action_from_button
+      if params[:nsfw_on]
+        'nsfw_on'
+      elsif params[:nsfw_off]
+        'nsfw_off'
+      elsif params[:delete]
+        'delete'
+      end
+    end
   end
 end
diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss
index a6cc8b62ba..1948a2a231 100644
--- a/app/javascript/styles/mastodon/admin.scss
+++ b/app/javascript/styles/mastodon/admin.scss
@@ -336,7 +336,8 @@
   }
 }
 
-.simple_form.new_report_note {
+.simple_form.new_report_note,
+.simple_form.new_account_moderation_note {
   max-width: 100%;
 }
 
diff --git a/app/views/admin/account_moderation_notes/_account_moderation_note.html.haml b/app/views/admin/account_moderation_notes/_account_moderation_note.html.haml
index 6761a43192..432fb79a6e 100644
--- a/app/views/admin/account_moderation_notes/_account_moderation_note.html.haml
+++ b/app/views/admin/account_moderation_notes/_account_moderation_note.html.haml
@@ -1,10 +1,7 @@
-%tr
-  %td
+.speech-bubble
+  .speech-bubble__bubble
     = simple_format(h(account_moderation_note.content))
-  %td
-    = account_moderation_note.account.acct
-  %td
-    %time.formatted{ datetime: account_moderation_note.created_at.iso8601, title: l(account_moderation_note.created_at) }
-      = l account_moderation_note.created_at
-  %td
-    = link_to t('admin.account_moderation_notes.delete'), admin_account_moderation_note_path(account_moderation_note), method: :delete if can?(:destroy, account_moderation_note)
+  .speech-bubble__owner
+    = admin_account_link_to account_moderation_note.account
+    %time.formatted{ datetime: account_moderation_note.created_at.iso8601 }= l account_moderation_note.created_at
+    = table_link_to 'trash', t('admin.account_moderation_notes.delete'), admin_account_moderation_note_path(account_moderation_note), method: :delete if can?(:destroy, account_moderation_note)
diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml
index 7312618ee2..2e6de18643 100644
--- a/app/views/admin/accounts/show.html.haml
+++ b/app/views/admin/accounts/show.html.haml
@@ -2,7 +2,7 @@
   = @account.acct
 
 .table-wrapper
-  %table.table
+  %table.table.inline-table
     %tbody
       %tr
         %th= t('admin.accounts.username')
@@ -73,17 +73,17 @@
 
       %tr
         %th= t('admin.accounts.follows')
-        %td= @account.following_count
+        %td= number_to_human @account.following_count
       %tr
         %th= t('admin.accounts.followers')
-        %td= @account.followers_count
+        %td= number_to_human @account.followers_count
       %tr
         %th= t('admin.accounts.statuses')
-        %td= link_to @account.statuses_count, admin_account_statuses_path(@account.id)
+        %td= link_to number_to_human(@account.statuses_count), admin_account_statuses_path(@account.id)
       %tr
         %th= t('admin.accounts.media_attachments')
         %td
-          = link_to @account.media_attachments.count, admin_account_statuses_path(@account.id, { media: true })
+          = link_to number_to_human(@account.media_attachments.count), admin_account_statuses_path(@account.id, { media: true })
           = surround '(', ')' do
             = number_to_human_size @account.media_attachments.sum('file_file_size')
       %tr
@@ -120,11 +120,12 @@
       = link_to t('admin.accounts.perform_full_suspension'), admin_account_suspension_path(@account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button' if can?(:suspend, @account)
 
 - if !@account.local? && @account.hub_url.present?
-  %hr
+  %hr.spacer/
+
   %h3 OStatus
 
   .table-wrapper
-    %table.table
+    %table.table.inline-table
       %tbody
         %tr
           %th= t('admin.accounts.feed_url')
@@ -148,11 +149,12 @@
         = link_to t('admin.accounts.unsubscribe'), unsubscribe_admin_account_path(@account.id), method: :post, class: 'button negative' if can?(:unsubscribe, @account)
 
 - if !@account.local? && @account.inbox_url.present?
-  %hr
+  %hr.spacer/
+
   %h3 ActivityPub
 
   .table-wrapper
-    %table.table
+    %table.table.inline-table
       %tbody
         %tr
           %th= t('admin.accounts.inbox_url')
@@ -167,24 +169,15 @@
           %th= t('admin.accounts.followers_url')
           %td= link_to @account.followers_url, @account.followers_url
 
-%hr
-%h3= t('admin.accounts.moderation_notes')
+%hr.spacer/
+
+= render @moderation_notes
 
 = simple_form_for @account_moderation_note, url: admin_account_moderation_notes_path do |f|
   = render 'shared/error_messages', object: @account_moderation_note
 
-  = f.input :content
+  = f.input :content, placeholder: t('admin.reports.notes.placeholder'), rows: 6
   = f.hidden_field :target_account_id
 
   .actions
-  = f.button :button, t('admin.account_moderation_notes.create'), type: :submit
-
-.table-wrapper
-  %table.table
-    %thead
-      %tr
-        %th
-        %th= t('admin.account_moderation_notes.account')
-        %th= t('admin.account_moderation_notes.created_at')
-    %tbody
-      = render @moderation_notes
+    = f.button :button, t('admin.account_moderation_notes.create'), type: :submit
diff --git a/app/views/admin/reports/_status.html.haml b/app/views/admin/reports/_status.html.haml
index 137609539b..9057e6048d 100644
--- a/app/views/admin/reports/_status.html.haml
+++ b/app/views/admin/reports/_status.html.haml
@@ -7,7 +7,7 @@
         %p><
           %strong= Formatter.instance.format_spoiler(status)
 
-      = Formatter.instance.format(status)
+      = Formatter.instance.format(status, custom_emojify: true)
 
     - unless status.media_attachments.empty?
       - if status.media_attachments.first.video?
diff --git a/app/views/admin/statuses/index.html.haml b/app/views/admin/statuses/index.html.haml
index fe25815274..880a24f769 100644
--- a/app/views/admin/statuses/index.html.haml
+++ b/app/views/admin/statuses/index.html.haml
@@ -3,11 +3,8 @@
 
 - content_for :page_title do
   = t('admin.statuses.title')
-
-.back-link
-  = link_to admin_account_path(@account.id) do
-    %i.fa.fa-chevron-left.fa-fw
-    = t('admin.statuses.back_to_account')
+  \-
+  = "@#{@account.acct}"
 
 .filters
   .filter-subset
@@ -15,33 +12,26 @@
     %ul
       %li= link_to t('admin.statuses.no_media'), admin_account_statuses_path(@account.id, current_params.merge(media: nil)), class: !params[:media] && 'selected'
       %li= link_to t('admin.statuses.with_media'), admin_account_statuses_path(@account.id, current_params.merge(media: true)), class: params[:media] && 'selected'
+  .back-link{ style: 'flex: 1 1 auto; text-align: right' }
+    = link_to admin_account_path(@account.id) do
+      %i.fa.fa-chevron-left.fa-fw
+      = t('admin.statuses.back_to_account')
 
-- if @statuses.empty?
-  .accounts-grid
-    = render 'accounts/nothing_here'
-- else
-  = form_for(@form, url: admin_account_statuses_path(@account.id)) do |f|
-    = hidden_field_tag :page, params[:page]
-    = hidden_field_tag :media, params[:media]
-    .batch-form-box
-      .batch-checkbox-all
+%hr.spacer/
+
+= form_for(@form, url: admin_account_statuses_path(@account.id)) do |f|
+  = hidden_field_tag :page, params[:page]
+  = hidden_field_tag :media, params[:media]
+
+  .batch-table
+    .batch-table__toolbar
+      %label.batch-table__toolbar__select.batch-checkbox-all
         = check_box_tag :batch_checkbox_all, nil, false
-      = f.select :action, Form::StatusBatch::ACTION_TYPE.map{|action| [t("admin.statuses.batch.#{action}"), action]}
-      = f.submit t('admin.statuses.execute'), data: { confirm: t('admin.reports.are_you_sure') }, class: 'button'
-      .media-spoiler-toggle-buttons
-        .media-spoiler-show-button.button= t('admin.statuses.media.show')
-        .media-spoiler-hide-button.button= t('admin.statuses.media.hide')
-    - @statuses.each do |status|
-      .account-status{ data: { id: status.id } }
-        .batch-checkbox
-          = f.check_box :status_ids, { multiple: true, include_hidden: false }, status.id
-        .activity-stream.activity-stream-headless
-          .entry= render 'stream_entries/simple_status', status: status
-        .account-status__actions
-          - unless status.media_attachments.empty?
-            = link_to admin_account_status_path(@account.id, status, current_params.merge(status: { sensitive: !status.sensitive })), method: :patch, class: 'icon-button nsfw-button', title: t("admin.reports.nsfw.#{!status.sensitive}") do
-              = fa_icon status.sensitive? ? 'eye' : 'eye-slash'
-          = link_to admin_account_status_path(@account.id, status), method: :delete, class: 'icon-button trash-button', title: t('admin.reports.delete'), data: { confirm: t('admin.reports.are_you_sure') }, remote: true do
-            = fa_icon 'trash'
+      .batch-table__toolbar__actions
+        = f.button safe_join([fa_icon('eye-slash'), t('admin.statuses.batch.nsfw_on')]), name: :nsfw_on, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
+        = f.button safe_join([fa_icon('eye'), t('admin.statuses.batch.nsfw_off')]), name: :nsfw_off, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
+        = f.button safe_join([fa_icon('trash'), t('admin.statuses.batch.delete')]), name: :delete, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
+    .batch-table__body
+      = render partial: 'admin/reports/status', collection: @statuses, locals: { f: f }
 
 = paginate @statuses
diff --git a/config/locales/ar.yml b/config/locales/ar.yml
index e9ca3038e4..a5179ae148 100644
--- a/config/locales/ar.yml
+++ b/config/locales/ar.yml
@@ -52,9 +52,7 @@ ar:
     unfollow: إلغاء المتابعة
   admin:
     account_moderation_notes:
-      account: مُشرِف
       create: إنشاء
-      created_at: التاريخ
       created_msg: تم إنشاء ملاحظة الإشراف بنجاح !
       delete: حذف
       destroyed_msg: تم تدمير ملاحظة الإشراف بنجاح !
@@ -241,12 +239,8 @@ ar:
       are_you_sure: هل أنت متأكد ؟
       comment:
         none: لا شيء
-      delete: حذف
       id: معرّف ID
       mark_as_resolved: إعتبار التقرير كمحلول
-      nsfw:
-        'false': الكشف عن الصور
-        'true': إخفاء الوسائط المرفقة
       report: 'التقرير #%{id}'
       report_contents: المحتويات
       reported_account: حساب مُبلّغ عنه
@@ -305,11 +299,8 @@ ar:
       back_to_account: العودة إلى صفحة الحساب
       batch:
         delete: حذف
-      execute: تفعيل
       failed_to_execute: خطأ في التفعيل
       media:
-        hide: إخفاء الوسائط
-        show: إظهار الوسائط
         title: الوسائط
       title: منشورات الحساب
       with_media: بالوسائط
diff --git a/config/locales/ca.yml b/config/locales/ca.yml
index 2758bb487a..a8c75c32df 100644
--- a/config/locales/ca.yml
+++ b/config/locales/ca.yml
@@ -53,9 +53,7 @@ ca:
     unfollow: Deixa de seguir
   admin:
     account_moderation_notes:
-      account: Moderador
       create: Crea
-      created_at: Data
       created_msg: La nota de moderació s'ha creat correctament!
       delete: Suprimeix
       destroyed_msg: Nota de moderació destruïda amb èxit!
@@ -269,7 +267,6 @@ ca:
       comment:
         none: Cap
       created_at: Reportat
-      delete: Suprimeix
       id: ID
       mark_as_resolved: Marca com a resolt
       mark_as_unresolved: Marcar sense resoldre
@@ -279,9 +276,6 @@ ca:
         create_and_unresolve: Reobrir amb nota
         delete: Esborrar
         placeholder: Descriu les accions que s'han pres o qualsevol altra actualització d'aquest informe…
-      nsfw:
-        'false': Mostra els fitxers multimèdia adjunts
-        'true': Amaga els fitxers multimèdia adjunts
       reopen: Reobrir informe
       report: 'Informe #%{id}'
       report_contents: Contingut
@@ -356,11 +350,8 @@ ca:
         delete: Suprimeix
         nsfw_off: Marcar com a no sensible
         nsfw_on: Marcar com a sensible
-      execute: Executa
       failed_to_execute: No s'ha pogut executar
       media:
-        hide: Amaga el contingut multimèdia
-        show: Mostra el contingut multimèdia
         title: Contingut multimèdia
       no_media: Sense contingut multimèdia
       title: Estats del compte
diff --git a/config/locales/de.yml b/config/locales/de.yml
index 6b2c087354..aa4295c896 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -53,9 +53,7 @@ de:
     unfollow: Entfolgen
   admin:
     account_moderation_notes:
-      account: Moderator*in
       create: Erstellen
-      created_at: Datum
       created_msg: Moderationsnotiz erfolgreich erstellt!
       delete: Löschen
       destroyed_msg: Moderationsnotiz erfolgreich gelöscht!
@@ -269,7 +267,6 @@ de:
       comment:
         none: Kein
       created_at: Gemeldet
-      delete: Löschen
       id: ID
       mark_as_resolved: Als gelöst markieren
       mark_as_unresolved: Als ungelöst markieren
@@ -279,9 +276,6 @@ de:
         create_and_unresolve: Mit Kommentar wieder öffnen
         delete: Löschen
         placeholder: Beschreibe, welche Maßnahmen ergriffen wurden oder andere Neuigkeiten zu dieser Meldung…
-      nsfw:
-        'false': Medienanhänge wieder anzeigen
-        'true': Medienanhänge verbergen
       reopen: Meldung wieder öffnen
       report: 'Meldung #%{id}'
       report_contents: Inhalt
@@ -356,11 +350,8 @@ de:
         delete: Löschen
         nsfw_off: Als nicht heikel markieren
         nsfw_on: Als heikel markieren
-      execute: Ausführen
       failed_to_execute: Ausführen fehlgeschlagen
       media:
-        hide: Medien verbergen
-        show: Medien anzeigen
         title: Medien
       no_media: Keine Medien
       title: Beiträge des Kontos
diff --git a/config/locales/en.yml b/config/locales/en.yml
index f7127f7948..7e389b1c22 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -53,9 +53,7 @@ en:
     unfollow: Unfollow
   admin:
     account_moderation_notes:
-      account: Moderator
-      create: Create
-      created_at: Date
+      create: Leave note
       created_msg: Moderation note successfully created!
       delete: Delete
       destroyed_msg: Moderation note successfully destroyed!
@@ -269,7 +267,6 @@ en:
       comment:
         none: None
       created_at: Reported
-      delete: Delete
       id: ID
       mark_as_resolved: Mark as resolved
       mark_as_unresolved: Mark as unresolved
@@ -279,9 +276,6 @@ en:
         create_and_unresolve: Reopen with note
         delete: Delete
         placeholder: Describe what actions have been taken, or any other updates to this report…
-      nsfw:
-        'false': Unhide media attachments
-        'true': Hide media attachments
       reopen: Reopen report
       report: 'Report #%{id}'
       report_contents: Contents
@@ -356,11 +350,8 @@ en:
         delete: Delete
         nsfw_off: Mark as not sensitive
         nsfw_on: Mark as sensitive
-      execute: Execute
       failed_to_execute: Failed to execute
       media:
-        hide: Hide media
-        show: Show media
         title: Media
       no_media: No media
       title: Account statuses
diff --git a/config/locales/eo.yml b/config/locales/eo.yml
index c768d8a03d..9c44d0e622 100644
--- a/config/locales/eo.yml
+++ b/config/locales/eo.yml
@@ -52,9 +52,7 @@ eo:
     unfollow: Ne plu sekvi
   admin:
     account_moderation_notes:
-      account: Kontrolanto
       create: Krei
-      created_at: Dato
       created_msg: Kontrola noto sukcese kreita!
       delete: Forigi
       destroyed_msg: Kontrola noto sukcese detruita!
@@ -244,12 +242,8 @@ eo:
       are_you_sure: Ĉu vi certas?
       comment:
         none: Nenio
-      delete: Forigi
       id: ID
       mark_as_resolved: Marki kiel solvita
-      nsfw:
-        'false': Malkaŝi aŭdovidajn kunsendaĵojn
-        'true': Kaŝi aŭdovidajn kunsendaĵojn
       report: 'Signalo #%{id}'
       report_contents: Enhavo
       reported_account: Signalita konto
@@ -320,11 +314,8 @@ eo:
         delete: Forigi
         nsfw_off: Malŝalti NSFW
         nsfw_on: Ŝalti NSFW
-      execute: Ekigi
       failed_to_execute: Ekigo malsukcesa
       media:
-        hide: Kaŝi aŭdovidaĵojn
-        show: Montri aŭdovidaĵojn
         title: Aŭdovidaĵoj
       no_media: Neniu aŭdovidaĵo
       title: Mesaĝoj de la konto
diff --git a/config/locales/es.yml b/config/locales/es.yml
index cd36e3a977..be7dcd81e5 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -53,9 +53,7 @@ es:
     unfollow: Dejar de seguir
   admin:
     account_moderation_notes:
-      account: Moderador
       create: Crear
-      created_at: Fecha
       created_msg: "¡Nota de moderación creada con éxito!"
       delete: Borrar
       destroyed_msg: "¡Nota de moderación destruida con éxito!"
@@ -269,7 +267,6 @@ es:
       comment:
         none: Ninguno
       created_at: Denunciado
-      delete: Eliminar
       id: ID
       mark_as_resolved: Marcar como resuelto
       mark_as_unresolved: Marcar como no resuelto
@@ -279,9 +276,6 @@ es:
         create_and_unresolve: Reabrir con una nota
         delete: Eliminar
         placeholder: Especificar qué acciones se han tomado o cualquier otra novedad respecto a esta denuncia…
-      nsfw:
-        'false': Mostrar multimedia
-        'true': Ocultar multimedia
       reopen: Reabrir denuncia
       report: 'Reportar #%{id}'
       report_contents: Contenido
@@ -356,11 +350,8 @@ es:
         delete: Eliminar
         nsfw_off: Marcar contenido como no sensible
         nsfw_on: Marcar contenido como sensible
-      execute: Ejecutar
       failed_to_execute: Falló al ejecutar
       media:
-        hide: Ocultar multimedia
-        show: Mostrar multimedia
         title: Multimedia
       no_media: No hay multimedia
       title: Estado de las cuentas
diff --git a/config/locales/fa.yml b/config/locales/fa.yml
index a3005547a5..3aa40f0b3e 100644
--- a/config/locales/fa.yml
+++ b/config/locales/fa.yml
@@ -52,9 +52,7 @@ fa:
     unfollow: پایان پیگیری
   admin:
     account_moderation_notes:
-      account: مدیر
       create: نوشتن
-      created_at: تاریخ
       created_msg: یادداشت مدیر با موفقیت ساخته شد!
       delete: پاک کردن
       destroyed_msg: یادداشت مدیر با موفقیت پاک شد!
@@ -244,12 +242,8 @@ fa:
       are_you_sure: آیا مطمئن هستید؟
       comment:
         none: خالی
-      delete: پاک‌کردن
       id: شناسه
       mark_as_resolved: علامت‌گذاری به عنوان حل‌شده
-      nsfw:
-        'false': نمایش پیوست‌های تصویری
-        'true': نهفتن پیوست‌های تصویری
       report: 'گزارش #%{id}'
       report_contents: محتوا
       reported_account: حساب گزارش‌شده
@@ -308,11 +302,8 @@ fa:
         delete: پاک‌کردن
         nsfw_off: NSFW خاموش
         nsfw_on: NSFW روشن
-      execute: اجرا
       failed_to_execute: اجرا نشد
       media:
-        hide: نهفتن رسانه
-        show: نمایش رسانه
         title: رسانه
       no_media: بدون رسانه
       title: نوشته‌های حساب
diff --git a/config/locales/fi.yml b/config/locales/fi.yml
index 550ad1805e..b313c87236 100644
--- a/config/locales/fi.yml
+++ b/config/locales/fi.yml
@@ -52,9 +52,7 @@ fi:
     unfollow: Lopeta seuraaminen
   admin:
     account_moderation_notes:
-      account: Moderaattori
       create: Luo
-      created_at: Päiväys
       created_msg: Moderointimerkinnän luonti onnistui!
       delete: Poista
       destroyed_msg: Moderointimerkinnän poisto onnistui!
@@ -244,12 +242,8 @@ fi:
       are_you_sure: Oletko varma?
       comment:
         none: Ei mitään
-      delete: Poista
       id: Tunniste
       mark_as_resolved: Merkitse ratkaistuksi
-      nsfw:
-        'false': Peru medialiitteiden piilotus
-        'true': Piilota medialiitteet
       report: Raportti nro %{id}
       report_contents: Sisältö
       reported_account: Raportoitu tili
@@ -320,11 +314,8 @@ fi:
         delete: Poista
         nsfw_off: NSFW POIS
         nsfw_on: NSFW PÄÄLLÄ
-      execute: Suorita
       failed_to_execute: Suoritus epäonnistui
       media:
-        hide: Piilota media
-        show: Näytä media
         title: Media
       no_media: Ei mediaa
       title: Tilin tilat
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index 462ce7b4b7..3b543fdb7b 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -53,9 +53,7 @@ fr:
     unfollow: Ne plus suivre
   admin:
     account_moderation_notes:
-      account: Modérateur·ice
       create: Créer
-      created_at: Date
       created_msg: Note de modération créée avec succès !
       delete: Supprimer
       destroyed_msg: Note de modération supprimée avec succès !
@@ -269,7 +267,6 @@ fr:
       comment:
         none: Aucun
       created_at: Signalé
-      delete: Supprimer
       id: ID
       mark_as_resolved: Marquer comme résolu
       mark_as_unresolved: Marquer comme non-résolu
@@ -279,9 +276,6 @@ fr:
         create_and_unresolve: Ré-ouvrir avec une note
         delete: Effacer
         placeholder: Décrivez quelles actions ont été prises, ou toute autre mise à jour de ce signalement…
-      nsfw:
-        'false': Ré-afficher les médias
-        'true': Masquer les médias
       reopen: Ré-ouvrir le signalement
       report: 'Signalement #%{id}'
       report_contents: Contenu
@@ -356,11 +350,8 @@ fr:
         delete: Supprimer
         nsfw_off: Marquer comme non-sensible
         nsfw_on: Marquer comme sensible
-      execute: Exécuter
       failed_to_execute: Erreur d’exécution
       media:
-        hide: Masquer les médias
-        show: Montrer les médias
         title: Médias
       no_media: Aucun média
       title: État du compte
diff --git a/config/locales/gl.yml b/config/locales/gl.yml
index 6f22702247..eec1b4e1ef 100644
--- a/config/locales/gl.yml
+++ b/config/locales/gl.yml
@@ -53,9 +53,7 @@ gl:
     unfollow: Deixar de seguir
   admin:
     account_moderation_notes:
-      account: Moderador
       create: Crear
-      created_at: Data
       created_msg: Nota a moderación creada con éxito!
       delete: Eliminar
       destroyed_msg: Nota a moderación destruída con éxito!
@@ -269,7 +267,6 @@ gl:
       comment:
         none: Nada
       created_at: Reportado
-      delete: Eliminar
       id: ID
       mark_as_resolved: Marcar como resolto
       mark_as_unresolved: Marcar como non resolto
@@ -279,9 +276,6 @@ gl:
         create_and_unresolve: Voltar a abrir con nota
         delete: Eliminar
         placeholder: Describir qué decisións foron tomadas, ou calquer actualización a este informe…
-      nsfw:
-        'false': Non agochar anexos de medios
-        'true': Agochar anexos de medios
       reopen: Voltar a abrir o informe
       report: 'Informe #%{id}'
       report_contents: Contidos
@@ -356,11 +350,8 @@ gl:
         delete: Eliminar
         nsfw_off: Marcar como non sensible
         nsfw_on: Marcar como sensible
-      execute: Executar
       failed_to_execute: Fallou a execución
       media:
-        hide: Agochar medios
-        show: Mostar medios
         title: Medios
       no_media: Sen medios
       title: Estados da conta
diff --git a/config/locales/he.yml b/config/locales/he.yml
index d641c6e1a5..d66d9f0346 100644
--- a/config/locales/he.yml
+++ b/config/locales/he.yml
@@ -52,9 +52,7 @@ he:
     unfollow: הפסקת מעקב
   admin:
     account_moderation_notes:
-      account: מנחה דיון
       create: ליצור
-      created_at: תאריך
       created_msg: הודעת מנחה נוצרה בהצלחה!
       delete: למחוק
       destroyed_msg: הודעת מנחה נמחקה בהצלחה!
@@ -181,12 +179,8 @@ he:
       are_you_sure: 100% על בטוח?
       comment:
         none: ללא
-      delete: מחיקה
       id: ID
       mark_as_resolved: סימון כפתור
-      nsfw:
-        'false': לכל המשפחה
-        'true': תוכן רגיש
       report: 'דווח על #%{id}'
       report_contents: תוכן
       reported_account: חשבון מדווח
diff --git a/config/locales/hu.yml b/config/locales/hu.yml
index 7fe431d377..422c059559 100644
--- a/config/locales/hu.yml
+++ b/config/locales/hu.yml
@@ -52,9 +52,7 @@ hu:
     unfollow: Követés abbahagyása
   admin:
     account_moderation_notes:
-      account: Moderátor
       create: Új bejegyzés
-      created_at: Dátum
       created_msg: Moderációs bejegyzés létrehozva!
       delete: Törlés
       destroyed_msg: Moderációs bejegyzés törölve!
@@ -244,12 +242,8 @@ hu:
       are_you_sure: Biztos vagy benne?
       comment:
         none: Egyik sem
-      delete: Törlés
       id: ID
       mark_as_resolved: Megjelölés megoldottként
-      nsfw:
-        'false': Média-csatolmányok rejtésének feloldása
-        'true': Média-csatolmányok elrejtése
       report: "#%{id} számú jelentés"
       report_contents: Tartalom
       reported_account: Bejelentett fiók
@@ -314,11 +308,8 @@ hu:
         delete: Törlés
         nsfw_off: Szenzitív tartalom kikapcsolva
         nsfw_on: Szenzitív tartalom bekapcsolva
-      execute: Végrehajt
       failed_to_execute: Végrehajtás sikertelen
       media:
-        hide: Média elrejtése
-        show: Média megjelenítése
         title: Média
       no_media: Nem található médiafájl
       title: Felhasználó tülkjei
diff --git a/config/locales/id.yml b/config/locales/id.yml
index 5a63b8038a..0476e2848e 100644
--- a/config/locales/id.yml
+++ b/config/locales/id.yml
@@ -107,7 +107,6 @@ id:
     reports:
       comment:
         none: Tidak ada
-      delete: Hapus
       id: ID
       mark_as_resolved: Tandai telah diseleseikan
       report: 'Laporkan #%{id}'
diff --git a/config/locales/io.yml b/config/locales/io.yml
index 7c25acc47a..bf15de4886 100644
--- a/config/locales/io.yml
+++ b/config/locales/io.yml
@@ -106,7 +106,6 @@ io:
     reports:
       comment:
         none: None
-      delete: Delete
       id: ID
       mark_as_resolved: Mark as resolved
       report: 'Report #%{id}'
diff --git a/config/locales/it.yml b/config/locales/it.yml
index e010be02d9..c1ac176c45 100644
--- a/config/locales/it.yml
+++ b/config/locales/it.yml
@@ -49,9 +49,7 @@ it:
     unfollow: Non seguire più
   admin:
     account_moderation_notes:
-      account: Moderatore
       create: Crea
-      created_at: Data
       created_msg: Nota di moderazione creata con successo!
       delete: Elimina
       destroyed_msg: Nota di moderazione distrutta con successo!
@@ -214,7 +212,6 @@ it:
       assigned: Moderatore assegnato
       comment:
         none: Nessuno
-      delete: Elimina
       id: ID
       mark_as_resolved: Segna come risolto
       mark_as_unresolved: Segna come non risolto
@@ -223,9 +220,6 @@ it:
         create_and_resolve: Risolvi con nota
         create_and_unresolve: Riapri con nota
         delete: Elimina
-      nsfw:
-        'false': Mostra gli allegati multimediali
-        'true': Nascondi allegati multimediali
       report_contents: Contenuti
       resolved: Risolto
       silence_account: Silenzia account
@@ -266,11 +260,8 @@ it:
         delete: Elimina
         nsfw_off: NSFW OFF
         nsfw_on: NSFW ON
-      execute: Esegui
       failed_to_execute: Impossibile eseguire
       media:
-        hide: Nascondi media
-        show: Mostra media
         title: Media
       no_media: Nessun media
       with_media: con media
diff --git a/config/locales/ja.yml b/config/locales/ja.yml
index 5b3cc5f159..bc48613b98 100644
--- a/config/locales/ja.yml
+++ b/config/locales/ja.yml
@@ -53,9 +53,7 @@ ja:
     unfollow: フォロー解除
   admin:
     account_moderation_notes:
-      account: モデレータ
       create: 書き込む
-      created_at: 日付
       created_msg: モデレーションメモを書き込みました!
       delete: 削除
       destroyed_msg: モデレーションメモを削除しました!
@@ -269,7 +267,6 @@ ja:
       comment:
         none: なし
       created_at: レポート日時
-      delete: 削除
       id: ID
       mark_as_resolved: 解決済みとしてマーク
       mark_as_unresolved: 未解決として再び開く
@@ -279,9 +276,6 @@ ja:
         create_and_unresolve: 書き込み、未解決として開く
         delete: 削除
         placeholder: このレポートに取られた措置や、その他の更新を記述してください…
-      nsfw:
-        'false': NSFW オフ
-        'true': NSFW オン
       reopen: 再び開く
       report: レポート#%{id}
       report_contents: 内容
@@ -356,11 +350,8 @@ ja:
         delete: 削除
         nsfw_off: 閲覧注意のマークを取り除く
         nsfw_on: 閲覧注意としてマークする
-      execute: 実行
       failed_to_execute: 実行に失敗しました
       media:
-        hide: メディアを隠す
-        show: メディアを表示
         title: メディア
       no_media: メディアなし
       title: トゥート一覧
diff --git a/config/locales/ko.yml b/config/locales/ko.yml
index 0f3c6483fb..bd6717d497 100644
--- a/config/locales/ko.yml
+++ b/config/locales/ko.yml
@@ -53,9 +53,7 @@ ko:
     unfollow: 팔로우 해제
   admin:
     account_moderation_notes:
-      account: 모더레이터
       create: 작성하기
-      created_at: 작성 날짜
       created_msg: 모더레이션 기록이 성공적으로 작성되었습니다!
       delete: 삭제
       destroyed_msg: 모더레이션 기록이 성공적으로 삭제되었습니다!
@@ -271,7 +269,6 @@ ko:
       comment:
         none: 없음
       created_at: 리포트 시각
-      delete: 삭제
       id: ID
       mark_as_resolved: 해결 완료 처리
       mark_as_unresolved: 미해결로 표시
@@ -281,9 +278,6 @@ ko:
         create_and_unresolve: 노트 작성과 함께 미해결로 표시
         delete: 삭제
         placeholder: 이 리포트에 대한 조치, 다른 업데이트 사항에 대해 설명합니다…
-      nsfw:
-        'false': NSFW 꺼짐
-        'true': NSFW 켜짐
       reopen: 리포트 다시 열기
       report: '신고 #%{id}'
       report_contents: 내용
@@ -358,11 +352,8 @@ ko:
         delete: 삭제
         nsfw_off: NSFW 끄기
         nsfw_on: NSFW 켜기
-      execute: 실행
       failed_to_execute: 실행을 실패하였습니다
       media:
-        hide: 미디어 숨기기
-        show: 미디어 보여주기
         title: 미디어
       no_media: 미디어 없음
       title: 계정 툿
diff --git a/config/locales/nl.yml b/config/locales/nl.yml
index 7a488bb0fa..00f21babd0 100644
--- a/config/locales/nl.yml
+++ b/config/locales/nl.yml
@@ -53,9 +53,7 @@ nl:
     unfollow: Ontvolgen
   admin:
     account_moderation_notes:
-      account: Moderator
       create: Aanmaken
-      created_at: Datum
       created_msg: Aanmaken van opmerking voor moderatoren geslaagd!
       delete: Verwijderen
       destroyed_msg: Verwijderen van opmerking voor moderatoren geslaagd!
@@ -269,7 +267,6 @@ nl:
       comment:
         none: Geen
       created_at: Gerapporteerd op
-      delete: Verwijderen
       id: ID
       mark_as_resolved: Markeer als opgelost
       mark_as_unresolved: Markeer als onopgelost
@@ -279,9 +276,6 @@ nl:
         create_and_unresolve: Heropenen met opmerking
         delete: Verwijderen
         placeholder: Beschrijf welke acties zijn ondernomen of andere opmerkingen over deze gerapporteerde toot…
-      nsfw:
-        'false': Media tonen
-        'true': Media verbergen
       reopen: Gerapporteerde toot heropenen
       report: 'Gerapporteerde toot #%{id}'
       report_contents: Inhoud
@@ -356,11 +350,8 @@ nl:
         delete: Verwijderen
         nsfw_off: Als niet gevoelig markeren
         nsfw_on: Als gevoelig markeren
-      execute: Uitvoeren
       failed_to_execute: Uitvoeren mislukt
       media:
-        hide: Media verbergen
-        show: Media tonen
         title: Media
       no_media: Geen media
       title: Toots van account
diff --git a/config/locales/no.yml b/config/locales/no.yml
index 8b84182af6..6ce2c0f013 100644
--- a/config/locales/no.yml
+++ b/config/locales/no.yml
@@ -52,9 +52,7 @@
     unfollow: Slutte følge
   admin:
     account_moderation_notes:
-      account: Moderator
       create: Lag
-      created_at: Dato
       created_msg: Moderasjonsnotat laget uten problem!
       delete: Slett
       destroyed_msg: Moderasjonsnotat slettet uten problem!
@@ -244,12 +242,8 @@
       are_you_sure: Er du sikker?
       comment:
         none: Ingen
-      delete: Slett
       id: ID
       mark_as_resolved: Merk som løst
-      nsfw:
-        'false': Vis mediavedlegg
-        'true': Skjul mediavedlegg
       report: 'Rapportér #%{id}'
       report_contents: Innhold
       reported_account: Rapportert konto
@@ -314,11 +308,8 @@
         delete: Slett
         nsfw_off: NSFW AV
         nsfw_on: NSFW PÅ
-      execute: Utfør
       failed_to_execute: Utføring mislyktes
       media:
-        hide: Skjul media
-        show: Vis media
         title: Media
       no_media: Ingen media
       title: Kontostatuser
diff --git a/config/locales/oc.yml b/config/locales/oc.yml
index c248ffd85a..c11fe614b0 100644
--- a/config/locales/oc.yml
+++ b/config/locales/oc.yml
@@ -53,9 +53,7 @@ oc:
     unfollow: Quitar de sègre
   admin:
     account_moderation_notes:
-      account: Moderator
       create: Crear
-      created_at: Data
       created_msg: Nòta de moderacion ben creada !
       delete: Suprimir
       destroyed_msg: Nòta de moderacion ben suprimida !
@@ -265,7 +263,6 @@ oc:
       comment:
         none: Pas cap
       created_at: Creacion
-      delete: Suprimir
       id: ID
       mark_as_resolved: Marcar coma resolgut
       mark_as_unresolved: Marcar coma pas resolgut
@@ -274,9 +271,6 @@ oc:
         create_and_resolve: Resòlvre amb una nòta
         create_and_unresolve: Tornar dobrir amb una nòta
         placeholder: Explicatz las accions que son estadas menadas o çò qu’es estat fach per aqueste rapòrt…
-      nsfw:
-        'false': Sens contengut sensible
-        'true': Contengut sensible activat
       reopen: Tornar dobrir lo rapòrt
       report: 'senhalament #%{id}'
       report_contents: Contenguts
@@ -348,11 +342,8 @@ oc:
         delete: Suprimir
         nsfw_off: NSFW OFF
         nsfw_on: NSFW ON
-      execute: Lançar
       failed_to_execute: Fracàs
       media:
-        hide: Amagar mèdia
-        show: Mostrar mèdia
         title: Mèdia
       no_media: Cap mèdia
       title: Estatuts del compte
diff --git a/config/locales/pl.yml b/config/locales/pl.yml
index 7455df2c02..bd38aa6f77 100644
--- a/config/locales/pl.yml
+++ b/config/locales/pl.yml
@@ -53,9 +53,7 @@ pl:
     unfollow: Przestań śledzić
   admin:
     account_moderation_notes:
-      account: Autor
       create: Dodaj
-      created_at: Data
       created_msg: Pomyślnie dodano notatkę moderacyjną!
       delete: Usuń
       destroyed_msg: Pomyślnie usunięto notatkę moderacyjną!
@@ -270,7 +268,6 @@ pl:
       comment:
         none: Brak
       created_at: Zgłoszono
-      delete: Usuń
       id: ID
       mark_as_resolved: Oznacz jako rozwiązane
       mark_as_unresolved: Oznacz jako nierozwiązane
@@ -280,9 +277,6 @@ pl:
         create_and_unresolve: Cofnij rozwiązanie i pozostaw notatkę
         delete: Usuń
         placeholder: Opisz wykonane akcje i inne szczegóły dotyczące tego zgłoszenia…
-      nsfw:
-        'false': Nie oznaczaj jako NSFW
-        'true': Oznaczaj jako NSFW
       reopen: Otwórz ponownie
       report: 'Zgłoszenie #%{id}'
       report_contents: Zawartość
@@ -357,11 +351,8 @@ pl:
         delete: Usuń
         nsfw_off: Cofnij NSFW
         nsfw_on: Oznacz jako NSFW
-      execute: Wykonaj
       failed_to_execute: Nie udało się wykonać
       media:
-        hide: Ukryj zawartość multimedialną
-        show: Pokaż zawartość multimedialną
         title: Media
       no_media: Bez zawartości multimedialnej
       title: Wpisy konta
diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml
index 4a574a575d..862833d873 100644
--- a/config/locales/pt-BR.yml
+++ b/config/locales/pt-BR.yml
@@ -53,9 +53,7 @@ pt-BR:
     unfollow: Deixar de seguir
   admin:
     account_moderation_notes:
-      account: Moderador
       create: Criar
-      created_at: Data
       created_msg: Nota de moderação criada com sucesso!
       delete: Excluir
       destroyed_msg: Nota de moderação excluída com sucesso!
@@ -269,7 +267,6 @@ pt-BR:
       comment:
         none: Nenhum
       created_at: Denunciado
-      delete: Excluir
       id: ID
       mark_as_resolved: Marcar como resolvido
       mark_as_unresolved: Marcar como não resolvido
@@ -279,9 +276,6 @@ pt-BR:
         create_and_unresolve: Reabrir com nota
         delete: Excluir
         placeholder: Descreva que ações foram tomadas, ou quaisquer atualizações sobre esta denúncia…
-      nsfw:
-        'false': Mostrar mídias anexadas
-        'true': Esconder mídias anexadas
       reopen: Reabrir denúncia
       report: 'Denúncia #%{id}'
       report_contents: Conteúdos
@@ -356,11 +350,8 @@ pt-BR:
         delete: Deletar
         nsfw_off: Marcar como não-sensível
         nsfw_on: Marcar como sensível
-      execute: Executar
       failed_to_execute: Falha em executar
       media:
-        hide: Esconder mídia
-        show: Mostrar mídia
         title: Mídia
       no_media: Não há mídia
       title: Postagens da conta
diff --git a/config/locales/pt.yml b/config/locales/pt.yml
index fb2a6cad1c..62a7ef0ec4 100644
--- a/config/locales/pt.yml
+++ b/config/locales/pt.yml
@@ -52,9 +52,7 @@ pt:
     unfollow: Deixar de seguir
   admin:
     account_moderation_notes:
-      account: Moderador
       create: Criar
-      created_at: Data
       created_msg: Nota de moderação criada com sucesso!
       delete: Eliminar
       destroyed_msg: Nota de moderação excluída com sucesso!
@@ -244,12 +242,8 @@ pt:
       are_you_sure: Tens a certeza?
       comment:
         none: Nenhum
-      delete: Eliminar
       id: ID
       mark_as_resolved: Marcar como resolvido
-      nsfw:
-        'false': Mostrar imagens/vídeos
-        'true': Esconder imagens/vídeos
       report: 'Denúncia #%{id}'
       report_contents: Conteúdos
       reported_account: Conta denunciada
@@ -314,11 +308,8 @@ pt:
         delete: Eliminar
         nsfw_off: NSFW OFF
         nsfw_on: NSFW ON
-      execute: Executar
       failed_to_execute: Falhou ao executar
       media:
-        hide: Esconder média
-        show: Mostrar média
         title: Média
       no_media: Não há média
       title: Estado das contas
diff --git a/config/locales/ru.yml b/config/locales/ru.yml
index bf42257581..603e1f3cab 100644
--- a/config/locales/ru.yml
+++ b/config/locales/ru.yml
@@ -52,9 +52,7 @@ ru:
     unfollow: Отписаться
   admin:
     account_moderation_notes:
-      account: Модератор
       create: Создать
-      created_at: Дата
       created_msg: Заметка модератора успешно создана!
       delete: Удалить
       destroyed_msg: Заметка модератора успешно удалена!
@@ -246,12 +244,8 @@ ru:
       are_you_sure: Вы уверены?
       comment:
         none: Нет
-      delete: Удалить
       id: ID
       mark_as_resolved: Отметить как разрешенную
-      nsfw:
-        'false': Показать мультимедийные вложения
-        'true': Скрыть мультимедийные вложения
       report: 'Жалоба #%{id}'
       report_contents: Содержимое
       reported_account: Аккаунт нарушителя
@@ -322,11 +316,8 @@ ru:
         delete: Удалить
         nsfw_off: Выключить NSFW
         nsfw_on: Включить NSFW
-      execute: Выполнить
       failed_to_execute: Не удалось выполнить
       media:
-        hide: Скрыть медиаконтент
-        show: Показать медиаконтент
         title: Медиаконтент
       no_media: Без медиаконтента
       title: Статусы аккаунта
diff --git a/config/locales/sk.yml b/config/locales/sk.yml
index fc9e9452c5..7fb097f03d 100644
--- a/config/locales/sk.yml
+++ b/config/locales/sk.yml
@@ -53,9 +53,7 @@ sk:
     unfollow: Prestať sledovať
   admin:
     account_moderation_notes:
-      account: Moderátor
       create: Vytvoriť
-      created_at: Dátum
       created_msg: Poznámka moderátora bola úspešne vytvorená!
       delete: Zmazať
       destroyed_msg: Poznámka moderátora bola úspešne zmazaná!
@@ -268,7 +266,6 @@ sk:
       comment:
         none: Žiadne
       created_at: Nahlásené
-      delete: Vymazať
       id: Identifikácia
       mark_as_resolved: Označiť ako vyriešené
       mark_as_unresolved: Označ ako nevyriešené
@@ -278,9 +275,6 @@ sk:
         create_and_unresolve: Otvor znovu, s poznámkou
         delete: Vymaž
         placeholder: Opíš aké opatrenia boli urobené, alebo akékoľvek iné aktualizácie k tomuto nahláseniu…
-      nsfw:
-        'false': Odkryť mediálne prílohy
-        'true': Skryť mediálne prílohy
       reopen: Znovu otvor report
       report: Nahlásiť
       report_contents: Obsah
@@ -355,11 +349,8 @@ sk:
         delete: Vymazať
         nsfw_off: Obsah nieje chúlostivý
         nsfw_on: Označ obeah aka chúlostivý
-      execute: Vykonať
       failed_to_execute: Nepodarilo sa vykonať
       media:
-        hide: Skryť médiá
-        show: Zobraziť médiá
         title: Médiá
       no_media: Žiadné médiá
       title: Statusy na účte
diff --git a/config/locales/sr-Latn.yml b/config/locales/sr-Latn.yml
index 742c976d19..dba952917a 100644
--- a/config/locales/sr-Latn.yml
+++ b/config/locales/sr-Latn.yml
@@ -52,9 +52,7 @@ sr-Latn:
     unfollow: Otprati
   admin:
     account_moderation_notes:
-      account: Moderator
       create: Napravi
-      created_at: Datum
       created_msg: Moderatorska beleška uspešno napravljena!
       delete: Obriši
       destroyed_msg: Moderatorska beleška uspešno obrisana!
@@ -246,12 +244,8 @@ sr-Latn:
       are_you_sure: Da li ste sigurni?
       comment:
         none: Ništa
-      delete: Obriši
       id: ID
       mark_as_resolved: Označi kao rešen
-      nsfw:
-        'false': Otkrij medijske priloge
-        'true': Sakrij medijske priloge
       report: 'Prijava #%{id}'
       report_contents: Sadržaj
       reported_account: Prijavljeni nalog
@@ -310,11 +304,8 @@ sr-Latn:
         delete: Obriši
         nsfw_off: NSFW isključen
         nsfw_on: NSFW uključen
-      execute: Izvrši
       failed_to_execute: Neuspelo izvršavanje
       media:
-        hide: Sakrij multimediju
-        show: Prikaži multimediju
         title: Multimedija
       no_media: Bez multimedije
       title: Statusi naloga
diff --git a/config/locales/sr.yml b/config/locales/sr.yml
index 0d55910a6c..74abf0323e 100644
--- a/config/locales/sr.yml
+++ b/config/locales/sr.yml
@@ -52,9 +52,7 @@ sr:
     unfollow: Отпрати
   admin:
     account_moderation_notes:
-      account: Модератор
       create: Направи
-      created_at: Датум
       created_msg: Модераторска белешка успешно направљена!
       delete: Обриши
       destroyed_msg: Модераторска белешка успешно обрисана!
@@ -246,12 +244,8 @@ sr:
       are_you_sure: Да ли сте сигурни?
       comment:
         none: Ништа
-      delete: Обриши
       id: ID
       mark_as_resolved: Означи као решен
-      nsfw:
-        'false': Откриј медијске прилоге
-        'true': Сакриј медијске прилоге
       report: 'Пријава #%{id}'
       report_contents: Садржај
       reported_account: Пријављени налог
@@ -310,11 +304,8 @@ sr:
         delete: Обриши
         nsfw_off: NSFW искључен
         nsfw_on: NSFW укључен
-      execute: Изврши
       failed_to_execute: Неуспело извршавање
       media:
-        hide: Сакриј мултимедију
-        show: Прикажи мултимедију
         title: Мултимедија
       no_media: Без мултимедије
       title: Статуси налога
diff --git a/config/locales/sv.yml b/config/locales/sv.yml
index 845248652f..9e0bb08565 100644
--- a/config/locales/sv.yml
+++ b/config/locales/sv.yml
@@ -53,9 +53,7 @@ sv:
     unfollow: Sluta följa
   admin:
     account_moderation_notes:
-      account: Moderator
       create: Skapa
-      created_at: Datum
       created_msg: Modereringsnotering skapad utan problem!
       delete: Ta bort
       destroyed_msg: Modereringsnotering borttagen utan problem!
@@ -269,7 +267,6 @@ sv:
       comment:
         none: Ingen
       created_at: Anmäld
-      delete: Radera
       id: ID
       mark_as_resolved: Markera som löst
       mark_as_unresolved: Markera som olöst
@@ -279,9 +276,6 @@ sv:
         create_and_unresolve: Återuppta med anteckning
         delete: Radera
         placeholder: Beskriv vilka åtgärder som vidtagits eller andra uppdateringar till den här anmälan…
-      nsfw:
-        'false': Visa bifogade mediafiler
-        'true': Dölj bifogade mediafiler
       reopen: Återuppta anmälan
       report: 'Anmäl #%{id}'
       report_contents: Innehåll
@@ -356,11 +350,8 @@ sv:
         delete: Radera
         nsfw_off: Markera som ej känslig
         nsfw_on: Markera som känslig
-      execute: Utför
       failed_to_execute: Misslyckades att utföra
       media:
-        hide: Dölj media
-        show: Visa media
         title: Media
       no_media: Ingen media
       title: Kontostatus
diff --git a/config/locales/th.yml b/config/locales/th.yml
index 350b93b521..9d93115b0d 100644
--- a/config/locales/th.yml
+++ b/config/locales/th.yml
@@ -109,7 +109,6 @@ th:
     reports:
       comment:
         none: None
-      delete: ลบ
       id: ไอดี
       mark_as_resolved: ทำเครื่องหมายว่าจัดการแล้ว
       report: 'Report #%{id}'
diff --git a/config/locales/tr.yml b/config/locales/tr.yml
index 6e7aeb77e9..0d78216b60 100644
--- a/config/locales/tr.yml
+++ b/config/locales/tr.yml
@@ -108,7 +108,6 @@ tr:
     reports:
       comment:
         none: Yok
-      delete: Sil
       id: ID
       mark_as_resolved: Giderildi olarak işaretle
       report: 'Şikayet #%{id}'
diff --git a/config/locales/uk.yml b/config/locales/uk.yml
index 44f64b5c9e..6fe46b4d98 100644
--- a/config/locales/uk.yml
+++ b/config/locales/uk.yml
@@ -100,7 +100,6 @@ uk:
     reports:
       comment:
         none: Немає
-      delete: Видалити
       id: ID
       mark_as_resolved: Відмітити як вирішену
       report: 'Скарга #%{id}'
diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml
index 78c72bd302..7ea491fd4b 100644
--- a/config/locales/zh-CN.yml
+++ b/config/locales/zh-CN.yml
@@ -52,9 +52,7 @@ zh-CN:
     unfollow: 取消关注
   admin:
     account_moderation_notes:
-      account: 管理员
       create: 新建
-      created_at: 日期
       created_msg: 管理备忘建立成功!
       delete: 删除
       destroyed_msg: 管理备忘删除成功!
@@ -242,12 +240,8 @@ zh-CN:
       are_you_sure: 你确定吗?
       comment:
         none: 没有
-      delete: 删除
       id: ID
       mark_as_resolved: 标记为“已处理”
-      nsfw:
-        'false': 取消 NSFW 标记
-        'true': 添加 NSFW 标记
       report: '举报 #%{id}'
       report_contents: 内容
       reported_account: 举报用户
@@ -312,11 +306,8 @@ zh-CN:
         delete: 删除
         nsfw_off: 取消 NSFW 标记
         nsfw_on: 添加 NSFW 标记
-      execute: 执行
       failed_to_execute: 执行失败
       media:
-        hide: 隐藏媒体文件
-        show: 显示媒体文件
         title: 媒体文件
       no_media: 不含媒体文件
       title: 帐户嘟文
diff --git a/config/locales/zh-HK.yml b/config/locales/zh-HK.yml
index a27b0c04c8..3f91b7191d 100644
--- a/config/locales/zh-HK.yml
+++ b/config/locales/zh-HK.yml
@@ -53,9 +53,7 @@ zh-HK:
     unfollow: 取消關注
   admin:
     account_moderation_notes:
-      account: 管理員
       create: 新增
-      created_at: 日期
       created_msg: 管理記錄已新增
       delete: 刪除
       destroyed_msg: 管理記錄已被刪除
@@ -268,7 +266,6 @@ zh-HK:
       comment:
         none: 沒有
       created_at: 日期
-      delete: 刪除
       id: ID
       mark_as_resolved: 標示為「已處理」
       mark_as_unresolved: 標示為「未處理」
@@ -278,9 +275,6 @@ zh-HK:
         create_and_unresolve: 建立筆記並標示為「未處理」
         delete: 刪除
         placeholder: 記錄已執行的動作,或其他更新
-      nsfw:
-        'false': 取消 NSFW 標記
-        'true': 添加 NSFW 標記
       reopen: 重開舉報
       report: '舉報 #%{id}'
       report_contents: 舉報內容
@@ -355,11 +349,8 @@ zh-HK:
         delete: 刪除
         nsfw_off: 取消 NSFW 標記
         nsfw_on: 添加 NSFW 標記
-      execute: 執行
       failed_to_execute: 執行失敗
       media:
-        hide: 隱藏媒體檔案
-        show: 顯示媒體檔案
         title: 媒體檔案
       no_media: 不含媒體檔案
       title: 帳戶文章
diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml
index f69d22d79a..d8c0f89a96 100644
--- a/config/locales/zh-TW.yml
+++ b/config/locales/zh-TW.yml
@@ -80,7 +80,6 @@ zh-TW:
     reports:
       comment:
         none: 無
-      delete: 刪除
       id: ID
       mark_as_resolved: 標記為已解決
       report: '檢舉 #%{id}'
diff --git a/config/routes.rb b/config/routes.rb
index d959301e98..90f6ac594d 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -135,7 +135,7 @@ Rails.application.routes.draw do
     end
 
     resources :reports, only: [:index, :show, :update] do
-      resources :reported_statuses, only: [:create, :update, :destroy]
+      resources :reported_statuses, only: [:create]
     end
 
     resources :report_notes, only: [:create, :destroy]
@@ -156,7 +156,7 @@ Rails.application.routes.draw do
       resource :silence, only: [:create, :destroy]
       resource :suspension, only: [:create, :destroy]
       resource :confirmation, only: [:create]
-      resources :statuses, only: [:index, :create, :update, :destroy]
+      resources :statuses, only: [:index, :create]
 
       resource :role do
         member do
diff --git a/spec/controllers/admin/reported_statuses_controller_spec.rb b/spec/controllers/admin/reported_statuses_controller_spec.rb
index 29957ed37e..7adbf36b9c 100644
--- a/spec/controllers/admin/reported_statuses_controller_spec.rb
+++ b/spec/controllers/admin/reported_statuses_controller_spec.rb
@@ -22,7 +22,7 @@ describe Admin::ReportedStatusesController do
     let(:sensitive) { true }
     let!(:media_attachment) { Fabricate(:media_attachment, status: status) }
 
-    context 'updates sensitive column to true' do
+    context 'when action is nsfw_on' do
       it 'updates sensitive column' do
         is_expected.to change {
           status.reload.sensitive
@@ -30,7 +30,7 @@ describe Admin::ReportedStatusesController do
       end
     end
 
-    context 'updates sensitive column to false' do
+    context 'when action is nsfw_off' do
       let(:action) { 'nsfw_off' }
       let(:sensitive) { false }
 
@@ -41,35 +41,13 @@ describe Admin::ReportedStatusesController do
       end
     end
 
-    it 'redirects to report page' do
-      subject.call
-      expect(response).to redirect_to(admin_report_path(report))
-    end
-  end
+    context 'when action is delete' do
+      let(:action) { 'delete' }
 
-  describe 'PATCH #update' do
-    subject do
-      -> { patch :update, params: { report_id: report, id: status, status: { sensitive: sensitive } } }
-    end
-
-    let(:status) { Fabricate(:status, sensitive: !sensitive) }
-    let(:sensitive) { true }
-
-    context 'updates sensitive column to true' do
-      it 'updates sensitive column' do
-        is_expected.to change {
-          status.reload.sensitive
-        }.from(false).to(true)
-      end
-    end
-
-    context 'updates sensitive column to false' do
-      let(:sensitive) { false }
-
-      it 'updates sensitive column' do
-        is_expected.to change {
-          status.reload.sensitive
-        }.from(true).to(false)
+      it 'removes a status' do
+        allow(RemovalWorker).to receive(:perform_async)
+        subject.call
+        expect(RemovalWorker).to have_received(:perform_async).with(status_ids.first)
       end
     end
 
@@ -78,15 +56,4 @@ describe Admin::ReportedStatusesController do
       expect(response).to redirect_to(admin_report_path(report))
     end
   end
-
-  describe 'DELETE #destroy' do
-    it 'removes a status' do
-      allow(RemovalWorker).to receive(:perform_async)
-
-      delete :destroy, params: { report_id: report, id: status }
-      expect(response).to have_http_status(200)
-      expect(RemovalWorker).
-        to have_received(:perform_async).with(status.id)
-    end
-  end
 end
diff --git a/spec/controllers/admin/statuses_controller_spec.rb b/spec/controllers/admin/statuses_controller_spec.rb
index cbaf397865..6afcc14425 100644
--- a/spec/controllers/admin/statuses_controller_spec.rb
+++ b/spec/controllers/admin/statuses_controller_spec.rb
@@ -34,13 +34,13 @@ describe Admin::StatusesController do
 
   describe 'POST #create' do
     subject do
-      -> { post :create, params: { account_id: account.id, form_status_batch: { action: action, status_ids: status_ids } } }
+      -> { post :create, params: { :account_id => account.id, action => '', :form_status_batch => { status_ids: status_ids } } }
     end
 
     let(:action) { 'nsfw_on' }
     let(:status_ids) { [media_attached_status.id] }
 
-    context 'updates sensitive column to true' do
+    context 'when action is nsfw_on' do
       it 'updates sensitive column' do
         is_expected.to change {
           media_attached_status.reload.sensitive
@@ -48,7 +48,7 @@ describe Admin::StatusesController do
       end
     end
 
-    context 'updates sensitive column to false' do
+    context 'when action is nsfw_off' do
       let(:action) { 'nsfw_off' }
       let(:sensitive) { false }
 
@@ -59,32 +59,13 @@ describe Admin::StatusesController do
       end
     end
 
-    it 'redirects to account statuses page' do
-      subject.call
-      expect(response).to redirect_to(admin_account_statuses_path(account.id))
-    end
-  end
+    context 'when action is delete' do
+      let(:action) { 'delete' }
 
-  describe 'PATCH #update' do
-    subject do
-      -> { patch :update, params: { account_id: account.id, id: media_attached_status, status: { sensitive: sensitive } } }
-    end
-
-    context 'updates sensitive column to true' do
-      it 'updates sensitive column' do
-        is_expected.to change {
-          media_attached_status.reload.sensitive
-        }.from(false).to(true)
-      end
-    end
-
-    context 'updates sensitive column to false' do
-      let(:sensitive) { false }
-
-      it 'updates sensitive column' do
-        is_expected.to change {
-          media_attached_status.reload.sensitive
-        }.from(true).to(false)
+      it 'removes a status' do
+        allow(RemovalWorker).to receive(:perform_async)
+        subject.call
+        expect(RemovalWorker).to have_received(:perform_async).with(status_ids.first)
       end
     end
 
@@ -93,15 +74,4 @@ describe Admin::StatusesController do
       expect(response).to redirect_to(admin_account_statuses_path(account.id))
     end
   end
-
-  describe 'DELETE #destroy' do
-    it 'removes a status' do
-      allow(RemovalWorker).to receive(:perform_async)
-
-      delete :destroy, params: { account_id: account.id, id: status }
-      expect(response).to have_http_status(200)
-      expect(RemovalWorker).
-        to have_received(:perform_async).with(status.id)
-    end
-  end
 end

From 2f63d52b925733520b668a2302e7e38d67251c71 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Sat, 5 May 2018 23:07:51 +0200
Subject: [PATCH 11/40] Fallback to old on-the-fly URI for follows/blocks if no
 stored URI (#7371)

Fix #7367
---
 app/serializers/activitypub/block_serializer.rb  | 2 +-
 app/serializers/activitypub/follow_serializer.rb | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/app/serializers/activitypub/block_serializer.rb b/app/serializers/activitypub/block_serializer.rb
index b3bd9f868e..624ce2fce8 100644
--- a/app/serializers/activitypub/block_serializer.rb
+++ b/app/serializers/activitypub/block_serializer.rb
@@ -5,7 +5,7 @@ class ActivityPub::BlockSerializer < ActiveModel::Serializer
   attribute :virtual_object, key: :object
 
   def id
-    [ActivityPub::TagManager.instance.uri_for(object.account), '#blocks/', object.id].join
+    ActivityPub::TagManager.instance.uri_for(object) || [ActivityPub::TagManager.instance.uri_for(object.account), '#blocks/', object.id].join
   end
 
   def type
diff --git a/app/serializers/activitypub/follow_serializer.rb b/app/serializers/activitypub/follow_serializer.rb
index 24dfe96f89..bb204ee8f3 100644
--- a/app/serializers/activitypub/follow_serializer.rb
+++ b/app/serializers/activitypub/follow_serializer.rb
@@ -5,7 +5,7 @@ class ActivityPub::FollowSerializer < ActiveModel::Serializer
   attribute :virtual_object, key: :object
 
   def id
-    ActivityPub::TagManager.instance.uri_for(object)
+    ActivityPub::TagManager.instance.uri_for(object) || [ActivityPub::TagManager.instance.uri_for(object.account), '#follows/', object.id].join
   end
 
   def type

From b611dbac79f9bae66a6b0fccec017fbd7c3ebee3 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Sun, 6 May 2018 10:52:36 +0200
Subject: [PATCH 12/40] Add contact e-mail hint to 2FA login form (#7376)

---
 app/views/auth/sessions/two_factor.html.haml | 7 +++++--
 config/locales/en.yml                        | 1 +
 config/locales/simple_form.en.yml            | 2 +-
 3 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/app/views/auth/sessions/two_factor.html.haml b/app/views/auth/sessions/two_factor.html.haml
index 2b07c923be..1af3193ae0 100644
--- a/app/views/auth/sessions/two_factor.html.haml
+++ b/app/views/auth/sessions/two_factor.html.haml
@@ -2,9 +2,12 @@
   = t('auth.login')
 
 = simple_form_for(resource, as: resource_name, url: session_path(resource_name), method: :post) do |f|
-  = f.input :otp_attempt, type: :number, placeholder: t('simple_form.labels.defaults.otp_attempt'), input_html: { 'aria-label' => t('simple_form.labels.defaults.otp_attempt'), :autocomplete => 'off' }, required: true, autofocus: true, hint: t('simple_form.hints.sessions.otp')
+  %p.hint{ style: 'margin-bottom: 25px' }= t('simple_form.hints.sessions.otp')
+
+  = f.input :otp_attempt, type: :number, placeholder: t('simple_form.labels.defaults.otp_attempt'), input_html: { 'aria-label' => t('simple_form.labels.defaults.otp_attempt'), :autocomplete => 'off' }, required: true, autofocus: true
 
   .actions
     = f.button :button, t('auth.login'), type: :submit
 
-.form-footer= render 'auth/shared/links'
+  - if Setting.site_contact_email.present?
+    %p.hint.subtle-hint= t('users.otp_lost_help_html', email: mail_to(Setting.site_contact_email, nil))
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 7e389b1c22..bb08ccc547 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -827,5 +827,6 @@ en:
   users:
     invalid_email: The e-mail address is invalid
     invalid_otp_token: Invalid two-factor code
+    otp_lost_help_html: If you lost access to both, you may get in touch with %{email}
     seamless_external_login: You are logged in via an external service, so password and e-mail settings are not available.
     signed_in_as: 'Signed in as:'
diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml
index 1a0d60f710..7ba32906d4 100644
--- a/config/locales/simple_form.en.yml
+++ b/config/locales/simple_form.en.yml
@@ -19,7 +19,7 @@ en:
       imports:
         data: CSV file exported from another Mastodon instance
       sessions:
-        otp: Enter the Two-factor code from your phone or use one of your recovery codes.
+        otp: 'Enter the two-factor code generated by your phone app or use one of your recovery codes:'
       user:
         filtered_languages: Checked languages will be filtered from public timelines for you
     labels:

From 39efc6d533bc3616ef3e199ab83d7753f07afb4c Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Sun, 6 May 2018 10:53:10 +0200
Subject: [PATCH 13/40] Add hint about 7 day cooldown for archive takeout
 (#7375)

---
 app/views/settings/exports/show.html.haml | 6 +++---
 config/locales/en.yml                     | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/app/views/settings/exports/show.html.haml b/app/views/settings/exports/show.html.haml
index 89d768d3fc..30cd269140 100644
--- a/app/views/settings/exports/show.html.haml
+++ b/app/views/settings/exports/show.html.haml
@@ -10,15 +10,15 @@
         %td
       %tr
         %th= t('exports.follows')
-        %td= @export.total_follows
+        %td= number_to_human @export.total_follows
         %td= table_link_to 'download', t('exports.csv'), settings_exports_follows_path(format: :csv)
       %tr
         %th= t('exports.blocks')
-        %td= @export.total_blocks
+        %td= number_to_human @export.total_blocks
         %td= table_link_to 'download', t('exports.csv'), settings_exports_blocks_path(format: :csv)
       %tr
         %th= t('exports.mutes')
-        %td= @export.total_mutes
+        %td= number_to_human @export.total_mutes
         %td= table_link_to 'download', t('exports.csv'), settings_exports_mutes_path(format: :csv)
 
 %p.muted-hint= t('exports.archive_takeout.hint_html')
diff --git a/config/locales/en.yml b/config/locales/en.yml
index bb08ccc547..4f967f9ac4 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -457,7 +457,7 @@ en:
     archive_takeout:
       date: Date
       download: Download your archive
-      hint_html: You can request an archive of your <strong>toots and uploaded media</strong>. The exported data will be in ActivityPub format, readable by any compliant software.
+      hint_html: You can request an archive of your <strong>toots and uploaded media</strong>. The exported data will be in ActivityPub format, readable by any compliant software. You can request an archive every 7 days.
       in_progress: Compiling your archive...
       request: Request your archive
       size: Size

From 8c35d163a5c50eeb30220141036dac0c5dad295c Mon Sep 17 00:00:00 2001
From: Yamagishi Kazutoshi <ykzts@desire.sh>
Date: Sun, 6 May 2018 17:55:50 +0900
Subject: [PATCH 14/40] Port travis_retry for CI (#7379)

* Port travis_retry for CI

* Add license
---
 .circleci/config.yml |  4 ++--
 Gemfile              |  1 -
 Gemfile.lock         |  3 ---
 bin/retry            | 46 ++++++++++++++++++++++++++++++++++++++++++++
 spec/spec_helper.rb  |  8 --------
 5 files changed, 48 insertions(+), 14 deletions(-)
 create mode 100755 bin/retry

diff --git a/.circleci/config.yml b/.circleci/config.yml
index 70d03f6b94..fc9c7e22b7 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -90,7 +90,7 @@ aliases:
             command: ./bin/rails parallel:create parallel:load_schema parallel:prepare
         - run:
             name: Run Tests
-            command: bundle exec parallel_test ./spec/ --group-by filesize --type rspec
+            command: ./bin/retry bundle exec parallel_test ./spec/ --group-by filesize --type rspec
 
 jobs:
   install:
@@ -150,7 +150,7 @@ jobs:
       - image: circleci/node:8.11.1-stretch
     steps:
       - *attach_workspace
-      - run: yarn test:jest
+      - run: ./bin/retry yarn test:jest
 
   check-i18n:
     <<: *defaults
diff --git a/Gemfile b/Gemfile
index 98718f87d0..2beac94d2c 100644
--- a/Gemfile
+++ b/Gemfile
@@ -112,7 +112,6 @@ group :test do
   gem 'microformats', '~> 4.0'
   gem 'rails-controller-testing', '~> 1.0'
   gem 'rspec-sidekiq', '~> 3.0'
-  gem 'rspec-retry', '~> 0.5', require: false
   gem 'simplecov', '~> 0.16', require: false
   gem 'webmock', '~> 3.3'
   gem 'parallel_tests', '~> 2.21'
diff --git a/Gemfile.lock b/Gemfile.lock
index f4be9bf9ca..6a579d53bb 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -501,8 +501,6 @@ GEM
       rspec-expectations (~> 3.7.0)
       rspec-mocks (~> 3.7.0)
       rspec-support (~> 3.7.0)
-    rspec-retry (0.5.7)
-      rspec-core (> 3.3)
     rspec-sidekiq (3.0.3)
       rspec-core (~> 3.0, >= 3.0.0)
       sidekiq (>= 2.4.0)
@@ -726,7 +724,6 @@ DEPENDENCIES
   redis-rails (~> 5.0)
   rqrcode (~> 0.10)
   rspec-rails (~> 3.7)
-  rspec-retry (~> 0.5)
   rspec-sidekiq (~> 3.0)
   rubocop (~> 0.55)
   ruby-progressbar (~> 1.4)
diff --git a/bin/retry b/bin/retry
new file mode 100755
index 0000000000..419ece62a5
--- /dev/null
+++ b/bin/retry
@@ -0,0 +1,46 @@
+#!/bin/bash
+# https://github.com/travis-ci/travis-build/blob/cbe49ea239ab37b9b38b5b44d287b7ec7a108c16/lib/travis/build/templates/header.sh#L243-L260
+#
+# MIT LICENSE
+#
+# Copyright (c) 2016 Travis CI GmbH <contact@travis-ci.org>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+retry() {
+  local result=0
+  local count=1
+
+  while [ $count -le 3 ]; do
+    if [ $result -ne 0 ]; then
+      echo -e "\n${ANSI_RED}The command \"$@\" failed. Retrying, $count of 3.${ANSI_RESET}\n" >&2
+    fi
+
+    "$@" && { result=0 && break; } || result=$?
+    count=$(($count + 1))
+    sleep 1
+  done
+
+  if [ $count -gt 3 ]; then
+    echo -e "\n${ANSI_RED}The command \"$@\" failed 3 times.${ANSI_RESET}\n" >&2
+  fi
+
+  return $result
+}
+
+retry $@
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 9030329373..a0466dd4bf 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,4 +1,3 @@
-#require 'rspec/retry'
 require 'simplecov'
 
 GC.disable
@@ -12,9 +11,6 @@ end
 gc_counter = -1
 
 RSpec.configure do |config|
-  #config.verbose_retry = true
-  #config.display_try_failure_messages = true
-
   config.expect_with :rspec do |expectations|
     expectations.include_chain_clauses_in_custom_matcher_descriptions = true
   end
@@ -29,10 +25,6 @@ RSpec.configure do |config|
     end
   end
 
-  #config.around :each do |ex|
-  #  ex.run_with_retry retry: 3
-  #end
-
   config.before :suite do
     Chewy.strategy(:bypass)
   end

From bd10a7e48076c913a652c347b0477496be8813fa Mon Sep 17 00:00:00 2001
From: Shuhei Kitagawa <shuheiktgw@users.noreply.github.com>
Date: Sun, 6 May 2018 17:59:03 +0900
Subject: [PATCH 15/40] Add resend confirmation for admin (#7378)

* Add confirmations_controller#resend

* Add tests for confirmations_controller#resend

* Add translations
---
 .../admin/confirmations_controller.rb         | 19 ++++++++++++
 app/views/admin/accounts/show.html.haml       | 10 ++++--
 config/locales/ar.yml                         |  6 ++++
 config/locales/ca.yml                         |  6 ++++
 config/locales/de.yml                         |  6 ++++
 config/locales/en.yml                         |  6 ++++
 config/locales/eo.yml                         |  6 ++++
 config/locales/es.yml                         |  6 ++++
 config/locales/fa.yml                         |  6 ++++
 config/locales/fi.yml                         |  6 ++++
 config/locales/fr.yml                         |  6 ++++
 config/locales/gl.yml                         |  6 ++++
 config/locales/he.yml                         |  6 ++++
 config/locales/hu.yml                         |  6 ++++
 config/locales/id.yml                         |  6 ++++
 config/locales/it.yml                         |  6 ++++
 config/locales/ja.yml                         |  6 ++++
 config/locales/ko.yml                         |  6 ++++
 config/locales/nl.yml                         |  6 ++++
 config/locales/no.yml                         |  6 ++++
 config/locales/oc.yml                         |  6 ++++
 config/locales/pl.yml                         |  6 ++++
 config/locales/pt-BR.yml                      |  6 ++++
 config/locales/pt.yml                         |  6 ++++
 config/locales/ru.yml                         |  6 ++++
 config/locales/sk.yml                         |  6 ++++
 config/locales/sr-Latn.yml                    |  6 ++++
 config/locales/sr.yml                         |  6 ++++
 config/locales/sv.yml                         |  6 ++++
 config/locales/th.yml                         |  6 ++++
 config/locales/tr.yml                         |  6 ++++
 config/locales/zh-CN.yml                      |  6 ++++
 config/locales/zh-HK.yml                      |  6 ++++
 config/routes.rb                              |  9 ++++--
 .../admin/confirmations_controller_spec.rb    | 31 +++++++++++++++++++
 35 files changed, 251 insertions(+), 4 deletions(-)

diff --git a/app/controllers/admin/confirmations_controller.rb b/app/controllers/admin/confirmations_controller.rb
index 34dfb458ec..8d3477e660 100644
--- a/app/controllers/admin/confirmations_controller.rb
+++ b/app/controllers/admin/confirmations_controller.rb
@@ -3,6 +3,7 @@
 module Admin
   class ConfirmationsController < BaseController
     before_action :set_user
+    before_action :check_confirmation, only: [:resend]
 
     def create
       authorize @user, :confirm?
@@ -11,10 +12,28 @@ module Admin
       redirect_to admin_accounts_path
     end
 
+    def resend
+      authorize @user, :confirm?
+
+      @user.resend_confirmation_instructions
+
+      log_action :confirm, @user
+
+      flash[:notice] = I18n.t('admin.accounts.resend_confirmation.success')
+      redirect_to admin_accounts_path
+    end
+
     private
 
     def set_user
       @user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
     end
+
+    def check_confirmation
+      if @user.confirmed?
+        flash[:error] = I18n.t('admin.accounts.resend_confirmation.already_confirmed')
+        redirect_to admin_accounts_path
+      end
+    end
   end
 end
diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml
index 2e6de18643..ed8190af5a 100644
--- a/app/views/admin/accounts/show.html.haml
+++ b/app/views/admin/accounts/show.html.haml
@@ -36,13 +36,19 @@
           %th= t('admin.accounts.email')
           %td
             = @account.user_email
-            - if @account.user_confirmed?
-              = fa_icon('check')
             = table_link_to 'edit', t('admin.accounts.change_email.label'), admin_account_change_email_path(@account.id) if can?(:change_email, @account.user)
         - if @account.user_unconfirmed_email.present?
           %th= t('admin.accounts.unconfirmed_email')
           %td
             = @account.user_unconfirmed_email
+        %tr
+          %th= t('admin.accounts.email_status')
+          %td
+            - if @account.user&.confirmed?
+              = t('admin.accounts.confirmed')
+            - else
+              = t('admin.accounts.confirming')
+              = table_link_to 'refresh', t('admin.accounts.resend_confirmation.send'), resend_admin_account_confirmation_path(@account.id), method: :post if can?(:confirm, @account.user)
         %tr
           %th= t('admin.accounts.login_status')
           %td
diff --git a/config/locales/ar.yml b/config/locales/ar.yml
index a5179ae148..eb0127eb51 100644
--- a/config/locales/ar.yml
+++ b/config/locales/ar.yml
@@ -61,6 +61,7 @@ ar:
       by_domain: النطاق
       confirm: تأكيد
       confirmed: مؤكَّد
+      confirming: التأكد
       demote: إنزال الرُتبة الوظيفية
       disable: تعطيل
       disable_two_factor_authentication: تعطيل 2FA
@@ -69,6 +70,7 @@ ar:
       domain: النطاق
       edit: تعديل
       email: البريد الإلكتروني
+      email_status: حالة البريد الإلكتروني
       enable: تفعيل
       enabled: مفعَّل
       feed_url: عنوان رابط التغذية
@@ -106,6 +108,10 @@ ar:
       public: عمومي
       push_subscription_expires: انتهاء الاشتراك ”PuSH“
       redownload: تحديث الصورة الرمزية
+      resend_confirmation:
+        already_confirmed: هذا المستخدم مؤكد بالفعل
+        send: أعد إرسال رسالة البريد الالكتروني الخاصة بالتأكيد
+        success: تم إرسال رسالة التأكيد بنجاح!
       reset: إعادة التعيين
       reset_password: إعادة ضبط كلمة السر
       resubscribe: اشترك مرة أخرى
diff --git a/config/locales/ca.yml b/config/locales/ca.yml
index a8c75c32df..bdc3f74844 100644
--- a/config/locales/ca.yml
+++ b/config/locales/ca.yml
@@ -70,6 +70,7 @@ ca:
         title: Canviar adreça de correu de %{username}
       confirm: Confirma
       confirmed: Confirmat
+      confirming: Confirmando
       demote: Degrada
       disable: Inhabilita
       disable_two_factor_authentication: Desactiva 2FA
@@ -78,6 +79,7 @@ ca:
       domain: Domini
       edit: Edita
       email: Correu electrònic
+      email_status: Estado del correo electrónico
       enable: Habilita
       enabled: Habilitat
       feed_url: URL del canal
@@ -116,6 +118,10 @@ ca:
       push_subscription_expires: La subscripció PuSH expira
       redownload: Actualitza l'avatar
       remove_avatar: Eliminar avatar
+      resend_confirmation:
+        already_confirmed: Este usuario ya está confirmado
+        send: Reenviar el correo electrónico de confirmación
+        success: "¡Correo electrónico de confirmación enviado con éxito!"
       reset: Reinicialitza
       reset_password: Restableix la contrasenya
       resubscribe: Torna a subscriure
diff --git a/config/locales/de.yml b/config/locales/de.yml
index aa4295c896..05d7e01b38 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -70,6 +70,7 @@ de:
         title: E-Mail-Adresse für %{username} ändern
       confirm: Bestätigen
       confirmed: Bestätigt
+      confirming: Bestätigung
       demote: Degradieren
       disable: Ausschalten
       disable_two_factor_authentication: 2FA abschalten
@@ -78,6 +79,7 @@ de:
       domain: Domain
       edit: Bearbeiten
       email: E-Mail
+      email_status: E-Mail-Status
       enable: Freischalten
       enabled: Freigegeben
       feed_url: Feed-URL
@@ -116,6 +118,10 @@ de:
       push_subscription_expires: PuSH-Abonnement läuft aus
       redownload: Avatar neu laden
       remove_avatar: Profilbild entfernen
+      resend_confirmation:
+        already_confirmed: Dieser Benutzer wurde bereits bestätigt
+        send: Bestätigungsmail erneut senden
+        success: Bestätigungs-E-Mail erfolgreich gesendet!
       reset: Zurücksetzen
       reset_password: Passwort zurücksetzen
       resubscribe: Wieder abonnieren
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 4f967f9ac4..e18354eac4 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -70,6 +70,7 @@ en:
         title: Change Email for %{username}
       confirm: Confirm
       confirmed: Confirmed
+      confirming: Confirming
       demote: Demote
       disable: Disable
       disable_two_factor_authentication: Disable 2FA
@@ -78,6 +79,7 @@ en:
       domain: Domain
       edit: Edit
       email: E-mail
+      email_status: E-mail Status
       enable: Enable
       enabled: Enabled
       feed_url: Feed URL
@@ -116,6 +118,10 @@ en:
       push_subscription_expires: PuSH subscription expires
       redownload: Refresh avatar
       remove_avatar: Remove avatar
+      resend_confirmation:
+        already_confirmed: This user is already confirmed
+        send: Resend confirmation email
+        success: Confirmation email successfully sent!
       reset: Reset
       reset_password: Reset password
       resubscribe: Resubscribe
diff --git a/config/locales/eo.yml b/config/locales/eo.yml
index 9c44d0e622..47d591993b 100644
--- a/config/locales/eo.yml
+++ b/config/locales/eo.yml
@@ -61,6 +61,7 @@ eo:
       by_domain: Domajno
       confirm: Konfirmi
       confirmed: Konfirmita
+      confirming: Konfirmante
       demote: Degradi
       disable: Malebligi
       disable_two_factor_authentication: Malebligi 2FA
@@ -69,6 +70,7 @@ eo:
       domain: Domajno
       edit: Redakti
       email: Retpoŝto
+      email_status: Retpoŝto Stato
       enable: Ebligi
       enabled: Ebligita
       feed_url: URL de la fluo
@@ -106,6 +108,10 @@ eo:
       public: Publika
       push_subscription_expires: Eksvalidiĝo de la abono al PuSH
       redownload: Aktualigi profilbildon
+      resend_confirmation:
+        already_confirmed: Ĉi tiu uzanto jam estas konfirmita
+        send: Esend konfirmi retpoŝton
+        success: Konfirma retpoŝto sukcese sendis
       reset: Restarigi
       reset_password: Restarigi pasvorton
       resubscribe: Reaboni
diff --git a/config/locales/es.yml b/config/locales/es.yml
index be7dcd81e5..11bee3239b 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -70,6 +70,7 @@ es:
         title: Cambiar el correo electrónico de %{username}
       confirm: Confirmar
       confirmed: Confirmado
+      confirming: Confirmando
       demote: Degradar
       disable: Deshabilitar
       disable_two_factor_authentication: Desactivar autenticación de dos factores
@@ -78,6 +79,7 @@ es:
       domain: Dominio
       edit: Editar
       email: E-mail
+      email_status: E-mail Status
       enable: Habilitar
       enabled: Habilitada
       feed_url: URL de notificaciones
@@ -116,6 +118,10 @@ es:
       push_subscription_expires: Expiración de la suscripción PuSH
       redownload: Refrescar avatar
       remove_avatar: Eliminar el avatar
+      resend_confirmation:
+        already_confirmed: Este usuario ya está confirmado
+        send: Reenviar el correo electrónico de confirmación
+        success: "¡Correo electrónico de confirmación enviado con éxito"
       reset: Reiniciar
       reset_password: Reiniciar contraseña
       resubscribe: Re-suscribir
diff --git a/config/locales/fa.yml b/config/locales/fa.yml
index 3aa40f0b3e..5255e0e38f 100644
--- a/config/locales/fa.yml
+++ b/config/locales/fa.yml
@@ -61,6 +61,7 @@ fa:
       by_domain: دامین
       confirm: تأیید
       confirmed: تأیید شد
+      confirming: تأیید
       demote: تنزل‌دادن
       disable: غیرفعال
       disable_two_factor_authentication: غیرفعال‌سازی ورود دومرحله‌ای
@@ -69,6 +70,7 @@ fa:
       domain: دامین
       edit: ویرایش
       email: ایمیل
+      email_status: وضعیت ایمیل
       enable: فعال
       enabled: فعال
       feed_url: نشانی فید
@@ -106,6 +108,10 @@ fa:
       public: عمومی
       push_subscription_expires: عضویت از راه PuSH منقضی شد
       redownload: به‌روزرسانی تصویر نمایه
+      resend_confirmation:
+        already_confirmed: این کاربر قبلا تایید شده است
+        send: ایمیل تایید را دوباره بفرستید
+        success: ایمیل تایید با موفقیت ارسال شد
       reset: بازنشانی
       reset_password: بازنشانی رمز
       resubscribe: اشتراک دوباره
diff --git a/config/locales/fi.yml b/config/locales/fi.yml
index b313c87236..fca58cc0f1 100644
--- a/config/locales/fi.yml
+++ b/config/locales/fi.yml
@@ -61,6 +61,7 @@ fi:
       by_domain: Verkko-osoite
       confirm: Vahvista
       confirmed: Vahvistettu
+      confirming: Vahvistavat
       demote: Alenna
       disable: Poista käytöstä
       disable_two_factor_authentication: Poista 2FA käytöstä
@@ -69,6 +70,7 @@ fi:
       domain: Verkko-osoite
       edit: Muokkaa
       email: Sähköposti
+      email_status: Sähköpostin tila
       enable: Ota käyttöön
       enabled: Käytössä
       feed_url: Syötteen URL
@@ -106,6 +108,10 @@ fi:
       public: Julkinen
       push_subscription_expires: PuSH-tilaus vanhenee
       redownload: Päivitä profiilikuva
+      resend_confirmation:
+        already_confirmed: Tämä käyttäjä on jo vahvistettu
+        send: Lähetä varmistusviesti uudelleen
+        success: Vahvistusviesti lähetettiin onnistuneesti
       reset: Palauta
       reset_password: Palauta salasana
       resubscribe: Tilaa uudelleen
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index 3b543fdb7b..e7142f3fdb 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -70,6 +70,7 @@ fr:
         title: Modifier le courriel pour %{username}
       confirm: Confirmer
       confirmed: Confirmé
+      confirming: Confirmant
       demote: Rétrograder
       disable: Désactiver
       disable_two_factor_authentication: Désactiver l’authentification à deux facteurs
@@ -78,6 +79,7 @@ fr:
       domain: Domaine
       edit: Éditer
       email: Courriel
+      email_status: État de la messagerie
       enable: Activer
       enabled: Activé
       feed_url: URL du flux
@@ -116,6 +118,10 @@ fr:
       push_subscription_expires: Expiration de l’abonnement PuSH
       redownload: Rafraîchir les avatars
       remove_avatar: Supprimer l'avatar
+      resend_confirmation:
+        already_confirmed: Cet utilisateur est déjà confirmé
+        send: Renvoyer un courriel de confirmation
+        success: Email de confirmation envoyé avec succès !
       reset: Réinitialiser
       reset_password: Réinitialiser le mot de passe
       resubscribe: Se réabonner
diff --git a/config/locales/gl.yml b/config/locales/gl.yml
index eec1b4e1ef..e00c4b4333 100644
--- a/config/locales/gl.yml
+++ b/config/locales/gl.yml
@@ -70,6 +70,7 @@ gl:
         title: Cambiar o correo-e de %{username}
       confirm: Confirmar
       confirmed: Confirmado
+      confirming: Confirmar
       demote: Degradar
       disable: Deshabilitar
       disable_two_factor_authentication: Deshabilitar 2FA
@@ -78,6 +79,7 @@ gl:
       domain: Dominio
       edit: Editar
       email: E-mail
+      email_status: Estado del correo electrónico
       enable: Habilitar
       enabled: Habilitado
       feed_url: URL fonte
@@ -116,6 +118,10 @@ gl:
       push_subscription_expires: A suscrición PuSH caduca
       redownload: Actualizar avatar
       remove_avatar: Eliminar avatar
+      resend_confirmation:
+        already_confirmed: Este usuario ya está confirmado
+        send: Reenviar el correo electrónico de confirmación
+        success: "¡Correo electrónico de confirmación enviado con éxito!"
       reset: Restablecer
       reset_password: Restablecer contrasinal
       resubscribe: Voltar a suscribir
diff --git a/config/locales/he.yml b/config/locales/he.yml
index d66d9f0346..c127db3852 100644
--- a/config/locales/he.yml
+++ b/config/locales/he.yml
@@ -61,6 +61,7 @@ he:
       by_domain: שם מתחם
       confirm: אישור
       confirmed: אושר
+      confirming: המאשר
       demote: הורדה בדרגה
       disable: לחסום
       disable_two_factor_authentication: ביטול הזדהות דו-שלבית
@@ -69,6 +70,7 @@ he:
       domain: תחום
       edit: עריכה
       email: דוא"ל
+      email_status: סטטוס דוא"ל
       enable: לאפשר
       enabled: מאופשר
       feed_url: כתובת פיד
@@ -106,6 +108,10 @@ he:
       public: פומבי
       push_subscription_expires: הרשמה להודעות בדחיפה פגה
       redownload: לקריאה מחדש של האווטאר
+      resend_confirmation:
+        already_confirmed: משתמש זה כבר אושר
+        send: שלח מחדש דוא"ל אימות
+        success: הודעת האימייל נשלחה בהצלחה!
       reset: איפוס
       reset_password: אתחול סיסמא
       resubscribe: להרשם מחדש
diff --git a/config/locales/hu.yml b/config/locales/hu.yml
index 422c059559..0cd0021c16 100644
--- a/config/locales/hu.yml
+++ b/config/locales/hu.yml
@@ -61,6 +61,7 @@ hu:
       by_domain: Domain
       confirm: Megerősítés
       confirmed: Megerősítve
+      confirming: Megerősítve
       demote: Lefokozás
       disable: Kikapcsolás
       disable_two_factor_authentication: Kétlépcsős azonosítás kikapcsolása
@@ -69,6 +70,7 @@ hu:
       domain: Domain
       edit: Szerkesztés
       email: E-mail
+      email_status: E-mail állapot
       enable: Engedélyezés
       enabled: Engedélyezve
       feed_url: Hírcsatorna URL
@@ -106,6 +108,10 @@ hu:
       public: Nyilvános
       push_subscription_expires: A PuSH feliratkozás elévül
       redownload: Profilkép frissítése
+      resend_confirmation:
+        already_confirmed: Ezt a felhasználót már megerősítették
+        send: Küldd újra a megerősítő email-t
+        success: A megerősítő e-mail sikeresen elküldve!
       reset: Visszaállítás
       reset_password: Jelszó visszaállítása
       resubscribe: Feliratkozás ismét
diff --git a/config/locales/id.yml b/config/locales/id.yml
index 0476e2848e..4fb75f2b0e 100644
--- a/config/locales/id.yml
+++ b/config/locales/id.yml
@@ -29,10 +29,12 @@ id:
       are_you_sure: Anda yakin?
       confirm: Konfirmasi
       confirmed: Dikonfirmasi
+      confirming: Mengkonfirmasi
       display_name: Nama
       domain: Domain
       edit: Ubah
       email: E-mail
+      email_status: Status Email
       feed_url: URL Feed
       followers: Pengikut
       follows: Mengikut
@@ -58,6 +60,10 @@ id:
       profile_url: URL profil
       public: Publik
       push_subscription_expires: Langganan PuSH telah kadaluarsa
+      resend_confirmation:
+        already_confirmed: Pengguna ini sudah dikonfirmasi
+        send: Kirim ulang email konfirmasi
+        success: Email konfirmasi berhasil dikirim!
       reset_password: Reset kata sandi
       salmon_url: URL Salmon
       show:
diff --git a/config/locales/it.yml b/config/locales/it.yml
index c1ac176c45..eafe6b2a17 100644
--- a/config/locales/it.yml
+++ b/config/locales/it.yml
@@ -66,6 +66,7 @@ it:
         title: Cambia email per %{username}
       confirm: Conferma
       confirmed: Confermato
+      confirming: Confermando
       demote: Declassa
       disable: Disabilita
       disable_two_factor_authentication: Disabilita 2FA
@@ -74,6 +75,7 @@ it:
       domain: Dominio
       edit: Modifica
       email: Email
+      email_status: Stato email
       enable: Abilita
       enabled: Abilitato
       feed_url: URL Feed
@@ -111,6 +113,10 @@ it:
       public: Pubblico
       redownload: Aggiorna avatar
       remove_avatar: Rimuovi avatar
+      resend_confirmation:
+        already_confirmed: Questo utente è già confermato
+        send: Reinvia email di conferma
+        success: Email di conferma inviata con successo!
       reset: Reimposta
       reset_password: Reimposta password
       resubscribe: Riscriversi
diff --git a/config/locales/ja.yml b/config/locales/ja.yml
index bc48613b98..c7025fc186 100644
--- a/config/locales/ja.yml
+++ b/config/locales/ja.yml
@@ -70,6 +70,7 @@ ja:
         title: "%{username} さんのメールアドレスを変更"
       confirm: 確認
       confirmed: 確認済み
+      confirming: 確認中
       demote: 降格
       disable: 無効化
       disable_two_factor_authentication: 二段階認証を無効にする
@@ -78,6 +79,7 @@ ja:
       domain: ドメイン
       edit: 編集
       email: メールアドレス
+      email_status: メールアドレスの状態
       enable: 有効化
       enabled: 有効
       feed_url: フィードURL
@@ -116,6 +118,10 @@ ja:
       push_subscription_expires: PuSH購読期限
       redownload: アバターの更新
       remove_avatar: アイコンを削除
+      resend_confirmation:
+        already_confirmed: メールアドレスは確認済みです。
+        send: 確認メールを再送
+        success: 確認メールを再送信しました!
       reset: リセット
       reset_password: パスワード再設定
       resubscribe: 再講読
diff --git a/config/locales/ko.yml b/config/locales/ko.yml
index bd6717d497..92e99ce501 100644
--- a/config/locales/ko.yml
+++ b/config/locales/ko.yml
@@ -70,6 +70,7 @@ ko:
         title: "%{username}의 이메일 주소 변경"
       confirm: 확인
       confirmed: 확인됨
+      confirming: 확인 중
       demote: 모더레이터 강등
       disable: 비활성화
       disable_two_factor_authentication: 2단계 인증을 비활성화
@@ -78,6 +79,7 @@ ko:
       domain: 도메인
       edit: 편집
       email: E-mail
+      email_status: 이메일 상태
       enable: 활성화
       enabled: 활성화된
       feed_url: 피드 URL
@@ -116,6 +118,10 @@ ko:
       push_subscription_expires: PuSH 구독 기간 만료
       redownload: 아바타 업데이트
       remove_avatar: 아바타 지우기
+      resend_confirmation:
+        already_confirmed: 이 사용자는 이미 확인되었습니다.
+        send: 다시 확인 이메일
+        success: 확인 이메일이 전송되었습니다.
       reset: 초기화
       reset_password: 비밀번호 초기화
       resubscribe: 다시 구독
diff --git a/config/locales/nl.yml b/config/locales/nl.yml
index 00f21babd0..6ca81b3757 100644
--- a/config/locales/nl.yml
+++ b/config/locales/nl.yml
@@ -70,6 +70,7 @@ nl:
         title: E-mailadres veranderen voor %{username}
       confirm: Bevestigen
       confirmed: Bevestigd
+      confirming: Bevestiging
       demote: Degraderen
       disable: Uitschakelen
       disable_two_factor_authentication: 2FA uitschakelen
@@ -78,6 +79,7 @@ nl:
       domain: Domein
       edit: Bewerken
       email: E-mail
+      email_status: E-mail Status
       enable: Inschakelen
       enabled: Ingeschakeld
       feed_url: Feed-URL
@@ -116,6 +118,10 @@ nl:
       push_subscription_expires: PuSH-abonnement verloopt op
       redownload: Avatar vernieuwen
       remove_avatar: Avatar verwijderen
+      resend_confirmation:
+        already_confirmed: Deze gebruiker is al bevestigd
+        send: Verzend bevestigingsmail opnieuw
+        success: Bevestigingsmail succesvol verzonden!
       reset: Opnieuw
       reset_password: Wachtwoord opnieuw instellen
       resubscribe: Opnieuw abonneren
diff --git a/config/locales/no.yml b/config/locales/no.yml
index 6ce2c0f013..837042f851 100644
--- a/config/locales/no.yml
+++ b/config/locales/no.yml
@@ -61,6 +61,7 @@
       by_domain: Domene
       confirm: Bekreft
       confirmed: Bekreftet
+      confirming: Bekrefte
       demote: Degrader
       disable: Deaktiver
       disable_two_factor_authentication: Skru av 2FA
@@ -69,6 +70,7 @@
       domain: Domene
       edit: Redigér
       email: E-post
+      email_status: E-poststatus
       enable: Aktiver
       enabled: Aktivert
       feed_url: Feed-URL
@@ -106,6 +108,10 @@
       public: Offentlig
       push_subscription_expires: PuSH-abonnent utløper
       redownload: Oppdater avatar
+      resend_confirmation:
+        already_confirmed: Denne brukeren er allerede bekreftet
+        send: Send bekreftelses-epost på nytt
+        success: Bekreftelses e-post er vellykket sendt!
       reset: Tilbakestill
       reset_password: Nullstill passord
       resubscribe: Abonner på nytt
diff --git a/config/locales/oc.yml b/config/locales/oc.yml
index c11fe614b0..e61f7ebd00 100644
--- a/config/locales/oc.yml
+++ b/config/locales/oc.yml
@@ -70,6 +70,7 @@ oc:
         title: Cambiar l’adreça a %{username}
       confirm: Confirmar
       confirmed: Confirmat
+      confirming: Confirmando
       demote: Retrogradar
       disable: Desactivar
       disable_two_factor_authentication: Desactivar 2FA
@@ -78,6 +79,7 @@ oc:
       domain: Domeni
       edit: Modificar
       email: Corrièl
+      email_status: Estado del correo electrónico
       enable: Activar
       enabled: Activat
       feed_url: Flux URL
@@ -116,6 +118,10 @@ oc:
       push_subscription_expires: Fin de l’abonament PuSH
       redownload: Actualizar los avatars
       remove_avatar: Supriir l’avatar
+      resend_confirmation:
+        already_confirmed: Este usuario ya está confirmado
+        send: Reenviar el correo electrónico de confirmación
+        success: "¡Correo electrónico de confirmación enviado con éxito!"
       reset: Reïnicializar
       reset_password: Reïnicializar lo senhal
       resubscribe: Se tornar abonar
diff --git a/config/locales/pl.yml b/config/locales/pl.yml
index bd38aa6f77..1fbe0d5c8f 100644
--- a/config/locales/pl.yml
+++ b/config/locales/pl.yml
@@ -70,6 +70,7 @@ pl:
         title: Zmień adres e-mail dla %{username}
       confirm: Potwierdź
       confirmed: Potwierdzono
+      confirming: Potwierdzam
       demote: Degraduj
       disable: Dezaktywuj
       disable_two_factor_authentication: Wyłącz uwierzytelnianie dwuetapowe
@@ -78,6 +79,7 @@ pl:
       domain: Domena
       edit: Edytuj
       email: Adres e-mail
+      email_status: Status e-maila
       enable: Aktywuj
       enabled: Aktywowano
       feed_url: Adres kanału
@@ -116,6 +118,10 @@ pl:
       push_subscription_expires: Subskrypcja PuSH wygasa
       redownload: Odśwież awatar
       remove_avatar: Usun awatar
+      resend_confirmation:
+        already_confirmed: Ten użytkownik jest już potwierdzony
+        send: Wyślij ponownie email potwierdzający
+        success: E-mail z potwierdzeniem został wysłany!
       reset: Resetuj
       reset_password: Resetuj hasło
       resubscribe: Ponów subskrypcję
diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml
index 862833d873..37abeffd9f 100644
--- a/config/locales/pt-BR.yml
+++ b/config/locales/pt-BR.yml
@@ -70,6 +70,7 @@ pt-BR:
         title: Mudar e-mail para %{username}
       confirm: Confirmar
       confirmed: Confirmado
+      confirming: Confirmando
       demote: Rebaixar
       disable: Desativar
       disable_two_factor_authentication: Desativar 2FA
@@ -78,6 +79,7 @@ pt-BR:
       domain: Domínio
       edit: Editar
       email: E-mail
+      email_status: Estado del correo electrónico
       enable: Ativar
       enabled: Ativado
       feed_url: URL do feed
@@ -116,6 +118,10 @@ pt-BR:
       push_subscription_expires: Inscrição PuSH expira
       redownload: Atualizar avatar
       remove_avatar: Remover avatar
+      resend_confirmation:
+        already_confirmed: Este usuario ya está confirmado
+        send: Reenviar el correo electrónico de confirmación
+        success: "¡Correo electrónico de confirmación enviado con éxito!"
       reset: Anular
       reset_password: Modificar senha
       resubscribe: Reinscrever-se
diff --git a/config/locales/pt.yml b/config/locales/pt.yml
index 62a7ef0ec4..7c10124ff6 100644
--- a/config/locales/pt.yml
+++ b/config/locales/pt.yml
@@ -61,6 +61,7 @@ pt:
       by_domain: Domínio
       confirm: Confirme
       confirmed: Confirmado
+      confirming: Confirmer
       demote: Rebaixar
       disable: Desativar
       disable_two_factor_authentication: Desativar 2FA
@@ -69,6 +70,7 @@ pt:
       domain: Domínio
       edit: Editar
       email: E-mail
+      email_status: État de la messagerie
       enable: Ativar
       enabled: Ativado
       feed_url: URL do Feed
@@ -106,6 +108,10 @@ pt:
       public: Público
       push_subscription_expires: A Inscrição PuSH expira
       redownload: Atualizar avatar
+      resend_confirmation:
+        already_confirmed: Cet utilisateur est déjà confirmé
+        send: Renvoyer un courriel de confirmation
+        success: Email de confirmation envoyé avec succès!
       reset: Restaurar
       reset_password: Reset palavra-passe
       resubscribe: Reinscrever
diff --git a/config/locales/ru.yml b/config/locales/ru.yml
index 603e1f3cab..99825a765a 100644
--- a/config/locales/ru.yml
+++ b/config/locales/ru.yml
@@ -61,6 +61,7 @@ ru:
       by_domain: Домен
       confirm: Подтвердить
       confirmed: Подтверждено
+      confirming: подтверждающий
       demote: Разжаловать
       disable: Отключить
       disable_two_factor_authentication: Отключить 2FA
@@ -69,6 +70,7 @@ ru:
       domain: Домен
       edit: Изменить
       email: E-mail
+      email_status: Статус электронной почты
       enable: Включить
       enabled: Включен
       feed_url: URL фида
@@ -106,6 +108,10 @@ ru:
       public: Публичный
       push_subscription_expires: Подписка PuSH истекает
       redownload: Обновить аватар
+      resend_confirmation:
+        already_confirmed: Этот пользователь уже подтвержден
+        send: Повторно отправить подтверждение по электронной почте
+        success: Письмо с подтверждением успешно отправлено!
       reset: Сбросить
       reset_password: Сбросить пароль
       resubscribe: Переподписаться
diff --git a/config/locales/sk.yml b/config/locales/sk.yml
index 7fb097f03d..ef756b474e 100644
--- a/config/locales/sk.yml
+++ b/config/locales/sk.yml
@@ -70,6 +70,7 @@ sk:
         title: Zmeň email pre %{username}
       confirm: Potvrdiť
       confirmed: Potvrdený
+      confirming: Potvrdzujúci
       demote: Degradovať
       disable: Zablokovať
       disable_two_factor_authentication: Zakázať 2FA
@@ -78,6 +79,7 @@ sk:
       domain: Doména
       edit: Upraviť
       email: Email
+      email_status: Stav Email
       enable: Povoliť
       enabled: Povolený
       feed_url: URL časovej osi
@@ -116,6 +118,10 @@ sk:
       push_subscription_expires: PuSH odoberanie expiruje
       redownload: Obnoviť avatar
       remove_avatar: Odstrániť avatár
+      resend_confirmation:
+        already_confirmed: Tento používateľ už bol potvrdený
+        send: Znova odoslať potvrdzovací e-mail
+        success: Potvrdený e-mail bol úspešne odoslaný!
       reset: Reset
       reset_password: Obnoviť heslo
       resubscribe: Znovu odoberať
diff --git a/config/locales/sr-Latn.yml b/config/locales/sr-Latn.yml
index dba952917a..0c7756c53e 100644
--- a/config/locales/sr-Latn.yml
+++ b/config/locales/sr-Latn.yml
@@ -61,6 +61,7 @@ sr-Latn:
       by_domain: Domen
       confirm: Potvrdi
       confirmed: Potvrđeno
+      confirming: Potvrđujući
       demote: Ražaluj
       disable: Isključi
       disable_two_factor_authentication: Isključi 2FA
@@ -69,6 +70,7 @@ sr-Latn:
       domain: Domen
       edit: Izmeni
       email: E-pošta
+      email_status: Status e-pošte
       enable: Uključi
       enabled: Uključeno
       feed_url: Adresa dovoda
@@ -106,6 +108,10 @@ sr-Latn:
       public: Javno
       push_subscription_expires: PuSH subscription expires
       redownload: Osveži avatar
+      resend_confirmation:
+        already_confirmed: Ovaj korisnik je već potvrđen
+        send: Ponovo pošaljite e-poruku za potvrdu
+        success: E-mail potvrde je uspešno poslat!
       reset: Resetuj
       reset_password: Resetuj lozinku
       resubscribe: Ponovo se pretplati
diff --git a/config/locales/sr.yml b/config/locales/sr.yml
index 74abf0323e..1cb87ff30c 100644
--- a/config/locales/sr.yml
+++ b/config/locales/sr.yml
@@ -61,6 +61,7 @@ sr:
       by_domain: Домен
       confirm: Потврди
       confirmed: Потврђено
+      confirming: Потврдување
       demote: Ражалуј
       disable: Искључи
       disable_two_factor_authentication: Искључи 2FA
@@ -69,6 +70,7 @@ sr:
       domain: Домен
       edit: Измени
       email: Е-пошта
+      email_status: Е-пошта статус
       enable: Укључи
       enabled: Укључено
       feed_url: Адреса довода
@@ -106,6 +108,10 @@ sr:
       public: Јавно
       push_subscription_expires: PuSH subscription expires
       redownload: Освежи аватар
+      resend_confirmation:
+        already_confirmed: Овој корисник е веќе потврден
+        send: Препрати го е-мајлот за потврда
+        success: Е-пошта за потврда успешно испратена!
       reset: Ресетуј
       reset_password: Ресетуј лозинку
       resubscribe: Поново се претплати
diff --git a/config/locales/sv.yml b/config/locales/sv.yml
index 9e0bb08565..1b09656555 100644
--- a/config/locales/sv.yml
+++ b/config/locales/sv.yml
@@ -70,6 +70,7 @@ sv:
         title: Byt E-postadress för %{username}
       confirm: Bekräfta
       confirmed: Bekräftad
+      confirming: Bekräftande
       demote: Degradera
       disable: inaktivera
       disable_two_factor_authentication: Inaktivera 2FA
@@ -78,6 +79,7 @@ sv:
       domain: Domän
       edit: Redigera
       email: E-post
+      email_status: E-poststatus
       enable: Aktivera
       enabled: Aktiverad
       feed_url: Flödes URL
@@ -116,6 +118,10 @@ sv:
       push_subscription_expires: PuSH-prenumerationen löper ut
       redownload: Uppdatera avatar
       remove_avatar: Ta bort avatar
+      resend_confirmation:
+        already_confirmed: Den här användaren är redan bekräftad
+        send: Skicka om e-postbekräftelse
+        success: Bekräftelsemeddelande skickas framgångsrikt!
       reset: Återställ
       reset_password: Återställ lösenord
       resubscribe: Starta en ny prenumeration
diff --git a/config/locales/th.yml b/config/locales/th.yml
index 9d93115b0d..6804dbd13d 100644
--- a/config/locales/th.yml
+++ b/config/locales/th.yml
@@ -29,11 +29,13 @@ th:
       are_you_sure: แน่ใจนะ?
       confirm: ยืนยัน
       confirmed: ยึนยันแล้ว
+      confirming: ยืนยัน
       disable_two_factor_authentication: Disable 2FA
       display_name: ชื่อสำหรับดีสเพล
       domain: โดแมน
       edit: แก้ไข
       email: อีเมล์
+      email_status: สถานะอีเมล
       feed_url: Feed URL
       followers: ผู้ติดตาม
       follows: ติดตาม
@@ -59,6 +61,10 @@ th:
       profile_url: Profile URL
       public: สาธารณะ
       push_subscription_expires: PuSH subscription expires
+      resend_confirmation:
+        already_confirmed: ผู้ใช้รายนี้ได้รับการยืนยันแล้ว
+        send: ส่งอีเมลยืนยันอีกครั้ง
+        success: ยืนยันอีเมลเรียบร้อยแล้ว!
       reset_password: รีเซ็ตรหัสผ่าน
       salmon_url: Salmon URL
       show:
diff --git a/config/locales/tr.yml b/config/locales/tr.yml
index 0d78216b60..8bafbface2 100644
--- a/config/locales/tr.yml
+++ b/config/locales/tr.yml
@@ -29,10 +29,12 @@ tr:
       are_you_sure: Emin misiniz?
       confirm: Onayla
       confirmed: Onaylandı
+      confirming: Onaylama
       display_name: Görünen adınız
       domain: Sunucu
       edit: Düzenle
       email: E-posta
+      email_status: Email Durumu
       feed_url: Besleme linki
       followers: Takipçiler
       follows: Takip edilen
@@ -58,6 +60,10 @@ tr:
       profile_url: Profil linki
       public: Herkese açık
       push_subscription_expires: PuSH aboneliği dolumu
+      resend_confirmation:
+        already_confirmed: Bu kullanıcı zaten onaylandı
+        send: Doğrulama epostasını yeniden gönder
+        success: Onay e-postası başarıyla gönderildi!
       reset_password: Parolayı değiştir
       salmon_url: Salmon Linki
       show:
diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml
index 7ea491fd4b..5eca9e6a56 100644
--- a/config/locales/zh-CN.yml
+++ b/config/locales/zh-CN.yml
@@ -61,6 +61,7 @@ zh-CN:
       by_domain: 域名
       confirm: 确认
       confirmed: 已确认
+      confirming: 确认
       demote: 降任
       disable: 停用
       disable_two_factor_authentication: 停用双重认证
@@ -69,6 +70,7 @@ zh-CN:
       domain: 域名
       edit: 编辑
       email: 电子邮件地址
+      email_status: 电子邮件状态
       enable: 启用
       enabled: 已启用
       feed_url: 订阅 URL
@@ -106,6 +108,10 @@ zh-CN:
       public: 公开页面
       push_subscription_expires: PuSH 订阅过期时间
       redownload: 刷新头像
+      resend_confirmation:
+        already_confirmed: 该用户已被确认
+        send: 重发确认邮件
+        success: 确认电子邮件成功发送!
       reset: 重置
       reset_password: 重置密码
       resubscribe: 重新订阅
diff --git a/config/locales/zh-HK.yml b/config/locales/zh-HK.yml
index 3f91b7191d..c5d8c86968 100644
--- a/config/locales/zh-HK.yml
+++ b/config/locales/zh-HK.yml
@@ -70,6 +70,7 @@ zh-HK:
         title: 改變 %{username} 的電郵
       confirm: 確定
       confirmed: 已確定
+      confirming: 確定
       demote: 降任
       disable: 停用
       disable_two_factor_authentication: 停用雙重認證
@@ -78,6 +79,7 @@ zh-HK:
       domain: 域名
       edit: 編輯
       email: 電郵地址
+      email_status: 电子邮件状态
       enable: 啟用
       enabled: 已啟用
       feed_url: Feed URL
@@ -116,6 +118,10 @@ zh-HK:
       push_subscription_expires: PuSH 訂閱過期
       redownload: 更新頭像
       remove_avatar: 取消頭像
+      resend_confirmation:
+        already_confirmed: 该用户已被确认
+        send: 重发确认邮件
+        success: 确认电子邮件成功发送!
       reset: 重設
       reset_password: 重設密碼
       resubscribe: 重新訂閱
diff --git a/config/routes.rb b/config/routes.rb
index 90f6ac594d..4c920cf74f 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -155,8 +155,13 @@ Rails.application.routes.draw do
       resource :reset, only: [:create]
       resource :silence, only: [:create, :destroy]
       resource :suspension, only: [:create, :destroy]
-      resource :confirmation, only: [:create]
-      resources :statuses, only: [:index, :create]
+      resources :statuses, only: [:index, :create, :update, :destroy]
+
+      resource :confirmation, only: [:create] do
+        collection do
+          post :resend
+        end
+      end
 
       resource :role do
         member do
diff --git a/spec/controllers/admin/confirmations_controller_spec.rb b/spec/controllers/admin/confirmations_controller_spec.rb
index 7c80349646..eec2b2f5c4 100644
--- a/spec/controllers/admin/confirmations_controller_spec.rb
+++ b/spec/controllers/admin/confirmations_controller_spec.rb
@@ -30,4 +30,35 @@ RSpec.describe Admin::ConfirmationsController, type: :controller do
       expect(response).to have_http_status(404)
     end
   end
+
+  describe 'POST #resernd' do
+    subject { post :resend, params: { account_id: account.id } }
+
+    let(:account) { Fabricate(:account) }
+    let!(:user) { Fabricate(:user, confirmed_at: confirmed_at, account: account) }
+
+    before do
+      allow(UserMailer).to receive(:confirmation_instructions) { double(:email, deliver_later: nil) }
+    end
+
+    context 'when email is not confirmed' do
+      let(:confirmed_at) { nil }
+
+      it 'resends confirmation mail' do
+        expect(subject).to redirect_to admin_accounts_path
+        expect(flash[:notice]).to eq I18n.t('admin.accounts.resend_confirmation.success')
+        expect(UserMailer).to have_received(:confirmation_instructions).once
+      end
+    end
+
+    context 'when email is confirmed' do
+      let(:confirmed_at) { Time.zone.now }
+
+      it 'does not resend confirmation mail' do
+        expect(subject).to redirect_to admin_accounts_path
+        expect(flash[:error]).to eq I18n.t('admin.accounts.resend_confirmation.already_confirmed')
+        expect(UserMailer).not_to have_received(:confirmation_instructions)
+      end
+    end
+  end
 end

From 61a90186070395e133ad2f8e959bdf003a8615ca Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Sun, 6 May 2018 11:48:51 +0200
Subject: [PATCH 16/40] Enable custom emojis in profiles (notes, field values,
 display names) (#7374)

Follow-up to #6124
---
 app/helpers/stream_entries_helper.rb          |  4 ++--
 .../mastodon/actions/importer/normalizer.js   | 19 +++++++++++--------
 app/lib/formatter.rb                          | 12 ++++++++++--
 app/models/account.rb                         |  6 +++++-
 app/serializers/rest/account_serializer.rb    |  1 +
 app/views/about/_administration.html.haml     |  2 +-
 app/views/about/_contact.html.haml            |  2 +-
 app/views/accounts/_grid_card.html.haml       |  2 +-
 app/views/accounts/_header.html.haml          |  4 ++--
 app/views/accounts/_moved_strip.html.haml     |  4 ++--
 app/views/admin/reports/_account.html.haml    |  2 +-
 app/views/authorize_follows/_card.html.haml   |  2 +-
 app/views/remote_unfollows/_card.html.haml    |  2 +-
 app/views/shared/_landing_strip.html.haml     |  2 +-
 .../stream_entries/_detailed_status.html.haml |  2 +-
 .../stream_entries/_simple_status.html.haml   |  2 +-
 app/views/stream_entries/_status.html.haml    |  2 +-
 17 files changed, 43 insertions(+), 27 deletions(-)

diff --git a/app/helpers/stream_entries_helper.rb b/app/helpers/stream_entries_helper.rb
index c6f12ecd41..707c8e26ce 100644
--- a/app/helpers/stream_entries_helper.rb
+++ b/app/helpers/stream_entries_helper.rb
@@ -4,8 +4,8 @@ module StreamEntriesHelper
   EMBEDDED_CONTROLLER = 'statuses'
   EMBEDDED_ACTION = 'embed'
 
-  def display_name(account)
-    account.display_name.presence || account.username
+  def display_name(account, **options)
+    Formatter.instance.format_display_name(account, options)
   end
 
   def account_description(account)
diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js
index 5f1274fab6..057bff58bd 100644
--- a/app/javascript/mastodon/actions/importer/normalizer.js
+++ b/app/javascript/mastodon/actions/importer/normalizer.js
@@ -3,18 +3,25 @@ import emojify from '../../features/emoji/emoji';
 
 const domParser = new DOMParser();
 
+const makeEmojiMap = record => record.emojis.reduce((obj, emoji) => {
+  obj[`:${emoji.shortcode}:`] = emoji;
+  return obj;
+}, {});
+
 export function normalizeAccount(account) {
   account = { ...account };
 
+  const emojiMap = makeEmojiMap(account);
   const displayName = account.display_name.length === 0 ? account.username : account.display_name;
-  account.display_name_html = emojify(escapeTextContentForBrowser(displayName));
-  account.note_emojified = emojify(account.note);
+
+  account.display_name_html = emojify(escapeTextContentForBrowser(displayName), emojiMap);
+  account.note_emojified = emojify(account.note, emojiMap);
 
   if (account.fields) {
     account.fields = account.fields.map(pair => ({
       ...pair,
       name_emojified: emojify(escapeTextContentForBrowser(pair.name)),
-      value_emojified: emojify(pair.value),
+      value_emojified: emojify(pair.value, emojiMap),
     }));
   }
 
@@ -42,11 +49,7 @@ export function normalizeStatus(status, normalOldStatus) {
     normalStatus.hidden = normalOldStatus.get('hidden');
   } else {
     const searchContent = [status.spoiler_text, status.content].join('\n\n').replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n');
-
-    const emojiMap = normalStatus.emojis.reduce((obj, emoji) => {
-      obj[`:${emoji.shortcode}:`] = emoji;
-      return obj;
-    }, {});
+    const emojiMap = makeEmojiMap(normalStatus);
 
     normalStatus.search_index = domParser.parseFromString(searchContent, 'text/html').documentElement.textContent;
     normalStatus.contentHtml  = emojify(normalStatus.content, emojiMap);
diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb
index 050c651ee9..e1ab05cc0b 100644
--- a/app/lib/formatter.rb
+++ b/app/lib/formatter.rb
@@ -67,9 +67,17 @@ class Formatter
     html.html_safe # rubocop:disable Rails/OutputSafety
   end
 
-  def format_field(account, str)
+  def format_display_name(account, **options)
+    html = encode(account.display_name.presence || account.username)
+    html = encode_custom_emojis(html, account.emojis) if options[:custom_emojify]
+    html.html_safe # rubocop:disable Rails/OutputSafety
+  end
+
+  def format_field(account, str, **options)
     return reformat(str).html_safe unless account.local? # rubocop:disable Rails/OutputSafety
-    encode_and_link_urls(str, me: true).html_safe # rubocop:disable Rails/OutputSafety
+    html = encode_and_link_urls(str, me: true)
+    html = encode_custom_emojis(html, account.emojis) if options[:custom_emojify]
+    html.html_safe # rubocop:disable Rails/OutputSafety
   end
 
   def linkify(text)
diff --git a/app/models/account.rb b/app/models/account.rb
index 72ba0398ef..4467d1512a 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -398,7 +398,7 @@ class Account < ApplicationRecord
   end
 
   def emojis
-    @emojis ||= CustomEmoji.from_text(note, domain)
+    @emojis ||= CustomEmoji.from_text(emojifiable_text, domain)
   end
 
   before_create :generate_keys
@@ -425,4 +425,8 @@ class Account < ApplicationRecord
 
     self.domain = TagManager.instance.normalize_domain(domain)
   end
+
+  def emojifiable_text
+    [note, display_name, fields.map(&:value)].join(' ')
+  end
 end
diff --git a/app/serializers/rest/account_serializer.rb b/app/serializers/rest/account_serializer.rb
index 863238eb74..8761bbb5e7 100644
--- a/app/serializers/rest/account_serializer.rb
+++ b/app/serializers/rest/account_serializer.rb
@@ -8,6 +8,7 @@ class REST::AccountSerializer < ActiveModel::Serializer
              :followers_count, :following_count, :statuses_count
 
   has_one :moved_to_account, key: :moved, serializer: REST::AccountSerializer, if: :moved_and_not_nested?
+  has_many :emojis, serializer: REST::CustomEmojiSerializer
 
   class FieldSerializer < ActiveModel::Serializer
     attributes :name, :value
diff --git a/app/views/about/_administration.html.haml b/app/views/about/_administration.html.haml
index ec5834f9cb..02286d68b7 100644
--- a/app/views/about/_administration.html.haml
+++ b/app/views/about/_administration.html.haml
@@ -6,7 +6,7 @@
           .account__avatar{ style: "background-image: url(#{@instance_presenter.contact_account.avatar.url})" }
         %span.display-name
           %bdi
-            %strong.display-name__html.emojify= display_name(@instance_presenter.contact_account)
+            %strong.display-name__html.emojify= display_name(@instance_presenter.contact_account, custom_emojify: true)
           %span.display-name__account @#{@instance_presenter.contact_account.acct}
     - else
       .account__display-name
diff --git a/app/views/about/_contact.html.haml b/app/views/about/_contact.html.haml
index cf21ad5a3c..3215d50b53 100644
--- a/app/views/about/_contact.html.haml
+++ b/app/views/about/_contact.html.haml
@@ -12,7 +12,7 @@
         .avatar= image_tag contact.contact_account.avatar.url
         .name
           = link_to TagManager.instance.url_for(contact.contact_account) do
-            %span.display_name.emojify= display_name(contact.contact_account)
+            %span.display_name.emojify= display_name(contact.contact_account, custom_emojify: true)
             %span.username @#{contact.contact_account.acct}
     - else
       .owner
diff --git a/app/views/accounts/_grid_card.html.haml b/app/views/accounts/_grid_card.html.haml
index 95acbd581e..a59ed128e6 100644
--- a/app/views/accounts/_grid_card.html.haml
+++ b/app/views/accounts/_grid_card.html.haml
@@ -5,7 +5,7 @@
     .avatar= image_tag account.avatar.url(:original)
   .name
     = link_to TagManager.instance.url_for(account) do
-      %span.display_name.emojify= display_name(account)
+      %span.display_name.emojify= display_name(account, custom_emojify: true)
       %span.username
         @#{account.local? ? account.local_username_and_domain : account.acct}
         = fa_icon('lock') if account.locked?
diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml
index 41315f0393..13dcaf616a 100644
--- a/app/views/accounts/_header.html.haml
+++ b/app/views/accounts/_header.html.haml
@@ -5,7 +5,7 @@
 
   .card__bio
     %h1.name
-      %span.p-name.emojify= display_name(account)
+      %span.p-name.emojify= display_name(account, custom_emojify: true)
       %small<
         %span>< @#{account.local_username_and_domain}
         = fa_icon('lock') if account.locked?
@@ -28,7 +28,7 @@
           - account.fields.each do |field|
             %dl
               %dt.emojify{ title: field.name }= field.name
-              %dd.emojify{ title: field.value }= Formatter.instance.format_field(account, field.value)
+              %dd.emojify{ title: field.value }= Formatter.instance.format_field(account, field.value, custom_emojify: true)
 
     .details-counters
       .counter{ class: active_nav_class(short_account_url(account)) }
diff --git a/app/views/accounts/_moved_strip.html.haml b/app/views/accounts/_moved_strip.html.haml
index 6a14a5dd30..ae18c6dc70 100644
--- a/app/views/accounts/_moved_strip.html.haml
+++ b/app/views/accounts/_moved_strip.html.haml
@@ -3,7 +3,7 @@
 .moved-strip
   .moved-strip__message
     = fa_icon 'suitcase'
-    = t('accounts.moved_html', name: content_tag(:strong, display_name(account), class: :emojify), new_profile_link: link_to(content_tag(:strong, safe_join(['@', content_tag(:span, moved_to_account.acct)])), TagManager.instance.url_for(moved_to_account), class: 'mention'))
+    = t('accounts.moved_html', name: content_tag(:strong, display_name(account, custom_emojify: true), class: :emojify), new_profile_link: link_to(content_tag(:strong, safe_join(['@', content_tag(:span, moved_to_account.acct)])), TagManager.instance.url_for(moved_to_account), class: 'mention'))
 
   .moved-strip__card
     = link_to TagManager.instance.url_for(moved_to_account), class: 'detailed-status__display-name p-author h-card', target: '_blank', rel: 'noopener' do
@@ -13,5 +13,5 @@
           .account__avatar-overlay-overlay{ style: "background-image: url('#{account.avatar.url(:original)}')" }
 
       %span.display-name
-        %strong.emojify= display_name(moved_to_account)
+        %strong.emojify= display_name(moved_to_account, custom_emojify: true)
         %span @#{moved_to_account.acct}
diff --git a/app/views/admin/reports/_account.html.haml b/app/views/admin/reports/_account.html.haml
index 22b7a08618..9ac161c9c6 100644
--- a/app/views/admin/reports/_account.html.haml
+++ b/app/views/admin/reports/_account.html.haml
@@ -15,5 +15,5 @@
           .account__avatar{ style: "background-image: url(#{account.avatar.url}); width: #{size}px; height: #{size}px; background-size: #{size}px #{size}px" }
         %span.display-name
           %bdi
-            %strong.display-name__html.emojify= display_name(account)
+            %strong.display-name__html.emojify= display_name(account, custom_emojify: true)
           %span.display-name__account @#{account.acct}
diff --git a/app/views/authorize_follows/_card.html.haml b/app/views/authorize_follows/_card.html.haml
index e81e292ba6..9abcfd37e1 100644
--- a/app/views/authorize_follows/_card.html.haml
+++ b/app/views/authorize_follows/_card.html.haml
@@ -6,7 +6,7 @@
     %span.display-name
       - account_url = local_assigns[:admin] ? admin_account_path(account.id) : TagManager.instance.url_for(account)
       = link_to account_url, class: 'detailed-status__display-name p-author h-card', target: '_blank', rel: 'noopener' do
-        %strong.emojify= display_name(account)
+        %strong.emojify= display_name(account, custom_emojify: true)
         %span @#{account.acct}
 
   - if account.note?
diff --git a/app/views/remote_unfollows/_card.html.haml b/app/views/remote_unfollows/_card.html.haml
index e81e292ba6..9abcfd37e1 100644
--- a/app/views/remote_unfollows/_card.html.haml
+++ b/app/views/remote_unfollows/_card.html.haml
@@ -6,7 +6,7 @@
     %span.display-name
       - account_url = local_assigns[:admin] ? admin_account_path(account.id) : TagManager.instance.url_for(account)
       = link_to account_url, class: 'detailed-status__display-name p-author h-card', target: '_blank', rel: 'noopener' do
-        %strong.emojify= display_name(account)
+        %strong.emojify= display_name(account, custom_emojify: true)
         %span @#{account.acct}
 
   - if account.note?
diff --git a/app/views/shared/_landing_strip.html.haml b/app/views/shared/_landing_strip.html.haml
index ae26fc1fff..78f5ed4bcc 100644
--- a/app/views/shared/_landing_strip.html.haml
+++ b/app/views/shared/_landing_strip.html.haml
@@ -2,7 +2,7 @@
   = image_tag asset_pack_path('logo.svg'), class: 'logo'
 
   %div
-    = t('landing_strip_html', name: content_tag(:span, display_name(account), class: :emojify), link_to_root_path: link_to(content_tag(:strong, site_hostname), root_path))
+    = t('landing_strip_html', name: content_tag(:span, display_name(account, custom_emojify: true), class: :emojify), link_to_root_path: link_to(content_tag(:strong, site_hostname), root_path))
 
     - if open_registrations?
       = t('landing_strip_signup_html', sign_up_path: new_user_registration_path)
diff --git a/app/views/stream_entries/_detailed_status.html.haml b/app/views/stream_entries/_detailed_status.html.haml
index afc66d1487..c0f1e4f0f5 100644
--- a/app/views/stream_entries/_detailed_status.html.haml
+++ b/app/views/stream_entries/_detailed_status.html.haml
@@ -4,7 +4,7 @@
       .avatar
         = image_tag status.account.avatar.url(:original), width: 48, height: 48, alt: '', class: 'u-photo'
     %span.display-name
-      %strong.p-name.emojify= display_name(status.account)
+      %strong.p-name.emojify= display_name(status.account, custom_emojify: true)
       %span= acct(status.account)
 
   - if embedded_view?
diff --git a/app/views/stream_entries/_simple_status.html.haml b/app/views/stream_entries/_simple_status.html.haml
index a6f5120fb1..b89860ad90 100644
--- a/app/views/stream_entries/_simple_status.html.haml
+++ b/app/views/stream_entries/_simple_status.html.haml
@@ -10,7 +10,7 @@
         %div
           = image_tag status.account.avatar(:original), width: 48, height: 48, alt: '', class: 'u-photo'
       %span.display-name
-        %strong.p-name.emojify= display_name(status.account)
+        %strong.p-name.emojify= display_name(status.account, custom_emojify: true)
         %span= acct(status.account)
 
   .status__content.p-name.emojify<
diff --git a/app/views/stream_entries/_status.html.haml b/app/views/stream_entries/_status.html.haml
index 9764bc74da..b87ca21771 100644
--- a/app/views/stream_entries/_status.html.haml
+++ b/app/views/stream_entries/_status.html.haml
@@ -28,7 +28,7 @@
         = fa_icon('retweet fw')
       %span
         = link_to TagManager.instance.url_for(status.account), class: 'status__display-name muted' do
-          %strong.emojify= display_name(status.account)
+          %strong.emojify= display_name(status.account, custom_emojify: true)
         = t('stream_entries.reblogged')
   - elsif pinned
     .pre-header

From 50ed1e83ac924a92bdbc22b1b771e10c16eeaf65 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Sun, 6 May 2018 11:50:00 +0200
Subject: [PATCH 17/40] Bump version to 2.4.0rc1 (#7283)

---
 lib/mastodon/version.rb | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb
index a6927eec31..2c643dcd7a 100644
--- a/lib/mastodon/version.rb
+++ b/lib/mastodon/version.rb
@@ -9,11 +9,11 @@ module Mastodon
     end
 
     def minor
-      3
+      4
     end
 
     def patch
-      3
+      0
     end
 
     def pre
@@ -21,7 +21,7 @@ module Mastodon
     end
 
     def flags
-      ''
+      'rc1'
     end
 
     def to_a

From e571de29bf0adcd9bb85970316e4d5669586d63d Mon Sep 17 00:00:00 2001
From: Yamagishi Kazutoshi <ykzts@desire.sh>
Date: Sun, 6 May 2018 20:19:56 +0900
Subject: [PATCH 18/40] Weblate translations (2018-05-06) (#7381)

* Translated using Weblate (Dutch)

Currently translated at 100.0% (627 of 627 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/

* Translated using Weblate (Catalan)

Currently translated at 100.0% (627 of 627 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ca/

* Translated using Weblate (Greek)

Currently translated at 28.2% (177 of 626 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/el/

* Translated using Weblate (Greek)

Currently translated at 100.0% (294 of 294 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/el/

* Translated using Weblate (Greek)

Currently translated at 100.0% (294 of 294 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/el/

* Translated using Weblate (Occitan)

Currently translated at 100.0% (62 of 62 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/oc/

* Translated using Weblate (Chinese (Hong Kong))

Currently translated at 98.2% (615 of 626 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/zh_Hant_HK/

* Translated using Weblate (Occitan)

Currently translated at 100.0% (294 of 294 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/oc/

* Translated using Weblate (Occitan)

Currently translated at 97.9% (613 of 626 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/oc/

* Translated using Weblate (Galician)

Currently translated at 100.0% (626 of 626 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/gl/

* Translated using Weblate (Galician)

Currently translated at 100.0% (627 of 627 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/gl/

* Translated using Weblate (Greek)

Currently translated at 100.0% (294 of 294 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/el/

* Translated using Weblate (Greek)

Currently translated at 28.5% (179 of 626 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/el/

* Translated using Weblate (Greek)

Currently translated at 28.5% (179 of 626 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/el/

* Translated using Weblate (Greek)

Currently translated at 29.0% (182 of 626 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/el/

* Translated using Weblate (Greek)

Currently translated at 29.0% (182 of 626 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/el/

* Translated using Weblate (Greek)

Currently translated at 29.2% (183 of 626 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/el/

* Translated using Weblate (Greek)

Currently translated at 29.3% (184 of 626 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/el/

* Translated using Weblate (Greek)

Currently translated at 29.5% (185 of 626 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/el/

* Translated using Weblate (Greek)

Currently translated at 29.7% (186 of 626 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/el/

* Translated using Weblate (Greek)

Currently translated at 29.7% (186 of 626 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/el/

* Translated using Weblate (Slovak)

Currently translated at 95.3% (597 of 626 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (294 of 294 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/sk/

* Translated using Weblate (Slovak)

Currently translated at 95.2% (597 of 627 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/

* Translated using Weblate (Chinese (Hong Kong))

Currently translated at 97.6% (612 of 627 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/zh_Hant_HK/

* Translated using Weblate (Chinese (Hong Kong))

Currently translated at 99.3% (623 of 627 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/zh_Hant_HK/

* Translated using Weblate (Polish)

Currently translated at 99.8% (626 of 627 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pl/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (75 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/sk/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (294 of 294 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ar/

* Translated using Weblate (Arabic)

Currently translated at 83.7% (525 of 627 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ar/

* Translated using Weblate (Spanish)

Currently translated at 99.8% (626 of 627 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/es/

* Translated using Weblate (Italian)

Currently translated at 54.7% (343 of 627 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/it/

* Translated using Weblate (Italian)

Currently translated at 78.9% (495 of 627 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/it/

* Translated using Weblate (Esperanto)

Currently translated at 100.0% (294 of 294 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/eo/

* Translated using Weblate (Japanese)

Currently translated at 99.8% (626 of 627 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/

* i18n-tasks normalize && i18n-tasks remove-unused && yarn manage:translations
---
 app/javascript/mastodon/locales/ar.json |   2 +-
 app/javascript/mastodon/locales/el.json | 128 ++++++++--------
 app/javascript/mastodon/locales/eo.json |  28 ++--
 app/javascript/mastodon/locales/oc.json |   4 +-
 app/javascript/mastodon/locales/sk.json |   2 +-
 config/locales/ar.yml                   |  10 ++
 config/locales/ca.yml                   |   1 +
 config/locales/doorkeeper.sk.yml        |   8 +-
 config/locales/el.yml                   | 167 +++++++++++++++++++++
 config/locales/es.yml                   |   1 +
 config/locales/gl.yml                   |   5 +-
 config/locales/it.yml                   | 188 +++++++++++++++++++++++-
 config/locales/ja.yml                   |   1 +
 config/locales/nl.yml                   |   1 +
 config/locales/oc.yml                   |  24 +--
 config/locales/pl.yml                   |   1 +
 config/locales/simple_form.oc.yml       |   2 +-
 config/locales/sk.yml                   |   8 +-
 config/locales/zh-HK.yml                |  21 ++-
 19 files changed, 493 insertions(+), 109 deletions(-)

diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json
index 947348f701..d531d8fd5a 100644
--- a/app/javascript/mastodon/locales/ar.json
+++ b/app/javascript/mastodon/locales/ar.json
@@ -258,7 +258,7 @@
   "status.pin": "تدبيس على الملف الشخصي",
   "status.pinned": "تبويق مثبَّت",
   "status.reblog": "رَقِّي",
-  "status.reblog_private": "Boost to original audience",
+  "status.reblog_private": "القيام بالترقية إلى الجمهور الأصلي",
   "status.reblogged_by": "{name} رقى",
   "status.reply": "ردّ",
   "status.replyAll": "رُد على الخيط",
diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json
index a7e1c408f8..5b36348eb8 100644
--- a/app/javascript/mastodon/locales/el.json
+++ b/app/javascript/mastodon/locales/el.json
@@ -2,9 +2,9 @@
   "account.block": "Απόκλεισε τον/την @{name}",
   "account.block_domain": "Απόκρυψε τα πάντα από τον/την",
   "account.blocked": "Αποκλεισμένος/η",
-  "account.direct": "Απευθείας μήνυμα προς @{name}",
+  "account.direct": "Άμεσο μήνυμα προς @{name}",
   "account.disclaimer_full": "Οι παρακάτω πληροφορίες μπορει να μην αντανακλούν το προφίλ του χρήστη επαρκως.",
-  "account.domain_blocked": "Domain hidden",
+  "account.domain_blocked": "Κρυμμένος τομέας",
   "account.edit_profile": "Επεξεργάσου το προφίλ",
   "account.follow": "Ακολούθησε",
   "account.followers": "Ακόλουθοι",
@@ -23,15 +23,15 @@
   "account.requested": "Εκκρεμεί έγκριση. Κάνε κλικ για να ακυρώσεις το αίτημα ακολούθησης",
   "account.share": "Μοιράσου το προφίλ του/της @{name}",
   "account.show_reblogs": "Δείξε τις προωθήσεις του/της @{name}",
-  "account.unblock": "Unblock @{name}",
+  "account.unblock": "Ξεμπλόκαρε τον/την @{name}",
   "account.unblock_domain": "Αποκάλυψε το {domain}",
-  "account.unfollow": "Unfollow",
-  "account.unmute": "Unmute @{name}",
-  "account.unmute_notifications": "Unmute notifications from @{name}",
+  "account.unfollow": "Διακοπή παρακολούθησης",
+  "account.unmute": "Διακοπή αποσιώπησης του/της @{name}",
+  "account.unmute_notifications": "Διακοπή αποσιώπησης ειδοποιήσεων του/της @{name}",
   "account.view_full_profile": "Δες το πλήρες προφίλ",
   "alert.unexpected.message": "Προέκυψε απροσδόκητο σφάλμα.",
   "alert.unexpected.title": "Εεπ!",
-  "boost_modal.combo": "You can press {combo} to skip this next time",
+  "boost_modal.combo": "Μπορείς να πατήσεις {combo} για να το προσπεράσεις αυτό την επόμενη φορά",
   "bundle_column_error.body": "Κάτι πήγε στραβά ενώ φορτωνόταν αυτό το στοιχείο.",
   "bundle_column_error.retry": "Δοκίμασε ξανά",
   "bundle_column_error.title": "Σφάλμα δικτύου",
@@ -41,7 +41,7 @@
   "column.blocks": "Αποκλεισμένοι χρήστες",
   "column.community": "Τοπική ροή",
   "column.direct": "Απευθείας μηνύματα",
-  "column.domain_blocks": "Hidden domains",
+  "column.domain_blocks": "Κρυμμένοι τομείς",
   "column.favourites": "Αγαπημένα",
   "column.follow_requests": "Αιτήματα παρακολούθησης",
   "column.home": "Αρχική",
@@ -60,7 +60,7 @@
   "column_subheading.navigation": "Πλοήγηση",
   "column_subheading.settings": "Ρυθμίσεις",
   "compose_form.direct_message_warning": "Αυτό το τουτ θα εμφανίζεται μόνο σε όλους τους αναφερόμενους χρήστες.",
-  "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.",
+  "compose_form.hashtag_warning": "Αυτό το τουτ δεν θα εμφανίζεται κάτω από καμία ταμπέλα καθώς είναι αφανές. Μόνο τα δημόσια τουτ μπορούν να αναζητηθούν ανά ταμπέλα.",
   "compose_form.lock_disclaimer": "Ο λογαριασμός σου δεν είναι {locked}. Οποιοσδήποτε μπορεί να σε ακολουθήσει για να δει τις δημοσιεύσεις σας προς τους ακολούθους σας.",
   "compose_form.lock_disclaimer.lock": "κλειδωμένος",
   "compose_form.placeholder": "Τι σκέφτεσαι;",
@@ -78,65 +78,65 @@
   "confirmations.delete.message": "Σίγουρα θες να διαγράψεις αυτή την κατάσταση;",
   "confirmations.delete_list.confirm": "Διέγραψε",
   "confirmations.delete_list.message": "Σίγουρα θες να διαγράψεις οριστικά αυτή τη λίστα;",
-  "confirmations.domain_block.confirm": "Hide entire domain",
-  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
-  "confirmations.mute.confirm": "Mute",
-  "confirmations.mute.message": "Are you sure you want to mute {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:",
-  "emoji_button.activity": "Activity",
-  "emoji_button.custom": "Custom",
-  "emoji_button.flags": "Flags",
-  "emoji_button.food": "Food & Drink",
-  "emoji_button.label": "Insert emoji",
-  "emoji_button.nature": "Nature",
-  "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻",
-  "emoji_button.objects": "Objects",
-  "emoji_button.people": "People",
-  "emoji_button.recent": "Frequently used",
-  "emoji_button.search": "Search...",
-  "emoji_button.search_results": "Search results",
-  "emoji_button.symbols": "Symbols",
-  "emoji_button.travel": "Travel & Places",
-  "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
-  "empty_column.hashtag": "There is nothing in this hashtag yet.",
-  "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.",
-  "empty_column.home.public_timeline": "the public timeline",
-  "empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.",
-  "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
-  "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up",
-  "follow_request.authorize": "Authorize",
-  "follow_request.reject": "Reject",
-  "getting_started.appsshort": "Apps",
+  "confirmations.domain_block.confirm": "Απόκρυψη ολόκληρου του τομέα",
+  "confirmations.domain_block.message": "Σίγουρα θες να μπλοκάρεις ολόκληρο το {domain}; Συνήθως μερικά εστιασμένα μπλοκ ή αποσιωπήσεις επαρκούν και προτιμούνται.",
+  "confirmations.mute.confirm": "Αποσιώπηση",
+  "confirmations.mute.message": "Σίγουρα θες να αποσιωπήσεις τον/την {name};",
+  "confirmations.unfollow.confirm": "Διακοπή παρακολούθησης",
+  "confirmations.unfollow.message": "Σίγουρα θες να πάψεις να ακολουθείς τον/την {name};",
+  "embed.instructions": "Ενσωματώστε αυτή την κατάσταση στην ιστοσελίδα σας αντιγράφοντας τον παρακάτω κώδικα.",
+  "embed.preview": "Ορίστε πως θα φαίνεται:",
+  "emoji_button.activity": "Δραστηριότητα",
+  "emoji_button.custom": "Προσαρμοσμένα",
+  "emoji_button.flags": "Σημαίες",
+  "emoji_button.food": "Φαγητά & Ποτά",
+  "emoji_button.label": "Εισάγετε emoji",
+  "emoji_button.nature": "Φύση",
+  "emoji_button.not_found": "Ουδέν emojo!! (╯°□°)╯︵ ┻━┻",
+  "emoji_button.objects": "Αντικείμενα",
+  "emoji_button.people": "Άνθρωποι",
+  "emoji_button.recent": "Δημοφιλή",
+  "emoji_button.search": "Αναζήτηση…",
+  "emoji_button.search_results": "Αποτελέσματα αναζήτησης",
+  "emoji_button.symbols": "Σύμβολα",
+  "emoji_button.travel": "Ταξίδια & Τοποθεσίες",
+  "empty_column.community": "Η τοπική ροή είναι κενή. Γράψε κάτι δημόσιο παραμύθι ν' αρχινίσει!",
+  "empty_column.direct": "Δεν έχεις απευθείας μηνύματα ακόμα. Όταν στείλεις ή λάβεις κανένα, θα εμφανιστεί εδώ.",
+  "empty_column.hashtag": "Δεν υπάρχει ακόμα κάτι για αυτή την ταμπέλα.",
+  "empty_column.home": "Η τοπική σου ροή είναι κενή! Πήγαινε στο {public} ή κάνε αναζήτηση για να ξεκινήσεις και να γνωρίσεις άλλους χρήστες.",
+  "empty_column.home.public_timeline": "η δημόσια ροή",
+  "empty_column.list": "Δεν υπάρχει τίποτα σε αυτή τη λίστα ακόμα. Όταν τα μέλη της δημοσιεύσουν νέες καταστάσεις, θα εμφανιστούν εδώ",
+  "empty_column.notifications": "Δεν έχεις ειδοποιήσεις ακόμα. Αλληλεπίδρασε με άλλους χρήστες για να ξεκινήσεις την κουβέντα.",
+  "empty_column.public": "Δεν υπάρχει τίποτα εδώ! Γράψε κάτι δημόσιο, ή ακολούθησε χειροκίνητα χρήστες από άλλα instances για να το γεμίσεις",
+  "follow_request.authorize": "Ενέκρινε",
+  "follow_request.reject": "Απέρριψε",
+  "getting_started.appsshort": "Εφαρμογές",
   "getting_started.faq": "FAQ",
-  "getting_started.heading": "Getting started",
-  "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}.",
-  "getting_started.userguide": "User Guide",
-  "home.column_settings.advanced": "Advanced",
-  "home.column_settings.basic": "Basic",
-  "home.column_settings.filter_regex": "Filter out by regular expressions",
-  "home.column_settings.show_reblogs": "Show boosts",
-  "home.column_settings.show_replies": "Show replies",
-  "home.settings": "Column settings",
-  "keyboard_shortcuts.back": "to navigate back",
-  "keyboard_shortcuts.boost": "to boost",
-  "keyboard_shortcuts.column": "to focus a status in one of the columns",
-  "keyboard_shortcuts.compose": "to focus the compose textarea",
+  "getting_started.heading": "Ξεκινώντας",
+  "getting_started.open_source_notice": "Το Mastodon είναι ελεύθερο λογισμικό. Μπορείς να συνεισφέρεις ή να αναφέρεις ζητήματα στο GitHub στο {github}.",
+  "getting_started.userguide": "Οδηγός Χρηστών",
+  "home.column_settings.advanced": "Προχωρημένα",
+  "home.column_settings.basic": "Βασικά",
+  "home.column_settings.filter_regex": "Φιλτράρετε μέσω regular expressions",
+  "home.column_settings.show_reblogs": "Εμφάνιση προωθήσεων",
+  "home.column_settings.show_replies": "Εμφάνιση απαντήσεων",
+  "home.settings": "Ρυθμίσεις στηλών",
+  "keyboard_shortcuts.back": "για επιστροφή πίσω",
+  "keyboard_shortcuts.boost": "για προώθηση",
+  "keyboard_shortcuts.column": "για εστίαση μιας κατάστασης σε μια από τις στήλες",
+  "keyboard_shortcuts.compose": "για εστίαση στην περιοχή κειμένου συγγραφής",
   "keyboard_shortcuts.description": "Description",
-  "keyboard_shortcuts.down": "to move down in the list",
+  "keyboard_shortcuts.down": "για κίνηση προς τα κάτω στη λίστα",
   "keyboard_shortcuts.enter": "to open status",
-  "keyboard_shortcuts.favourite": "to favourite",
+  "keyboard_shortcuts.favourite": "για σημείωση αγαπημένου",
   "keyboard_shortcuts.heading": "Keyboard Shortcuts",
-  "keyboard_shortcuts.hotkey": "Hotkey",
-  "keyboard_shortcuts.legend": "to display this legend",
-  "keyboard_shortcuts.mention": "to mention author",
-  "keyboard_shortcuts.reply": "to reply",
-  "keyboard_shortcuts.search": "to focus search",
-  "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW",
-  "keyboard_shortcuts.toot": "to start a brand new toot",
+  "keyboard_shortcuts.hotkey": "Συντόμευση",
+  "keyboard_shortcuts.legend": "για να εμφανίσεις αυτόν τον οδηγό",
+  "keyboard_shortcuts.mention": "για να αναφέρεις το συγγραφέα",
+  "keyboard_shortcuts.reply": "για απάντηση",
+  "keyboard_shortcuts.search": "για εστίαση αναζήτησης",
+  "keyboard_shortcuts.toggle_hidden": "για εμφάνιση/απόκρυψη κειμένου πίσω από την προειδοποίηση",
+  "keyboard_shortcuts.toot": "για δημιουργία ολοκαίνουριου τουτ",
   "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
   "keyboard_shortcuts.up": "to move up in the list",
   "lightbox.close": "Close",
diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
index 37587c14c6..1ab6d2aa09 100644
--- a/app/javascript/mastodon/locales/eo.json
+++ b/app/javascript/mastodon/locales/eo.json
@@ -2,7 +2,7 @@
   "account.block": "Bloki @{name}",
   "account.block_domain": "Kaŝi ĉion de {domain}",
   "account.blocked": "Blokita",
-  "account.direct": "Direct message @{name}",
+  "account.direct": "Rekte mesaĝi @{name}",
   "account.disclaimer_full": "Subaj informoj povas reflekti la profilon de la uzanto nekomplete.",
   "account.domain_blocked": "Domajno kaŝita",
   "account.edit_profile": "Redakti profilon",
@@ -29,8 +29,8 @@
   "account.unmute": "Malsilentigi @{name}",
   "account.unmute_notifications": "Malsilentigi sciigojn de @{name}",
   "account.view_full_profile": "Vidi plenan profilon",
-  "alert.unexpected.message": "An unexpected error occurred.",
-  "alert.unexpected.title": "Oops!",
+  "alert.unexpected.message": "Neatendita eraro okazis.",
+  "alert.unexpected.title": "Ups!",
   "boost_modal.combo": "Vi povas premi {combo} por preterpasi sekvafoje",
   "bundle_column_error.body": "Io misfunkciis en la ŝargado de ĉi tiu elemento.",
   "bundle_column_error.retry": "Bonvolu reprovi",
@@ -40,8 +40,8 @@
   "bundle_modal_error.retry": "Bonvolu reprovi",
   "column.blocks": "Blokitaj uzantoj",
   "column.community": "Loka tempolinio",
-  "column.direct": "Direct messages",
-  "column.domain_blocks": "Hidden domains",
+  "column.direct": "Rektaj mesaĝoj",
+  "column.domain_blocks": "Kaŝitaj domajnoj",
   "column.favourites": "Stelumoj",
   "column.follow_requests": "Petoj de sekvado",
   "column.home": "Hejmo",
@@ -59,7 +59,7 @@
   "column_header.unpin": "Depingli",
   "column_subheading.navigation": "Navigado",
   "column_subheading.settings": "Agordado",
-  "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.",
+  "compose_form.direct_message_warning": "Tiu mesaĝo videblos nur por ĉiuj menciitaj uzantoj.",
   "compose_form.hashtag_warning": "Ĉi tiu mesaĝo ne estos listigita per ajna kradvorto. Nur publikaj mesaĝoj estas serĉeblaj per kradvortoj.",
   "compose_form.lock_disclaimer": "Via konta ne estas {locked}. Iu ajn povas sekvi vin por vidi viajn mesaĝojn, kiuj estas nur por sekvantoj.",
   "compose_form.lock_disclaimer.lock": "ŝlosita",
@@ -101,7 +101,7 @@
   "emoji_button.symbols": "Simboloj",
   "emoji_button.travel": "Vojaĝoj kaj lokoj",
   "empty_column.community": "La loka tempolinio estas malplena. Skribu ion por plenigi ĝin!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "Vi ankoraŭ ne havas rektan mesaĝon. Kiam vi sendos aŭ ricevos iun, ĝi aperos ĉi tie.",
   "empty_column.hashtag": "Ankoraŭ estas nenio per ĉi tiu kradvorto.",
   "empty_column.home": "Via hejma tempolinio estas malplena! Vizitu {public} aŭ uzu la serĉilon por renkonti aliajn uzantojn.",
   "empty_column.home.public_timeline": "la publikan tempolinion",
@@ -135,7 +135,7 @@
   "keyboard_shortcuts.mention": "por mencii la aŭtoron",
   "keyboard_shortcuts.reply": "por respondi",
   "keyboard_shortcuts.search": "por fokusigi la serĉilon",
-  "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW",
+  "keyboard_shortcuts.toggle_hidden": "por montri/kaŝi tekston malantaŭ enhava averto",
   "keyboard_shortcuts.toot": "por komenci tute novan mesaĝon",
   "keyboard_shortcuts.unfocus": "por malfokusigi la tekstujon aŭ la serĉilon",
   "keyboard_shortcuts.up": "por iri supren en la listo",
@@ -157,8 +157,8 @@
   "mute_modal.hide_notifications": "Ĉu vi volas kaŝi la sciigojn el ĉi tiu uzanto?",
   "navigation_bar.blocks": "Blokitaj uzantoj",
   "navigation_bar.community_timeline": "Loka tempolinio",
-  "navigation_bar.direct": "Direct messages",
-  "navigation_bar.domain_blocks": "Hidden domains",
+  "navigation_bar.direct": "Rektaj mesaĝoj",
+  "navigation_bar.domain_blocks": "Kaŝitaj domajnoj",
   "navigation_bar.edit_profile": "Redakti profilon",
   "navigation_bar.favourites": "Stelumoj",
   "navigation_bar.follow_requests": "Petoj de sekvado",
@@ -242,10 +242,10 @@
   "search_results.total": "{count, number} {count, plural, one {rezulto} other {rezultoj}}",
   "standalone.public_title": "Enrigardo…",
   "status.block": "Bloki @{name}",
-  "status.cancel_reblog_private": "Unboost",
+  "status.cancel_reblog_private": "Eksdiskonigi",
   "status.cannot_reblog": "Ĉi tiu mesaĝo ne diskonigeblas",
   "status.delete": "Forigi",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "Rekte mesaĝi @{name}",
   "status.embed": "Enkorpigi",
   "status.favourite": "Stelumi",
   "status.load_more": "Ŝargi pli",
@@ -258,7 +258,7 @@
   "status.pin": "Alpingli profile",
   "status.pinned": "Alpinglita mesaĝo",
   "status.reblog": "Diskonigi",
-  "status.reblog_private": "Boost to original audience",
+  "status.reblog_private": "Diskonigi al la originala atentaro",
   "status.reblogged_by": "{name} diskonigis",
   "status.reply": "Respondi",
   "status.replyAll": "Respondi al la fadeno",
@@ -276,7 +276,7 @@
   "tabs_bar.home": "Hejmo",
   "tabs_bar.local_timeline": "Loka tempolinio",
   "tabs_bar.notifications": "Sciigoj",
-  "tabs_bar.search": "Search",
+  "tabs_bar.search": "Serĉi",
   "ui.beforeunload": "Via malneto perdiĝos se vi eliras de Mastodon.",
   "upload_area.title": "Altreni kaj lasi por alŝuti",
   "upload_button.label": "Aldoni aŭdovidaĵon",
diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json
index d4836e9fe0..c4cb996cf3 100644
--- a/app/javascript/mastodon/locales/oc.json
+++ b/app/javascript/mastodon/locales/oc.json
@@ -110,7 +110,7 @@
   "empty_column.public": "I a pas res aquí ! Escrivètz quicòm de public, o seguètz de personas d’autras instàncias per garnir lo flux public",
   "follow_request.authorize": "Autorizar",
   "follow_request.reject": "Regetar",
-  "getting_started.appsshort": "Apps",
+  "getting_started.appsshort": "Aplicacions",
   "getting_started.faq": "FAQ",
   "getting_started.heading": "Per començar",
   "getting_started.open_source_notice": "Mastodon es un logicial liure. Podètz contribuir e mandar vòstres comentaris e rapòrt de bug via {github} sus GitHub.",
@@ -158,7 +158,7 @@
   "navigation_bar.blocks": "Personas blocadas",
   "navigation_bar.community_timeline": "Flux public local",
   "navigation_bar.direct": "Messatges dirèctes",
-  "navigation_bar.domain_blocks": "Hidden domains",
+  "navigation_bar.domain_blocks": "Domenis amagats",
   "navigation_bar.edit_profile": "Modificar lo perfil",
   "navigation_bar.favourites": "Favorits",
   "navigation_bar.follow_requests": "Demandas d’abonament",
diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json
index e5e826c964..d69648ccff 100644
--- a/app/javascript/mastodon/locales/sk.json
+++ b/app/javascript/mastodon/locales/sk.json
@@ -231,7 +231,7 @@
   "report.target": "Nahlásenie {target}",
   "search.placeholder": "Hľadaj",
   "search_popout.search_format": "Pokročilé vyhľadávanie",
-  "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
+  "search_popout.tips.full_text": "Jednoduchý textový výpis statusov ktoré si napísal/a, ktoré si obľúbil/a, povýšil/a, alebo aj tých, v ktorých si bol/a spomenutý/á, a potom všetky zadaniu odpovedajúce prezívky, mená a haštagy.",
   "search_popout.tips.hashtag": "haštag",
   "search_popout.tips.status": "status",
   "search_popout.tips.text": "Jednoduchý text vráti zhodujúce sa mená, prezývky a hashtagy",
diff --git a/config/locales/ar.yml b/config/locales/ar.yml
index eb0127eb51..632bd03b03 100644
--- a/config/locales/ar.yml
+++ b/config/locales/ar.yml
@@ -4,6 +4,7 @@ ar:
     about_hashtag_html: هذه هي الرسائل العامة مع الكلمات الدلالية  <strong> 1#%{hashtag}</strong>. يمكنك التفاعل معهم إذا كان لديك حساب في أي مكان على الإنترنت المتحد.
     about_mastodon_html: ماستدون شبكة إجتماعية <em>حرة و مفتوحة المصدر</em>. هو بديل <em>لامركزي</em> لمنصات تجارية ، يمكنك من تجنب احتكار شركة واحدة للإتصالات الخاصة بك. يمكنك اختيار أي خادم تثق فيه. أيهما تختار، يمكنك التفاعل مع أي شخص آخر على الشبكة. يمكن لأي شخص تنصيب و تشغيل خادم ماستدون خاص به والمشاركة في <em>الشبكات الاجتماعية</em> بكل شفافية.
     about_this: عن مثيل الخادوم هذا
+    administered_by: 'يديره :'
     closed_registrations: التسجيلات في مثيل الخادوم هذا مُغلقة حاليًا.
     contact: للتواصل معنا
     contact_missing: غير محدد
@@ -58,7 +59,15 @@ ar:
       destroyed_msg: تم تدمير ملاحظة الإشراف بنجاح !
     accounts:
       are_you_sure: متأكد ؟
+      avatar: الصورة الرمزية
       by_domain: النطاق
+      change_email:
+        changed_msg: تم تعديل عنوان البريد الإلكتروني الخاص بالحساب بنجاح !
+        current_email: عنوان البريد الإلكتروني الحالي
+        label: تعديل عنوان البريد الإلكتروني
+        new_email: عنوان البريد الإلكتروني الجديد
+        submit: تعديل عنوان البريد الإلكتروني
+        title: تعديل عنوان البريد الإلكتروني الخاص بـ %{username}
       confirm: تأكيد
       confirmed: مؤكَّد
       confirming: التأكد
@@ -108,6 +117,7 @@ ar:
       public: عمومي
       push_subscription_expires: انتهاء الاشتراك ”PuSH“
       redownload: تحديث الصورة الرمزية
+      remove_avatar: حذف الصورة الرمزية
       resend_confirmation:
         already_confirmed: هذا المستخدم مؤكد بالفعل
         send: أعد إرسال رسالة البريد الالكتروني الخاصة بالتأكيد
diff --git a/config/locales/ca.yml b/config/locales/ca.yml
index bdc3f74844..9956c00988 100644
--- a/config/locales/ca.yml
+++ b/config/locales/ca.yml
@@ -373,6 +373,7 @@ ca:
   admin_mailer:
     new_report:
       body: "%{reporter} ha informat de %{target}"
+      body_remote: Algú des de el domini %{domain} ha informat sobre %{target}
       subject: Informe nou per a %{instance} (#%{id})
   application_mailer:
     notification_preferences: Canvia les preferències de correu
diff --git a/config/locales/doorkeeper.sk.yml b/config/locales/doorkeeper.sk.yml
index bda26429e8..65b3b496ee 100644
--- a/config/locales/doorkeeper.sk.yml
+++ b/config/locales/doorkeeper.sk.yml
@@ -33,14 +33,14 @@ sk:
       help:
         native_redirect_uri: Použite %{native_redirect_uri} pre lokálne testy
         redirect_uri: Iba jedna URI na riadok
-        scopes: Rozsahy oddeľujte medzerami. Nechajte prázdne pre štandardné rozsahy.
+        scopes: Oprávnenia oddeľujte medzerami. Nechajte prázdne pre štandardné oprávnenia.
       index:
         application: Aplikácia
         callback_url: Návratová URL
         delete: Zmazať
         name: Názov
         new: Nová aplikácia
-        scopes: Rozsahy
+        scopes: Oprávnenia
         show: Ukázať
         title: Vaše aplikácie
       new:
@@ -49,7 +49,7 @@ sk:
         actions: Akcie
         application_id: Kľúč klienta
         callback_urls: Návratové URL adresy
-        scopes: Rozsahy
+        scopes: Oprávnenia
         secret: Tajné slovo klienta
         title: 'Aplikácia: %{name}'
     authorizations:
@@ -73,7 +73,7 @@ sk:
         application: Aplikácia
         created_at: Autorizované
         date_format: "%Y-%m-%d %H:%M:%S"
-        scopes: Rozsahy
+        scopes: Oprávnenia
         title: Vaše autorizované aplikácie
     errors:
       messages:
diff --git a/config/locales/el.yml b/config/locales/el.yml
index 8741635e1d..da2b7a64a8 100644
--- a/config/locales/el.yml
+++ b/config/locales/el.yml
@@ -1,12 +1,15 @@
 ---
 el:
   about:
+    about_hashtag_html: Αυτά είναι δημόσια τουτ σημειωμένα με <strong>#%{hashtag}</strong>. Μπορείς να αλληλεπιδράσεις με αυτά αν έχεις λογαριασμό οπουδήποτε στο fediverse.
     about_mastodon_html: Το Mastodon είναι ένα κοινωνικό δίκτυο που βασίζεται σε ανοιχτά δικτυακά πρωτόκολλα και ελεύθερο λογισμικό ανοιχτού κώδικα. Είναι αποκεντρωμένο όπως το e-mail.
     about_this: Σχετικά
     administered_by: 'Διαχειρίζεται από:'
     closed_registrations: Αυτή τη στιγμή οι εγγραφές σε αυτό τον διακομιστή είναι κλειστές. Αλλά! Μπορείς να βρεις έναν άλλο διακομιστή για να ανοίξεις λογαριασμό και να έχεις πρόσβαση από εκεί στο ίδιο ακριβώς δίκτυο.
     contact: Επικοινωνία
     contact_missing: Δεν έχει οριστεί
+    contact_unavailable: Μ/Δ
+    description_headline: Τι είναι το %{domain};
     domain_count_after: άλλοι διακομιστές
     domain_count_before: Συνδέεται με
     extended_description_html: |
@@ -15,9 +18,12 @@ el:
     features:
       humane_approach_body: Μαθαίνοντας από τις αποτυχίες άλλων δικτύων, το Mastodon στοχεύει να κάνει σχεδιαστικά ηθικές επιλογές για να καταπολεμήσει την κακόβουλη χρήση των κοινωνικών δικτύων.
       humane_approach_title: Μια πιο ανθρώπινη προσέγγιση
+      not_a_product_body: Το Mastodon δεν είναι εμπορικό δίκτυο. Χωρίς διαφημίσεις, χωρίς εξόρυξη δεδομένων, χωρίς φραγμένες περιοχές. Δεν έχει κεντρική αρχή.
       not_a_product_title: Είσαι άτομο, όχι προϊόν
+      real_conversation_body: Με 500 χαρακτήρες στη διάθεσή σου και υποστήριξη για λεπτομερή έλεγχο και προειδοποιήσεις πολυμέσων, μπορείς να εκφραστείς με τον τρόπο που θέλεις.
       real_conversation_title: Φτιαγμένο για αληθινή συζήτηση
       within_reach_body: Οι πολλαπλές εφαρμογές για το iOS, το Android και τις υπόλοιπες πλατφόρμες, χάρη σε ένα φιλικό προς τους προγραμματιστές οικοσύστημα API, σου επιτρέπουν να κρατάς επαφή με τους φίλους και τις φίλες σου οπουδήποτε.
+      within_reach_title: Πάντα προσβάσιμο
     generic_description: "%{domain} είναι ένας εξυπηρετητής στο δίκτυο"
     hosted_on: Το Mastodon φιλοξενείται στο %{domain}
     learn_more: Μάθε περισσότερα
@@ -26,6 +32,7 @@ el:
     status_count_after: καταστάσεις
     status_count_before: Ποιός συνέγραψε
     user_count_after: χρήστες
+    user_count_before: Σπίτι
     what_is_mastodon: Τι είναι το Mastodon;
   accounts:
     follow: Ακολούθησε
@@ -38,3 +45,163 @@ el:
     people_who_follow: Χρήστες που ακολουθούν τον/την %{name}
     posts: Τουτ
     posts_with_replies: Τουτ και απαντήσεις
+    remote_follow: Απομακρυσμένη παρακολούθηση
+    reserved_username: Το όνομα χρήστη είναι κατειλημμένο
+    roles:
+      admin: Διαχειριστής
+      moderator: Μεσολαβητής
+    unfollow: Διακοπή παρακολούθησης
+  admin:
+    account_moderation_notes:
+      create: Δημιουργία
+      created_msg: Επιτυχής δημιουργία σημειώματος μεσολάβησης!
+      delete: Διαγραφή
+      destroyed_msg: Επιτυχής καταστροφή σημειώματος μεσολάβησης!
+    accounts:
+      are_you_sure: Σίγουρα;
+      avatar: Αβατάρ
+      by_domain: Τομέας
+      change_email:
+        changed_msg: Επιτυχής αλλαγή email λογαριασμού!
+        current_email: Τρέχον email
+        label: Αλλαγή email
+        new_email: Νέο email
+        submit: Αλλαγή email
+        title: Αλλαγή email για %{username}
+      confirm: Επιβεβαίωση
+      confirmed: Επιβεβαιώθηκε
+      demote: Υποβίβαση
+      disable: Απενεργοποίηση
+      disable_two_factor_authentication: Απενεργοποίηση 2FA
+      disabled: Απενεργοποιημένο
+      display_name: Όνομα εμφάνισης
+      domain: Τομέας
+      edit: Αλλαγή
+      email: Email
+      enable: Ενεργοποίηση
+      enabled: Ενεργοποιημένο
+      feed_url: URL ροής
+      followers: Ακόλουθοι
+      followers_url: URL ακολούθων
+      follows: Ακολουθεί
+      inbox_url: URL εισερχομένων
+      ip: IP
+      location:
+        all: Όλα
+        local: Τοπικά
+        remote: Απομακρυσμένα
+        title: Τοποθεσία
+      login_status: Κατάσταση εισόδου
+      media_attachments: Συνημμένα πολυμέσα
+      memorialize: Μετατροπή σε νεκρολογία
+      moderation:
+        all: Όλα
+        silenced: Αποσιωπημένα
+        suspended: Σε αναστολή
+        title: Μεσολάβηση
+      moderation_notes: Σημειώσεις μεσολάβησης
+      most_recent_activity: Πιο πρόσφατη δραστηριότητα
+      most_recent_ip: Πιο πρόσφατη IP
+      not_subscribed: Άνευ εγγραφής
+      order:
+        alphabetic: Αλφαβητικά
+        most_recent: Πιο πρόσφατα
+        title: Ταξινόμηση
+      outbox_url: URL εξερχομένων
+      perform_full_suspension: Πλήρης αναστολή
+      profile_url: URL προφίλ
+      promote: Προβίβασε
+      protocol: Πρωτόκολλο
+      public: Δημόσιο
+      push_subscription_expires: Η εγγραφή PuSH λήγει
+      redownload: Ανανέωση αβατάρ
+      remove_avatar: Απομακρυσμένο αβατάρ
+      reset: Επαναφορά
+      reset_password: Επαναφορά συνθηματικού
+      resubscribe: Επανεγγραφή
+      role: Δικαιώματα
+      roles:
+        admin: Διαχειριστής
+        moderator: Μεσολαβητής
+        staff: Προσωπικό
+        user: Χρήστης
+      salmon_url: URL Salmon
+      search: Αναζήτηση
+      shared_inbox_url: URL κοινόχρηστων εισερχομένων
+      show:
+        created_reports: Αναφορές από αυτόν το λογαριασμό
+        report: ανάφερε
+        targeted_reports: Αναφορές για αυτόν το λογαριασμό
+      silence: Αποσιώπησε
+      statuses: Καταστάσεις
+      subscribe: Εγγραφή
+      title: Λογαριασμοί
+      unconfirmed_email: Ανεπιβεβαίωτο email
+      undo_silenced: Αναίρεση αποσιώπησης
+      undo_suspension: Αναίρεση παύσης
+      unsubscribe: Κατάργηση εγγραφής
+      username: Όνομα χρήστη
+      web: Διαδίκτυο
+    action_logs:
+      actions:
+        assigned_to_self_report: Ο/Η %{name} ανάθεσε την αναφορά %{target} στον εαυτό του/της
+        change_email_user: Ο/Η %{name} άλλαξε τη διεύθυνση email του χρήστη %{target}
+        confirm_user: Ο/Η %{name} επιβεβαίωσε τη διεύθυνση email του χρήστη %{target}
+        create_custom_emoji: Ο/Η %{name} ανέβασε νέο emoji %{target}
+        create_domain_block: Ο/Η %{name} μπλόκαρε τον τομέα %{target}
+        create_email_domain_block: Ο/Η %{name} έβαλε τον τομέα email %{target} σε μαύρη λίστα
+        demote_user: Ο/Η %{name} υποβίβασε το χρήστη %{target}
+        destroy_domain_block: Ο/Η %{name} ξεμπλόκαρε τον τομέα %{target}
+        destroy_email_domain_block: Ο/Η %{name} έβαλε τον τομέα email %{target} σε λευκή λίστα
+        destroy_status: Ο/Η %{name} αφαίρεσε την κατάσταση του/της %{target}
+        disable_2fa_user: Ο/Η %{name} απενεργοποίησε την απαίτηση δύο παραγόντων για το χρήστη %{target}
+        disable_custom_emoji: Ο/Η %{name} απενεργοποίησε το emoji %{target}
+        disable_user: Ο/Η %{name} απενεργοποίησε την είσοδο για το χρήστη %{target}
+        enable_custom_emoji: Ο/Η %{name} ενεργοποίησε το emoji %{target}
+        enable_user: Ο/Η %{name} ενεργοποίησε την είσοδο του χρήστη %{target}
+        memorialize_account: Ο/Η %{name} μετέτρεψε το λογαριασμό του/της %{target} σε σελίδα νεκρολογίας
+        promote_user: Ο/Η %{name} προβίβασε το χρήστη %{target}
+        remove_avatar_user: Ο/Η %{name} αφαίρεσε το αβατάρ του/της %{target}
+        reopen_report: Ο/Η %{name} ξανάνοιξε την αναφορά %{target}
+        reset_password_user: Ο/Η %{name} επανέφερε το συνθηματικό του χρήστη %{target}
+        resolve_report: Ο/Η %{name} επέλυσε την αναφορά %{target}
+        silence_account: Ο/Η %{name} αποσιώπησε το λογαριασμό του/της %{target}
+        suspend_account: Ο/Η %{name} έπαυσε το λογαριασμό του/της %{target}
+        unassigned_report: Ο/Η %{name} αποδέσμευσε την αναφορά %{target}
+        unsilence_account: Ο/Η %{name} ήρε την αποσιώπηση του λογαριασμού του/της %{target}
+        unsuspend_account: Ο/Η %{name} ήρε την παύση του λογαριασμού του χρήστη %{target}
+        update_custom_emoji: Ο/Η %{name} ενημέρωσε το emoji %{target}
+        update_status: Ο/Η %{name} ενημέρωσε την κατάσταση του/της %{target}
+      title: Αρχείο ελέγχου
+    custom_emojis:
+      by_domain: Τομέας
+      copied_msg: Επιτυχής δημιουργία τοπικού αντίγραφου του emoji
+      copy: Αντιγραφή
+      copy_failed_msg: Αδυναμία δημιουργίας τοπικού αντίγραφου αυτού του emoji
+      created_msg: Επιτυχής δημιουργία του emoji!
+      delete: Διαγραφή
+      destroyed_msg: Επιτυχής καταστροφή του emojo!
+      disable: Απενεργοποίηση
+      disabled_msg: Επιτυχής απενεργοποίηση αυτού του emoji
+      emoji: Emoji
+      enable: Ενεργοποίηση
+      enabled_msg: Επιτυχής ενεργοποίηση αυτού του emoji
+      image_hint: PNG έως 50KB
+      listed: Αναφερθέντα
+      new:
+        title: Προσθήκη νέου προσαρμοσμένου emoji
+      overwrite: Αντικατάσταση
+      shortcode: Σύντομος κωδικός
+      shortcode_hint: Τουλάχιστον 2 χαρακτήρες, μόνο αλφαριθμητικοί και κάτω παύλες
+      title: Προσαρμοσμένα emoji
+      update_failed_msg: Αδυναμία ενημέρωσης του emoji
+      updated_msg: Επιτυχής ενημέρωση του Emoji!
+      upload: Ανέβασμα
+    domain_blocks:
+      add_new: Προσθήκη νέου
+      created_msg: Ο αποκλεισμός τομέα είναι υπό επεξεργασία
+      destroyed_msg: Ο αποκλεισμός τομέα άρθηκε
+      domain: Τομέας
+      new:
+        create: Δημιουργία αποκλεισμού
+        hint: Ο αποκλεισμός τομέα δεν θα αποτρέψει νέες καταχωρίσεις λογαριασμών στην βάση δεδομένων, αλλά θα εφαρμόσει αναδρομικά και αυτόματα συγκεκριμένες πολιτικές μεσολάβησης σε αυτούς τους λογαριασμούς.
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 11bee3239b..593716b169 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -373,6 +373,7 @@ es:
   admin_mailer:
     new_report:
       body: "%{reporter} ha reportado a %{target}"
+      body_remote: Alguien de %{domain} a reportado a %{target}
       subject: Nuevo reporte para la %{instance} (#%{id})
   application_mailer:
     notification_preferences: Cambiar preferencias de correo electrónico
diff --git a/config/locales/gl.yml b/config/locales/gl.yml
index e00c4b4333..e676fe8374 100644
--- a/config/locales/gl.yml
+++ b/config/locales/gl.yml
@@ -19,8 +19,8 @@ gl:
       humane_approach_body: Aprendendo dos erros de outras redes, Mastodon intenta tomar decisións éticas de deseño para loitar contra os usos incorrectos da rede.
       humane_approach_title: Unha aproximación máis humana
       not_a_product_body: Mastodon non é unha rede comercial. Sen anuncios, sen minería de datos, sen xardíns privados. Non hai autoridade centralizada.
-      not_a_product_title: Vostede é unha persona, non un producto
-      real_conversation_body: Con 500 caracteres a súa disposición e soporte para contido ao por menor e avisos sobre o contido, pode expresarse vostede do xeito que queira.
+      not_a_product_title: Vostede é unha persoa, non un producto
+      real_conversation_body: Con 500 caracteres a súa disposición, soporte para contido polo miúdo e avisos sobre o contido, pode expresarse vostede con libertade.
       real_conversation_title: Construído para conversacións reais
       within_reach_body: Existen múltiples aplicativos para iOS, Android e outras plataformas grazas a un entorno API amigable para o desenvolvedor que lle permite estar ao tanto cos seus amigos en calquer lugar.
       within_reach_title: Sempre en contacto
@@ -373,6 +373,7 @@ gl:
   admin_mailer:
     new_report:
       body: "%{reporter} informou sobre %{target}"
+      body_remote: Alguén desde %{domain} informou sobre %{target}
       subject: Novo informe sobre %{instance} (#%{id})
   application_mailer:
     notification_preferences: Cambiar os axustes de correo-e
diff --git a/config/locales/it.yml b/config/locales/it.yml
index eafe6b2a17..b53645c9fd 100644
--- a/config/locales/it.yml
+++ b/config/locales/it.yml
@@ -143,6 +143,17 @@ it:
         confirm_user: "%{name} ha confermato l'indirizzo email per l'utente %{target}"
         create_custom_emoji: "%{name} ha caricato un nuovo emoji %{target}"
         create_domain_block: "%{name} ha bloccato il dominio %{target}"
+        disable_custom_emoji: "%{name} ha disabilitato l'emoji %{target}"
+        disable_user: "%{name} ha disabilitato il login per l'utente %{target}"
+        enable_custom_emoji: "%{name} ha abilitato l'emoji %{target}"
+        enable_user: "%{name} ha abilitato il login per l'utente %{target}"
+        remove_avatar_user: "%{name} ha eliminato l'avatar di %{target}"
+        reset_password_user: "%{name} ha reimpostato la password dell'utente %{target}"
+        silence_account: "%{name} ha silenziato l'account di %{target}"
+        suspend_account: "%{name} ha sospeso l'account di %{target}"
+        unsilence_account: "%{name} ha de-silenziato l'account di %{target}"
+        unsuspend_account: "%{name} ha annullato la sospensione dell'account di %{target}"
+        update_custom_emoji: "%{name} ha aggiornato l'emoji %{target}"
     custom_emojis:
       by_domain: Dominio
       copied_msg: Creata con successo una copia locale dell'emoji
@@ -162,6 +173,7 @@ it:
         title: Aggiungi nuovo emoji personalizzato
       overwrite: Sovrascrivi
       shortcode: Shortcode
+      shortcode_hint: Almeno due caratteri, solo caratteri alfanumerici e trattino basso
       title: Emoji personalizzate
       unlisted: Non elencato
       update_failed_msg: Impossibile aggiornare questa emojii
@@ -185,6 +197,13 @@ it:
         suspend: Sospendi
       severity: Severità
       show:
+        affected_accounts:
+          one: Interessato un solo account nel database
+          other: Interessati %{count} account nel database
+        retroactive:
+          silence: De-silenzia tutti gli account esistenti da questo dominio
+          suspend: Annulla la sospensione di tutti gli account esistenti da questo dominio
+        title: Annulla il blocco del dominio per %{domain}
         undo: Annulla
       title: Blocchi dominio
       undo: Annulla
@@ -238,12 +257,19 @@ it:
       view: Mostra
     settings:
       activity_api_enabled:
+        desc_html: Conteggi degli status pubblicati localmente, degli utenti attivi e delle nuove registrazioni in gruppi settimanali
         title: Pubblica statistiche aggregate circa l'attività dell'utente
+      bootstrap_timeline_accounts:
+        title: Seguiti predefiniti per i nuovi utenti
       contact_information:
         username: Nome utente del contatto
       peers_api_enabled:
+        desc_html: Nomi di dominio che questa istanza ha incontrato nella fediverse
         title: Pubblica elenco di istanze scoperte
       registrations:
+        closed_message:
+          desc_html: Mostrato nella pagina iniziale quando le registrazioni sono chiuse. Puoi usare tag HTML
+          title: Messaggio per registrazioni chiuse
         deletion:
           desc_html: Consenti a chiunque di cancellare il proprio account
           title: Apri la cancellazione dell'account
@@ -251,6 +277,9 @@ it:
           disabled: Nessuno
         open:
           desc_html: Consenti a chiunque di creare un account
+          title: Apri registrazioni
+      show_known_fediverse_at_about_page:
+        desc_html: Quando attivato, mostra nell'anteprima i toot da tutte le istanze conosciute. Altrimenti mostra solo i toot locali.
       show_staff_badge:
         title: Mostra badge staff
       site_description:
@@ -262,6 +291,7 @@ it:
         title: Anteprima timeline
       title: Impostazioni sito
     statuses:
+      back_to_account: Torna alla pagina dell'account
       batch:
         delete: Elimina
         nsfw_off: NSFW OFF
@@ -270,6 +300,7 @@ it:
       media:
         title: Media
       no_media: Nessun media
+      title: Gli status dell'account
       with_media: con media
     subscriptions:
       callback_url: URL Callback
@@ -288,16 +319,23 @@ it:
     created: Applicazione creata con successo
     destroyed: Applicazione eliminata con successo
     invalid_url: L'URL fornito non è valido
+    regenerate_token: Rigenera token di accesso
+    token_regenerated: Token di accesso rigenerato
+    warning: Fa' molta attenzione con questi dati. Non fornirli mai a nessun altro!
   auth:
+    agreement_html: Iscrivendoti, accetti di seguire <a href="%{rules_path}">le regole dell'istanza</a> e <a href="%{terms_path}"> le nostre condizioni di servizio</a>.
     change_password: Password
     confirm_email: Conferma email
     delete_account: Elimina account
+    delete_account_html: Se desideri cancellare il tuo account, puoi <a href="%{path}">farlo qui</a>. Ti sarà chiesta conferma.
     didnt_get_confirmation: Non hai ricevuto le istruzioni di conferma?
     forgot_password: Hai dimenticato la tua password?
     login: Entra
     logout: Logout
     migrate_account: Sposta ad un account differente
+    migrate_account_html: Se vuoi che questo account sia reindirizzato a uno diverso, puoi <a href="%{path}">configurarlo qui</a>.
     or: o
+    or_log_in_with: Oppure accedi con
     register: Iscriviti
     register_elsewhere: Iscriviti su un altro server
     resend_confirmation: Invia di nuovo le istruzioni di conferma
@@ -308,6 +346,11 @@ it:
     already_following: Stai già seguendo questo account
     error: Sfortunatamente c'è stato un errore nel consultare l'account remoto
     follow: Segui
+    follow_request: 'Hai mandato una richiesta di diventare seguace a:'
+    following: 'Accettato! Ora stai seguendo:'
+    post_follow:
+      close: Oppure puoi chiudere questa finestra.
+      return: Torna al profilo dell'utente
     title: Segui %{acct}
   datetime:
     distance_in_words:
@@ -323,11 +366,41 @@ it:
       x_minutes: "%{count} minuti"
       x_months: "%{count} mesi"
       x_seconds: "%{count} secondi"
+  deletes:
+    bad_password_msg: Ci avete provato, hacker! Password errata
+    confirm_password: Inserisci la tua password attuale per verificare la tua identità
+    description_html: Questa azione eliminerà <strong>in modo permanente e irreversibile</strong> tutto il contenuto del tuo account e lo disattiverà. Il tuo nome utente resterà riservato per prevenire che qualcuno in futuro assuma la tua identità.
+    proceed: Cancella l'account
+    success_msg: Il tuo account è stato cancellato
+    warning_html: È garantita solo la cancellazione del contenuto solo da questa istanza. I contenuti che sono stati ampiamente condivisi probabilmente lasceranno delle tracce. I server offline e quelli che non ricevono più i tuoi aggiornamenti non aggiorneranno i loro database.
+  errors:
+    '403': Non sei autorizzato a visualizzare questa pagina.
+    '404': La pagina che stavi cercando non esiste.
+    '410': La pagina che stavi cercando non esiste più.
+    '422':
+      content: Verifica di sicurezza non riuscita. Stai bloccando i cookies?
+      title: Verifica di sicurezza non riuscita
+    noscript_html: Per usare l'interfaccia web di Mastodon dovi abilitare JavaScript. In alternativa puoi provare una delle <a href="https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md">app native</a> per Mastodon per la tua piattaforma.
   exports:
+    archive_takeout:
+      date: Data
+      download: Scarica il tuo archivio
+      hint_html: Puoi richiedere un archivio dei tuoi <strong>toot e media caricati</strong>. I dati esportati sono in formato ActivityPub, leggibili da qualunque software che segue questo standard.
+      in_progress: Creazione archivio...
+      request: Richiedi il tuo archivio
+      size: Dimensioni
     blocks: Stai bloccando
     csv: CSV
     follows: Stai seguendo
+    mutes: Stai silenziando
     storage: Archiviazione media
+  followers:
+    domain: Dominio
+    explanation_html: Se vuoi garantire la privacy dei tuoi status, devi sapere chi ti sta seguendo. <strong>I tuoi status privati vengono inviati a tutte le istanze su cui hai dei seguaci</strong>. Puoi controllare chi sono i tuoi seguaci, ed eliminarli se non hai fiducia che la tua privacy venga rispettata dallo staff o dal software di quelle istanze.
+    followers_count: Numero di seguaci
+    purge: Elimina dai seguaci
+    true_privacy_html: Tieni presente che <strong>l'effettiva riservatezza si può ottenere solo con la crittografia end-to-end</strong>.
+    unlocked_warning_html: Chiunque può seguirti per vedere immediatamente i tuoi status privati. %{lock_link} per poter esaminare e respingere gli utenti che vogliono seguirti.
   generic:
     changes_saved_msg: Modifiche effettuate con successo!
     powered_by: offerto da %{link}
@@ -341,38 +414,76 @@ it:
     types:
       blocking: Lista dei bloccati
       following: Lista dei seguaci
+      muting: Lista dei silenziati
     upload: Carica
+  in_memoriam_html: In Memoriam.
+  invites:
+    delete: Disattiva
+    expired: Scaduto
+    expires_in:
+      '1800': 30 minuti
+      '21600': 6 ore
+      '3600': 1 ora
+      '43200': 12 ore
+      '604800': 1 settimana
+      '86400': 1 giorno
+    expires_in_prompt: Mai
+    generate: Genera
+    max_uses_prompt: Nessun limite
+    prompt: Genera dei link e forniscili ad altri per concedere l'accesso a questa istanza
+    table:
+      expires_at: Scade
+    title: Invita persone
   landing_strip_html: "<strong>%{name}</strong> è un utente su %{link_to_root_path}. Puoi seguirlo o interagire con lui se possiedi un account ovunque nel fediverse."
   landing_strip_signup_html: Se non possiedi un account, puoi <a href="%{sign_up_path}">iscriverti qui</a>.
+  lists:
+    errors:
+      limit: Hai raggiunto il numero massimo di liste
   media_attachments:
     validations:
       images_and_video: Impossibile allegare video a un post che contiene già immagini
       too_many: Impossibile allegare più di 4 file
+  migrations:
+    acct: utente@dominio del nuovo account
+    currently_redirecting: 'Il tuo profilo sarà ridirezionato a:'
+    proceed: Salva
+    updated_msg: L'impostazione per la migrazione dell'account è sta aggiornata!
+  moderation:
+    title: Moderazione
   notification_mailer:
     digest:
+      action: Vedi tutte le notifiche
       body: 'Ecco un breve riassunto di quello che ti sei perso su %{instance} dalla tua ultima visita del %{since}:'
       mention: "%{name} ti ha menzionato:"
       new_followers_summary:
-        one: Hai ricevuto un nuovo seguace! Urrà!
-        other: Hai ricevuto %{count} nuovi seguaci! Incredibile!
+        one: E inoltre hai ricevuto un nuovo seguace mentre eri assente! Urrà!
+        other: Inoltre, hai acquisito %{count} nuovi seguaci mentre eri assente! Incredibile!
       subject:
         one: "1 nuova notifica dalla tua ultima visita \U0001F418"
         other: "%{count} nuove notifiche dalla tua ultima visita \U0001F418"
+      title: In tua assenza…
     favourite:
       body: 'Il tuo status è stato apprezzato da %{name}:'
       subject: "%{name} ha apprezzato il tuo status"
+      title: Nuovo preferito
     follow:
       body: "%{name} ti sta seguendo!"
       subject: "%{name} ti sta seguendo"
+      title: Nuovo seguace
     follow_request:
+      action: Gestisci richieste di essere seguito
       body: "%{name} ha chiesto di seguirti"
       subject: 'Seguace in sospeso: %{name}'
+      title: Nuova richiesta di essere seguito
     mention:
+      action: Rispondi
       body: 'Sei stato menzionato da %{name} su:'
       subject: Sei stato menzionato da %{name}
+      title: Nuova menzione
     reblog:
       body: 'Il tuo status è stato condiviso da %{name}:'
       subject: "%{name} ha condiviso il tuo status"
+      title: Nuova condivisione
   number:
     human:
       decimal_units:
@@ -385,35 +496,82 @@ it:
           trillion: T
           unit: ''
   pagination:
+    newer: Più recente
     next: Avanti
+    older: Più vecchio
     prev: Indietro
     truncate: "&hellip;"
+  preferences:
+    languages: Lingue
+    other: Altro
+    publishing: Pubblicazione
+    web: Web
+  push_notifications:
+    favourite:
+      title: "%{name} ha segnato il tuo status come apprezzato"
+    follow:
+      title: "%{name} ha iniziato a seguirti"
+    group:
+      title: "%{count} notifiche"
+    mention:
+      action_expand: Mostra altro
+      action_favourite: Apprezzato
+    reblog:
+      title: "%{name} ha condiviso il tuo status"
   remote_follow:
     acct: Inserisci il tuo username@dominio da cui vuoi seguire questo utente
     missing_resource: Impossibile trovare l'URL di reindirizzamento richiesto per il tuo account
     proceed: Conferma
     prompt: 'Stai per seguire:'
+  remote_unfollow:
+    error: Errore
+    title: Titolo
+  sessions:
+    browser: Browser
+    browsers:
+      blackberry: Blackberry
+    current_session: Sessione corrente
+    description: "%{browser} su %{platform}"
+    platforms:
+      other: piattaforma sconosciuta
   settings:
     authorized_apps: Applicazioni autorizzate
     back: Torna a Mastodon
+    delete: Cancellazione account
+    development: Sviluppo
     edit_profile: Modifica profilo
     export: Esporta impostazioni
+    followers: Seguaci autorizzati
     import: Importa
+    migrate: Migrazione dell'account
+    notifications: Notifiche
     preferences: Preferenze
     settings: Impostazioni
-    two_factor_authentication: Autenticazione a Due Fattori
+    two_factor_authentication: Autenticazione a due fattori
+    your_apps: Le tue applicazioni
   statuses:
     open_in_web: Apri sul Web
     over_character_limit: Limite caratteri superato di %{max}
+    pin_errors:
+      limit: Hai già fissato in cima il massimo numero di toot
+      ownership: Non puoi fissare in cima un toot di qualcun altro
+      private: Un toot non pubblico non può essere fissato in cima
+      reblog: Un toot condiviso non può essere fissato in cima
     show_more: Mostra di più
     visibilities:
       private: Mostra solo ai tuoi seguaci
+      private_long: Mostra solo ai seguaci
       public: Pubblico
+      public_long: Tutti lo possono vedere
       unlisted: Pubblico, ma non visibile sulla timeline pubblica
+      unlisted_long: Tutti lo possono vedere, ma non compare nelle timeline pubbliche
   stream_entries:
     click_to_show: Clicca per mostrare
+    pinned: Toot fissato in cima
     reblogged: condiviso
     sensitive_content: Materiale sensibile
+  themes:
+    contrast: Contrasto elevato
   time:
     formats:
       default: "%b %d, %Y, %H:%M"
@@ -422,15 +580,39 @@ it:
     description_html: Se abiliti <strong>l'autorizzazione a due fattori</strong>, entrare nel tuo account ti richiederà di avere vicino il tuo telefono, il quale ti genererà un codice per eseguire l'accesso.
     disable: Disabilita
     enable: Abilita
+    enabled: È abilitata l'autenticazione a due fattori
     enabled_success: Autenticazione a due fattori attivata con successo
+    generate_recovery_codes: Genera codici di recupero
     instructions_html: "<strong>Scannerizza questo QR code con Google Authenticator o un'app TOTP simile sul tuo telefono</strong>. Da ora in poi, quell'applicazione genererà codici da inserire necessariamente per eseguire l'accesso."
+    lost_recovery_codes: I codici di recupero ti permettono di accedere al tuo account se perdi il telefono. Se hai perso i tuoi codici di recupero, puoi rigenerarli qui. Quelli vecchi saranno annullati.
     manual_instructions: 'Se non puoi scannerizzare il QR code e hai bisogno di inserirlo manualmente, questo è il codice segreto in chiaro:'
+    recovery_codes_regenerated: I codici di recupero sono stati rigenerati
+    recovery_instructions_html: Se perdi il telefono, puoi usare uno dei codici di recupero qui sotto per riottenere l'accesso al tuo account. <strong>Conserva i codici di recupero in un posto sicuro</strong>. Ad esempio puoi stamparli e conservarli insieme ad altri documenti importanti.
     setup: Configura
     wrong_code: Il codice inserito non è corretto! Assicurati che l'orario del server e l'orario del telefono siano corretti.
   user_mailer:
+    backup_ready:
+      explanation: Hai richiesto un backup completo del tuo account Mastodon. È pronto per essere scaricato!
+      subject: Il tuo archivio è pronto per essere scaricato
+      title: Esportazione archivio
     welcome:
+      edit_profile_step: Puoi personalizzare il tuo profilo caricando un avatar, un'intestazione, modificando il tuo nome visualizzato e così via. Se vuoi controllare i tuoi nuovi seguaci prima di autorizzarli a seguirti, puoi bloccare il tuo account.
+      explanation: Ecco alcuni suggerimenti per iniziare
+      final_action: Inizia a postare
+      final_step: 'Inizia a postare! Anche se non hai seguaci, i tuoi messaggi pubblici possono essere visti da altri, ad esempio nelle timeline locali e negli hashtag. Se vuoi puoi presentarti con l''hashtag #introductions.'
+      full_handle: Il tuo nome utente completo
+      full_handle_hint: Questo è ciò che diresti ai tuoi amici in modo che possano seguirti o contattarti da un'altra istanza.
+      review_preferences_action: Cambia preferenze
+      review_preferences_step: Dovresti impostare le tue preferenze, ad esempio quali email vuoi ricevere oppure il livello predefinito di privacy per i tuoi post. Se le immagini in movimento non ti danno fastidio, puoi abilitare l'animazione automatica delle GIF.
+      subject: Benvenuto/a su Mastodon
+      tip_bridge_html: Se vieni da Twitter, puoi trovare i tuoi amici su Mastodon usando la<a href="%{bridge_url}">app bridge</a>. Ma funziona solo se anche loro la usano!
+      tip_federated_timeline: La timeline federata visualizza uno dopo l'altro i messaggi pubblicati su Mastodon. Ma comprende solo gli utenti seguiti dai tuoi vicini, quindi non è completa.
+      tip_following: Per impostazione predefinita, segui l'amministratore/i del tuo server. Per trovare utenti più interessanti, dà un'occhiata alle timeline locale e federata.
+      tip_local_timeline: La timeline locale visualizza uno dopo l'altro i messaggi degli utenti di %{instance}. Questi sono i tuoi vicini!
+      tip_mobile_webapp: Se il tuo browser mobile ti dà la possibilità di aggiungere Mastodon allo schermo, puoi ricevere le notifiche. Funziona un po' come un'app natova!
       tips: Suggerimenti
       title: Benvenuto a bordo, %{name}!
   users:
     invalid_email: L'indirizzo email inserito non è valido
     invalid_otp_token: Codice d'accesso non valido
+    seamless_external_login: Ti sei collegato per mezzo di un servizio esterno, quindi le impostazioni di email e password non sono disponibili.
diff --git a/config/locales/ja.yml b/config/locales/ja.yml
index c7025fc186..83283a3771 100644
--- a/config/locales/ja.yml
+++ b/config/locales/ja.yml
@@ -373,6 +373,7 @@ ja:
   admin_mailer:
     new_report:
       body: "%{reporter} が %{target} を通報しました"
+      body_remote: "%{domain} の誰かが %{target} を通報しました"
       subject: "%{instance} の新しい通報 (#%{id})"
   application_mailer:
     notification_preferences: メール設定の変更
diff --git a/config/locales/nl.yml b/config/locales/nl.yml
index 6ca81b3757..44ad1ed6d8 100644
--- a/config/locales/nl.yml
+++ b/config/locales/nl.yml
@@ -373,6 +373,7 @@ nl:
   admin_mailer:
     new_report:
       body: "%{reporter} heeft %{target} gerapporteerd"
+      body_remote: Iemand van %{domain} heeft %{target} gerapporteerd
       subject: Nieuwe toots gerapporteerd op %{instance} (#%{id})
   application_mailer:
     notification_preferences: E-mailvoorkeuren wijzigen
diff --git a/config/locales/oc.yml b/config/locales/oc.yml
index e61f7ebd00..82c8eacbb2 100644
--- a/config/locales/oc.yml
+++ b/config/locales/oc.yml
@@ -49,7 +49,7 @@ oc:
     reserved_username: Aqueste nom d’utilizaire es reservat
     roles:
       admin: Admin
-      moderator: Mod
+      moderator: Moderador
     unfollow: Quitar de sègre
   admin:
     account_moderation_notes:
@@ -128,7 +128,7 @@ oc:
       role: Permissions
       roles:
         admin: Administrator
-        moderator: Moderator
+        moderator: Moderador
         staff: Personnal
         user: Uitlizaire
       salmon_url: URL Salmon
@@ -168,10 +168,12 @@ oc:
         memorialize_account: "%{name} transformèt en memorial la pagina de perfil a %{target}"
         promote_user: "%{name} promoguèt %{target}"
         remove_avatar_user: "%{name} suprimèt l’avatar a %{target}"
+        reopen_report: "%{name} tornèt dobrir lo rapòrt %{target}"
         reset_password_user: "%{name} reïnicializèt lo senhal a %{target}"
-        resolve_report: "%{name} anullèt lo rapòrt de %{target}"
+        resolve_report: "%{name} anullèt lo rapòrt %{target}"
         silence_account: "%{name} metèt en silenci lo compte a %{target}"
         suspend_account: "%{name} susprenguèt lo compte a %{target}"
+        unassigned_report: "%{name} daissèt de tractar lo rapòrt %{target}"
         unsilence_account: "%{name} levèt lo silenci del compte a %{target}"
         unsuspend_account: "%{name} restabliguèt lo compte a %{target}"
         update_custom_emoji: "%{name} metèt a jorn l’emoji %{target}"
@@ -266,6 +268,8 @@ oc:
         report: rapòrt
       action_taken_by: Mesura menada per
       are_you_sure: Es segur ?
+      assign_to_self: Me l’assignar
+      assigned: Moderador assignat
       comment:
         none: Pas cap
       created_at: Creacion
@@ -283,12 +287,15 @@ oc:
       reported_account: Compte senhalat
       reported_by: Senhalat per
       resolved: Resolgut
+      resolved_msg: Rapòrt corrèctament resolgut  !
       silence_account: Metre lo compte en silenci
       status: Estatut
       suspend_account: Suspendre lo compte
       target: Cibla
       title: Senhalament
+      unassign: Levar
       unresolved: Pas resolguts
+      updated_at: Actualizat
       view: Veire
     settings:
       activity_api_enabled:
@@ -346,8 +353,8 @@ oc:
       back_to_account: Tornar a la pagina Compte
       batch:
         delete: Suprimir
-        nsfw_off: NSFW OFF
-        nsfw_on: NSFW ON
+        nsfw_off: Marcar coma pas sensible
+        nsfw_on: Marcar coma sensible
       failed_to_execute: Fracàs
       media:
         title: Mèdia
@@ -487,9 +494,7 @@ oc:
       less_than_x_minutes:
         one: Fa mens d’una minuta
         other: Fa mens de %{count} minutas
-      less_than_x_seconds:
-        one: Fa mens d’una segonda
-        other: Fa mens de %{count} segondas
+      less_than_x_seconds: Ara meteis
       over_x_years:
         one: Fa mai d’un an
         other: Fa mai de %{count} ans
@@ -700,7 +705,7 @@ oc:
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
       opera: Opera
-      otter: Otter
+      otter: Autre
       phantom_js: PhantomJS
       qq: QQ Browser
       safari: Safari
@@ -750,6 +755,7 @@ oc:
       video:
         one: "%{count} vidèo"
         other: "%{count} vidèos"
+    content_warning: 'Avertiment de contengut : %{warning}'
     disallowed_hashtags:
       one: 'conten una etiqueta desactivada : %{tags}'
       other: 'conten las etiquetas desactivadas : %{tags}'
diff --git a/config/locales/pl.yml b/config/locales/pl.yml
index 1fbe0d5c8f..0d107ed53a 100644
--- a/config/locales/pl.yml
+++ b/config/locales/pl.yml
@@ -374,6 +374,7 @@ pl:
   admin_mailer:
     new_report:
       body: Użytkownik %{reporter} zgłosił %{target}
+      body_remote: Ktoś z %{domain} zgłosił %{target}
       subject: Nowe zgłoszenie na %{instance} (#%{id})
   application_mailer:
     notification_preferences: Zmień ustawienia e-maili
diff --git a/config/locales/simple_form.oc.yml b/config/locales/simple_form.oc.yml
index 4ca58c1023..c80f85cce2 100644
--- a/config/locales/simple_form.oc.yml
+++ b/config/locales/simple_form.oc.yml
@@ -41,7 +41,7 @@ oc:
         header: Bandièra
         locale: Lenga
         locked: Far venir lo compte privat
-        max_uses: Limit d’utilizacion
+        max_uses: Limit d’utilizacions
         new_password: Nòu senhal
         note: Bio
         otp_attempt: Còdi Two-factor
diff --git a/config/locales/sk.yml b/config/locales/sk.yml
index ef756b474e..033019e0b4 100644
--- a/config/locales/sk.yml
+++ b/config/locales/sk.yml
@@ -1,7 +1,7 @@
 ---
 sk:
   about:
-    about_hashtag_html: Toto sú verejné toot príspevky otagované <strong>#%{tagom}</strong>. Ak máš účet niekde vo fediverse, môžeš ich používať.
+    about_hashtag_html: Toto sú verejné toot príspevky otagované <strong>#%{hashtag}</strong>. Ak máš účet niekde vo fediverse, môžeš ich používať.
     about_mastodon_html: Mastodon je sociálna sieť založená na otvorených webových protokoloch. Jej zrojový kód je otvorený a je decentralizovaná podobne ako email.
     about_this: O tejto instancii
     administered_by: 'Správca je:'
@@ -635,10 +635,10 @@ sk:
     title: Sezóna
   settings:
     authorized_apps: Autorizované aplikácie
-    back: Späť do Mastodonu
-    delete: Zmazanie účtu
+    back: Späť na Mastodon
+    delete: Vymazanie účtu
     development: Vývoj
-    edit_profile: Upraviť profil
+    edit_profile: Uprav profil
     export: Exportovať dáta
     followers: Povolení sledovatelia
     import: Importovať
diff --git a/config/locales/zh-HK.yml b/config/locales/zh-HK.yml
index c5d8c86968..a5f6a960a5 100644
--- a/config/locales/zh-HK.yml
+++ b/config/locales/zh-HK.yml
@@ -227,6 +227,7 @@ zh-HK:
       severity: 阻隔分級
       show:
         affected_accounts:
+          one: 資料庫中有 %{count} 個用戶受影響
           other: 資料庫中有%{count}個用戶受影響
         retroactive:
           silence: 對此域名的所有用戶取消靜音
@@ -372,6 +373,7 @@ zh-HK:
   admin_mailer:
     new_report:
       body: "%{reporter} 舉報了用戶 %{target}。"
+      body_remote: 來自 %{domain} 的用戶舉報了用戶 %{target}
       subject: 來自 %{instance} 的用戶舉報(#%{id})
   application_mailer:
     notification_preferences: 更改電郵首選項
@@ -510,7 +512,9 @@ zh-HK:
       '86400': 1 天後
     expires_in_prompt: 永不過期
     generate: 生成邀請連結
-    max_uses: "%{count} 次"
+    max_uses:
+      one: 1 次
+      other: "%{count} 次"
     max_uses_prompt: 無限制
     prompt: 生成分享連結,邀請他人在本服務站註冊
     table:
@@ -577,7 +581,6 @@ zh-HK:
           quadrillion: Q
           thousand: K
           trillion: T
-          unit: ''
   pagination:
     newer: 較新
     next: 下一頁
@@ -669,9 +672,16 @@ zh-HK:
   statuses:
     attached:
       description: 附件: %{attached}
-      image: "%{count} 張圖片"
-      video: "%{count} 段影片"
+      image:
+        one: "%{count} 幅圖片"
+        other: "%{count} 幅圖片"
+      video:
+        one: "%{count} 段影片"
+        other: "%{count} 段影片"
     content_warning: 'Content warning: %{warning}'
+    disallowed_hashtags:
+      one: 包含不允許的標籤: %{tags}
+      other: 包含不允許的標籤: %{tags}
     open_in_web: 開啟網頁
     over_character_limit: 超過了 %{max} 字的限制
     pin_errors:
@@ -695,6 +705,9 @@ zh-HK:
     sensitive_content: 敏感內容
   terms:
     title: "%{instance} 使用條款和隱私權政策"
+  themes:
+    contrast: 高對比
+    default: 萬象
   time:
     formats:
       default: "%Y年%-m月%d日 %H:%M"

From 5c7bed6bbcc53895272879558640e48f04e2e8b3 Mon Sep 17 00:00:00 2001
From: ThibG <thib@sitedethib.com>
Date: Sun, 6 May 2018 20:38:17 +0200
Subject: [PATCH 19/40] Various improvements to the report UI (#7342)

* Also display replies in report modal

* Allow report modal to be up to 80% of viewport height

* Use narrow no-break space where needed in the French translation
---
 .../features/ui/components/report_modal.js    |  6 ++---
 app/javascript/mastodon/locales/fr.json       | 24 +++++++++----------
 .../styles/mastodon/components.scss           |  2 +-
 3 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/app/javascript/mastodon/features/ui/components/report_modal.js b/app/javascript/mastodon/features/ui/components/report_modal.js
index 8a55c553c1..8616f0315c 100644
--- a/app/javascript/mastodon/features/ui/components/report_modal.js
+++ b/app/javascript/mastodon/features/ui/components/report_modal.js
@@ -30,7 +30,7 @@ const makeMapStateToProps = () => {
       account: getAccount(state, accountId),
       comment: state.getIn(['reports', 'new', 'comment']),
       forward: state.getIn(['reports', 'new', 'forward']),
-      statusIds: OrderedSet(state.getIn(['timelines', `account:${accountId}`, 'items'])).union(state.getIn(['reports', 'new', 'status_ids'])),
+      statusIds: OrderedSet(state.getIn(['timelines', `account:${accountId}:with_replies`, 'items'])).union(state.getIn(['reports', 'new', 'status_ids'])),
     };
   };
 
@@ -64,12 +64,12 @@ export default class ReportModal extends ImmutablePureComponent {
   }
 
   componentDidMount () {
-    this.props.dispatch(expandAccountTimeline(this.props.account.get('id')));
+    this.props.dispatch(expandAccountTimeline(this.props.account.get('id'), { withReplies: true }));
   }
 
   componentWillReceiveProps (nextProps) {
     if (this.props.account !== nextProps.account && nextProps.account) {
-      this.props.dispatch(expandAccountTimeline(nextProps.account.get('id')));
+      this.props.dispatch(expandAccountTimeline(nextProps.account.get('id'), { withReplies: true }));
     }
   }
 
diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json
index a4af97ddab..16bf4033c2 100644
--- a/app/javascript/mastodon/locales/fr.json
+++ b/app/javascript/mastodon/locales/fr.json
@@ -13,7 +13,7 @@
   "account.hide_reblogs": "Masquer les partages de @{name}",
   "account.media": "Média",
   "account.mention": "Mentionner",
-  "account.moved_to": "{name} a déménagé vers :",
+  "account.moved_to": "{name} a déménagé vers :",
   "account.mute": "Masquer @{name}",
   "account.mute_notifications": "Ignorer les notifications de @{name}",
   "account.muted": "Silencé",
@@ -30,7 +30,7 @@
   "account.unmute_notifications": "Réactiver les notifications de @{name}",
   "account.view_full_profile": "Afficher le profil complet",
   "alert.unexpected.message": "Une erreur non-attendue s'est produite.",
-  "alert.unexpected.title": "Oups !",
+  "alert.unexpected.title": "Oups !",
   "boost_modal.combo": "Vous pouvez appuyer sur {combo} pour pouvoir passer ceci, la prochaine fois",
   "bundle_column_error.body": "Une erreur s’est produite lors du chargement de ce composant.",
   "bundle_column_error.retry": "Réessayer",
@@ -77,7 +77,7 @@
   "confirmations.delete.confirm": "Supprimer",
   "confirmations.delete.message": "Confirmez-vous la suppression de ce pouet ?",
   "confirmations.delete_list.confirm": "Supprimer",
-  "confirmations.delete_list.message": "Êtes-vous sûr de vouloir supprimer définitivement cette liste ?",
+  "confirmations.delete_list.message": "Êtes-vous sûr de vouloir supprimer définitivement cette liste ?",
   "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",
@@ -85,14 +85,14 @@
   "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 le code ci-dessous.",
-  "embed.preview": "Il apparaîtra comme cela :",
+  "embed.preview": "Il apparaîtra comme cela :",
   "emoji_button.activity": "Activités",
   "emoji_button.custom": "Personnalisés",
   "emoji_button.flags": "Drapeaux",
   "emoji_button.food": "Nourriture & Boisson",
   "emoji_button.label": "Insérer un émoji",
   "emoji_button.nature": "Nature",
-  "emoji_button.not_found": "Pas d'emojis !! (╯°□°)╯︵ ┻━┻",
+  "emoji_button.not_found": "Pas d'emojis !! (╯°□°)╯︵ ┻━┻",
   "emoji_button.objects": "Objets",
   "emoji_button.people": "Personnages",
   "emoji_button.recent": "Fréquemment utilisés",
@@ -154,7 +154,7 @@
   "media_gallery.toggle_visible": "Modifier la visibilité",
   "missing_indicator.label": "Non trouvé",
   "missing_indicator.sublabel": "Ressource introuvable",
-  "mute_modal.hide_notifications": "Masquer les notifications de cette personne ?",
+  "mute_modal.hide_notifications": "Masquer les notifications de cette personne ?",
   "navigation_bar.blocks": "Comptes bloqués",
   "navigation_bar.community_timeline": "Fil public local",
   "navigation_bar.direct": "Messages directs",
@@ -177,9 +177,9 @@
   "notifications.clear": "Nettoyer les notifications",
   "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é⋅e·s :",
-  "notifications.column_settings.mention": "Mentions :",
+  "notifications.column_settings.favourite": "Favoris :",
+  "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",
   "notifications.column_settings.reblog": "Partages :",
@@ -216,7 +216,7 @@
   "privacy.unlisted.long": "Ne pas afficher dans les fils publics",
   "privacy.unlisted.short": "Non-listé",
   "regeneration_indicator.label": "Chargement…",
-  "regeneration_indicator.sublabel": "Le flux de votre page principale est en cours de préparation !",
+  "regeneration_indicator.sublabel": "Le flux de votre page principale est en cours de préparation !",
   "relative_time.days": "{number} j",
   "relative_time.hours": "{number} h",
   "relative_time.just_now": "à l’instant",
@@ -224,8 +224,8 @@
   "relative_time.seconds": "{number} s",
   "reply_indicator.cancel": "Annuler",
   "report.forward": "Transférer à {target}",
-  "report.forward_hint": "Le compte provient d'un autre serveur. Envoyez également une copie anonyme du rapport ?",
-  "report.hint": "Le rapport sera envoyé aux modérateurs de votre instance. Vous pouvez expliquer pourquoi vous signalez ce compte ci-dessous :",
+  "report.forward_hint": "Le compte provient d'un autre serveur. Envoyez également une copie anonyme du rapport ?",
+  "report.hint": "Le rapport sera envoyé aux modérateurs de votre instance. Vous pouvez expliquer pourquoi vous signalez ce compte ci-dessous :",
   "report.placeholder": "Commentaires additionnels",
   "report.submit": "Envoyer",
   "report.target": "Signalement",
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 7cf6f4b769..3e2a1ae104 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -4033,7 +4033,7 @@ a.status-card {
 .report-modal__statuses {
   flex: 1 1 auto;
   min-height: 20vh;
-  max-height: 40vh;
+  max-height: 80vh;
   overflow-y: auto;
   overflow-x: hidden;
 

From e06fbc4fcfbaaa2d420cbbace37d7c228a8bfc5b Mon Sep 17 00:00:00 2001
From: ThibG <thib@sitedethib.com>
Date: Sun, 6 May 2018 22:26:39 +0200
Subject: [PATCH 20/40] Fixes/rollback scroll changes (#7387)

* Revert "Do not re-position scroll when loading more (inserting items from below) (#7344)"

This reverts commit 8c601b54ccf530bd193b4500fee439aa4e9162d0.

* Revert "Prevent timeline from moving when cursor is hovering over it (fixes #7278) (#7327)"

This reverts commit 58852695c8ec490239ed3812f82971f8c1e6c172.
---
 .../mastodon/components/scrollable_list.js          | 13 ++-----------
 1 file changed, 2 insertions(+), 11 deletions(-)

diff --git a/app/javascript/mastodon/components/scrollable_list.js b/app/javascript/mastodon/components/scrollable_list.js
index 7cdd63910e..fd6858d05b 100644
--- a/app/javascript/mastodon/components/scrollable_list.js
+++ b/app/javascript/mastodon/components/scrollable_list.js
@@ -35,7 +35,6 @@ export default class ScrollableList extends PureComponent {
 
   state = {
     fullscreen: null,
-    mouseOver: false,
   };
 
   intersectionObserverWrapper = new IntersectionObserverWrapper();
@@ -72,7 +71,7 @@ export default class ScrollableList extends PureComponent {
     const someItemInserted = React.Children.count(prevProps.children) > 0 &&
       React.Children.count(prevProps.children) < React.Children.count(this.props.children) &&
       this.getFirstChildKey(prevProps) !== this.getFirstChildKey(this.props);
-    if (someItemInserted && this.node.scrollTop > 0 || (this.state.mouseOver && !prevProps.isLoading)) {
+    if (someItemInserted && this.node.scrollTop > 0) {
       return this.node.scrollHeight - this.node.scrollTop;
     } else {
       return null;
@@ -140,14 +139,6 @@ export default class ScrollableList extends PureComponent {
     this.props.onLoadMore();
   }
 
-  handleMouseEnter = () => {
-    this.setState({ mouseOver: true });
-  }
-
-  handleMouseLeave = () => {
-    this.setState({ mouseOver: false });
-  }
-
   render () {
     const { children, scrollKey, trackScroll, shouldUpdateScroll, isLoading, hasMore, prepend, emptyMessage, onLoadMore } = this.props;
     const { fullscreen } = this.state;
@@ -158,7 +149,7 @@ export default class ScrollableList extends PureComponent {
 
     if (isLoading || childrenCount > 0 || !emptyMessage) {
       scrollableArea = (
-        <div className={classNames('scrollable', { fullscreen })} ref={this.setRef} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
+        <div className={classNames('scrollable', { fullscreen })} ref={this.setRef}>
           <div role='feed' className='item-list'>
             {prepend}
 

From 95595ccd2171e225f983166c6b259591ce8c861a Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Mon, 7 May 2018 01:34:19 +0200
Subject: [PATCH 21/40] Add Corsican localization (#7390)

* Added Corsican localization

* Declaring Corsican localization

* Fixed activerecord locale

* Added a missing translation and switched to French-style apostrophes

* Fixed a (predictable) mistake made while bulk-replacing apostrophes

* More fixing

* i18n-tasks normalize
---
 app/helpers/settings_helper.rb                |   1 +
 app/javascript/mastodon/locales/co.json       | 296 +++++++
 .../mastodon/locales/whitelist_co.json        |   2 +
 config/application.rb                         |   1 +
 config/locales/activerecord.co.yml            |  13 +
 config/locales/co.yml                         | 831 ++++++++++++++++++
 config/locales/devise.co.yml                  |  82 ++
 config/locales/doorkeeper.co.yml              | 119 +++
 config/locales/simple_form.co.yml             |  79 ++
 9 files changed, 1424 insertions(+)
 create mode 100644 app/javascript/mastodon/locales/co.json
 create mode 100644 app/javascript/mastodon/locales/whitelist_co.json
 create mode 100644 config/locales/activerecord.co.yml
 create mode 100644 config/locales/co.yml
 create mode 100644 config/locales/devise.co.yml
 create mode 100644 config/locales/doorkeeper.co.yml
 create mode 100644 config/locales/simple_form.co.yml

diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb
index f78e5fbc3f..f6d86a18e8 100644
--- a/app/helpers/settings_helper.rb
+++ b/app/helpers/settings_helper.rb
@@ -6,6 +6,7 @@ module SettingsHelper
     ar: 'العربية',
     bg: 'Български',
     ca: 'Català',
+    co: 'Corsu',
     de: 'Deutsch',
     el: 'Ελληνικά',
     eo: 'Esperanto',
diff --git a/app/javascript/mastodon/locales/co.json b/app/javascript/mastodon/locales/co.json
new file mode 100644
index 0000000000..2d7427c55d
--- /dev/null
+++ b/app/javascript/mastodon/locales/co.json
@@ -0,0 +1,296 @@
+{
+  "account.block": "Bluccà @{name}",
+  "account.block_domain": "Piattà tuttu da {domain}",
+  "account.blocked": "Bluccatu",
+  "account.direct": "Missaghju direttu @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.domain_blocked": "Duminiu piattatu",
+  "account.edit_profile": "Mudificà u prufile",
+  "account.follow": "Siguità",
+  "account.followers": "Abbunati",
+  "account.follows": "Abbunamenti",
+  "account.follows_you": "Vi seguita",
+  "account.hide_reblogs": "Piattà spartere da @{name}",
+  "account.media": "Media",
+  "account.mention": "Mintuvà @{name}",
+  "account.moved_to": "{name} hè partutu nant'à:",
+  "account.mute": "Piattà @{name}",
+  "account.mute_notifications": "Piattà nutificazione da @{name}",
+  "account.muted": "Piattatu",
+  "account.posts": "Statuti",
+  "account.posts_with_replies": "Statuti è risposte",
+  "account.report": "Palisà @{name}",
+  "account.requested": "In attesa d'apprubazione. Cliccate per annullà a dumanda",
+  "account.share": "Sparte u prufile di @{name}",
+  "account.show_reblogs": "Vede spartere da @{name}",
+  "account.unblock": "Sbluccà @{name}",
+  "account.unblock_domain": "Ùn piattà più {domain}",
+  "account.unfollow": "Ùn siguità più",
+  "account.unmute": "Ùn piattà più @{name}",
+  "account.unmute_notifications": "Ùn piattà più nutificazione da @{name}",
+  "account.view_full_profile": "View full profile",
+  "alert.unexpected.message": "Un prublemu inaspettatu hè accadutu.",
+  "alert.unexpected.title": "Uups!",
+  "boost_modal.combo": "Pudete appughjà nant'à {combo} per saltà quessa a prussima volta",
+  "bundle_column_error.body": "C'hè statu un prublemu caricandu st'elementu.",
+  "bundle_column_error.retry": "Pruvà torna",
+  "bundle_column_error.title": "Errore di cunnessione",
+  "bundle_modal_error.close": "Chjudà",
+  "bundle_modal_error.message": "C'hè statu un prublemu caricandu st'elementu.",
+  "bundle_modal_error.retry": "Pruvà torna",
+  "column.blocks": "Utilizatori bluccati",
+  "column.community": "Linea pubblica lucale",
+  "column.direct": "Missaghji diretti",
+  "column.domain_blocks": "Duminii piattati",
+  "column.favourites": "Favuriti",
+  "column.follow_requests": "Dumande d'abbunamentu",
+  "column.home": "Accolta",
+  "column.lists": "Liste",
+  "column.mutes": "Utilizatori piattati",
+  "column.notifications": "Nutificazione",
+  "column.pins": "Statuti puntarulati",
+  "column.public": "Linea pubblica glubale",
+  "column_back_button.label": "Ritornu",
+  "column_header.hide_settings": "Piattà i parametri",
+  "column_header.moveLeft_settings": "Spiazzà à manca",
+  "column_header.moveRight_settings": "Spiazzà à diritta",
+  "column_header.pin": "Puntarulà",
+  "column_header.show_settings": "Mustrà i parametri",
+  "column_header.unpin": "Spuntarulà",
+  "column_subheading.navigation": "Navigazione",
+  "column_subheading.settings": "Parametri",
+  "compose_form.direct_message_warning": "Solu l'utilizatori mintuvati puderenu vede stu statutu.",
+  "compose_form.hashtag_warning": "Stu statutu ùn hè \"Micca listatu\" è ùn sarà micca listatu indè e circate da hashtag. Per esse vistu in quesse, u statutu deve esse \"Pubblicu\".",
+  "compose_form.lock_disclaimer": "U vostru contu ùn hè micca {locked}. Tuttu u mondu pò seguitavi è vede i vostri statuti privati.",
+  "compose_form.lock_disclaimer.lock": "privatu",
+  "compose_form.placeholder": "À chè pensate?",
+  "compose_form.publish": "Toot",
+  "compose_form.publish_loud": "{publish}!",
+  "compose_form.sensitive.marked": "Media indicatu cum'è sensibile",
+  "compose_form.sensitive.unmarked": "Media micca indicatu cum'è sensibile",
+  "compose_form.spoiler.marked": "Testu piattatu daret'à un'avertimentu",
+  "compose_form.spoiler.unmarked": "Testu micca piattatu",
+  "compose_form.spoiler_placeholder": "Scrive u vostr'avertimentu quì",
+  "confirmation_modal.cancel": "Annullà",
+  "confirmations.block.confirm": "Bluccà",
+  "confirmations.block.message": "Site sicuru·a che vulete bluccà @{name}?",
+  "confirmations.delete.confirm": "Toglie",
+  "confirmations.delete.message": "Site sicuru·a che vulete supprime stu statutu?",
+  "confirmations.delete_list.confirm": "Toglie",
+  "confirmations.delete_list.message": "Site sicuru·a che vulete supprime sta lista?",
+  "confirmations.domain_block.confirm": "Piattà tuttu u duminiu?",
+  "confirmations.domain_block.message": "Site sicuru·a che vulete piattà tuttu à {domain}? Saria forse abbastanza di bluccà ò piattà alcuni conti da quallà.",
+  "confirmations.mute.confirm": "Piattà",
+  "confirmations.mute.message": "Site sicuru·a che vulete piattà @{name}?",
+  "confirmations.unfollow.confirm": "Disabbunassi",
+  "confirmations.unfollow.message": "Site sicuru·a ch'ùn vulete più siguità @{name}?",
+  "embed.instructions": "Integrà stu statutu à u vostru situ cù u codice quì sottu.",
+  "embed.preview": "Assumiglierà à qualcosa cusì:",
+  "emoji_button.activity": "Attività",
+  "emoji_button.custom": "Persunalizati",
+  "emoji_button.flags": "Bandere",
+  "emoji_button.food": "Manghjusca è Bienda",
+  "emoji_button.label": "Mette un'emoji",
+  "emoji_button.nature": "Natura",
+  "emoji_button.not_found": "Ùn c'hè nunda! (╯°□°)╯︵ ┻━┻",
+  "emoji_button.objects": "Oggetti",
+  "emoji_button.people": "Parsunaghji",
+  "emoji_button.recent": "Assai utilizati",
+  "emoji_button.search": "Cercà...",
+  "emoji_button.search_results": "Risultati di a cerca",
+  "emoji_button.symbols": "Simbuli",
+  "emoji_button.travel": "Lochi è Viaghju",
+  "empty_column.community": "Ùn c'hè nunda indè a linea lucale. Scrivete puru qualcosa!",
+  "empty_column.direct": "Ùn avete ancu nisun missaghju direttu. S'è voi mandate o ricevete unu, u vidarete quì.",
+  "empty_column.hashtag": "Ùn c'hè ancu nunda quì.",
+  "empty_column.home": "A vostr'accolta hè viota! Pudete andà nant'à {public} o pruvà a ricerca per truvà parsone da siguità.",
+  "empty_column.home.public_timeline": "a linea pubblica",
+  "empty_column.list": "Ùn c'hè ancu nunda quì. Quandu membri di sta lista manderanu novi statuti, i vidarete quì.",
+  "empty_column.notifications": "Ùn avete ancu nisuna nutificazione. Interact with others to start the conversation.",
+  "empty_column.public": "Ùn c'hè nunda quì! Scrivete qualcosa in pubblicu o seguitate utilizatori d'altre istanze per empie a linea pubblica.",
+  "follow_request.authorize": "Auturizà",
+  "follow_request.reject": "Righjittà",
+  "getting_started.appsshort": "Applicazione",
+  "getting_started.faq": "FAQ",
+  "getting_started.heading": "Per principià",
+  "getting_started.open_source_notice": "Mastodon ghjè un lugiziale liberu. Pudete cuntribuisce à u codice o a traduzione, o palisà un bug, nant'à GitHub: {github}",
+  "getting_started.userguide": "Guida d'utilizazione",
+  "home.column_settings.advanced": "Avanzati",
+  "home.column_settings.basic": "Bàsichi",
+  "home.column_settings.filter_regex": "Filtrà cù spressione regulare (regex)",
+  "home.column_settings.show_reblogs": "Vede e spartere",
+  "home.column_settings.show_replies": "Vede e risposte",
+  "home.settings": "Parametri di a colonna",
+  "keyboard_shortcuts.back": "rivultà",
+  "keyboard_shortcuts.boost": "sparte",
+  "keyboard_shortcuts.column": "fucalizà un statutu indè una colonna",
+  "keyboard_shortcuts.compose": "fucalizà nant'à l'area di ridazzione",
+  "keyboard_shortcuts.description": "Descrizzione",
+  "keyboard_shortcuts.down": "falà indè a lista",
+  "keyboard_shortcuts.enter": "apre u statutu",
+  "keyboard_shortcuts.favourite": "aghjunghje à i favuriti",
+  "keyboard_shortcuts.heading": "Accorte cù a tastera",
+  "keyboard_shortcuts.hotkey": "Accorta",
+  "keyboard_shortcuts.legend": "vede a legenda",
+  "keyboard_shortcuts.mention": "mintuvà l'autore",
+  "keyboard_shortcuts.reply": "risponde",
+  "keyboard_shortcuts.search": "fucalizà nant'à l'area di circata",
+  "keyboard_shortcuts.toggle_hidden": "vede/piattà u testu daretu à l'avertimentu CW",
+  "keyboard_shortcuts.toot": "scrive un novu statutu",
+  "keyboard_shortcuts.unfocus": "ùn fucalizà più l'area di testu",
+  "keyboard_shortcuts.up": "cullà indè a lista",
+  "lightbox.close": "Chjudà",
+  "lightbox.next": "Siguente",
+  "lightbox.previous": "Pricidente",
+  "lists.account.add": "Aghjunghje à a lista",
+  "lists.account.remove": "Toglie di a lista",
+  "lists.delete": "Supprime a lista",
+  "lists.edit": "Mudificà a lista",
+  "lists.new.create": "Aghjustà una lista",
+  "lists.new.title_placeholder": "Titulu di a lista",
+  "lists.search": "Circà indè i vostr'abbunamenti",
+  "lists.subheading": "E vo liste",
+  "loading_indicator.label": "Caricamentu...",
+  "media_gallery.toggle_visible": "Cambià a visibilità",
+  "missing_indicator.label": "Micca trovu",
+  "missing_indicator.sublabel": "Ùn era micca pussivule di truvà sta risorsa",
+  "mute_modal.hide_notifications": "Piattà nutificazione da st'utilizatore?",
+  "navigation_bar.blocks": "Utilizatori bluccati",
+  "navigation_bar.community_timeline": "Linea pubblica lucale",
+  "navigation_bar.direct": "Missaghji diretti",
+  "navigation_bar.domain_blocks": "Duminii piattati",
+  "navigation_bar.edit_profile": "Mudificà u prufile",
+  "navigation_bar.favourites": "Favuriti",
+  "navigation_bar.follow_requests": "Dumande d'abbunamentu",
+  "navigation_bar.info": "À prupositu di l'istanza",
+  "navigation_bar.keyboard_shortcuts": "Accorte cù a tastera",
+  "navigation_bar.lists": "Liste",
+  "navigation_bar.logout": "Scunnettassi",
+  "navigation_bar.mutes": "Utilizatori piattati",
+  "navigation_bar.pins": "Statuti puntarulati",
+  "navigation_bar.preferences": "Preferenze",
+  "navigation_bar.public_timeline": "Linea pubblica glubale",
+  "notification.favourite": "{name} hà aghjuntu u vostru statutu à i so favuriti",
+  "notification.follow": "{name} v'hà seguitatu",
+  "notification.mention": "{name} v'hà mintuvatu",
+  "notification.reblog": "{name} hà spartutu u vostru statutu",
+  "notifications.clear": "Purgà e nutificazione",
+  "notifications.clear_confirmation": "Site sicuru·a che vulete toglie tutte ste nutificazione?",
+  "notifications.column_settings.alert": "Nutificazione nant'à l'urdinatore",
+  "notifications.column_settings.favourite": "Favuriti:",
+  "notifications.column_settings.follow": "Abbunati novi:",
+  "notifications.column_settings.mention": "Minzione:",
+  "notifications.column_settings.push": "Nutificazione Push",
+  "notifications.column_settings.push_meta": "Quess'apparechju",
+  "notifications.column_settings.reblog": "Spartere:",
+  "notifications.column_settings.show": "Mustrà indè a colonna",
+  "notifications.column_settings.sound": "Sunà",
+  "onboarding.done": "Fatta",
+  "onboarding.next": "Siguente",
+  "onboarding.page_five.public_timelines": "A linea pubblica lucale mostra statuti pubblichi da tuttu u mondu nant'à {domain}. A linea pubblica glubale mostra ancu quelli di a ghjente seguitata da l'utilizatori di {domain}. Quesse sò una bona manera d'incuntrà nove parsone.",
+  "onboarding.page_four.home": "A linea d'accolta mostra i statuti di i vostr'abbunamenti.",
+  "onboarding.page_four.notifications": "A colonna di nutificazione mostra l'interazzione ch'altre parsone anu cù u vostru contu.",
+  "onboarding.page_one.federation": "Mastodon ghjè una rete di servori independenti, chjamati istanze, uniti indè una sola rete suciale.",
+  "onboarding.page_one.full_handle": "U vostru identificatore cumplettu",
+  "onboarding.page_one.handle_hint": "Quessu ghjè cio chì direte à i vostri amichi per circavi.",
+  "onboarding.page_one.welcome": "Benvenuti/a nant'à Mastodon!",
+  "onboarding.page_six.admin": "L'amministratore di a vostr'istanza hè {admin}.",
+  "onboarding.page_six.almost_done": "Quasgi finitu...",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "Ci sò {apps} dispunibule per iOS, Android è altre piattaforme.",
+  "onboarding.page_six.github": "Mastodon ghjè un lugiziale liberu. Pudete cuntribuisce à u codice o a traduzione, o palisà un prublemu, nant'à {github}.",
+  "onboarding.page_six.guidelines": "regule di a cumunità",
+  "onboarding.page_six.read_guidelines": "Ùn vi scurdate di leghje e {guidelines} di {domain}",
+  "onboarding.page_six.various_app": "applicazione pè u telefuninu",
+  "onboarding.page_three.profile": "Pudete mudificà u prufile per cambia u ritrattu, a descrizzione è u nome affissatu. Ci sò ancu alcun'altre preferenze.",
+  "onboarding.page_three.search": "Fate usu di l'area di ricerca per truvà altre persone è vede hashtag cum'è {illustration} o {introductions}. Per vede qualcunu ch'ùn hè micca nant'à st'istanza, cercate u so identificatore complettu (pare un'email).",
+  "onboarding.page_two.compose": "I statuti è missaghji si scrivenu indè l'area di ridazzione. Pudete caricà imagine, cambià i parametri di pubblicazione, è mette avertimenti di cuntenuti cù i buttoni quì sottu.",
+  "onboarding.skip": "Passà",
+  "privacy.change": "Mudificà a cunfidenzialità di u statutu",
+  "privacy.direct.long": "Mandà solu à quelli chì so mintuvati",
+  "privacy.direct.short": "Direttu",
+  "privacy.private.long": "Mustrà solu à l'abbunati",
+  "privacy.private.short": "Privatu",
+  "privacy.public.long": "Mustrà à tuttu u mondu nant'a linea pubblica",
+  "privacy.public.short": "Pubblicu",
+  "privacy.unlisted.long": "Ùn mette micca nant'a linea pubblica (ma tutt'u mondu pò vede u statutu nant'à u vostru prufile)",
+  "privacy.unlisted.short": "Micca listatu",
+  "regeneration_indicator.label": "Caricamentu…",
+  "regeneration_indicator.sublabel": "Priparazione di a vostra pagina d'accolta",
+  "relative_time.days": "{number}d",
+  "relative_time.hours": "{number}h",
+  "relative_time.just_now": "avà",
+  "relative_time.minutes": "{number}m",
+  "relative_time.seconds": "{number}s",
+  "reply_indicator.cancel": "Annullà",
+  "report.forward": "Trasferisce à {target}",
+  "report.forward_hint": "U contu hè nant'à un'altru servore. Vulete ancu mandà una copia anonima di u signalamentu quallà?",
+  "report.hint": "U signalamentu sarà mandatu à i muderatori di l'istanza. Pudete spiegà perchè avete palisatu stu contu quì sottu:",
+  "report.placeholder": "Altri cummenti",
+  "report.submit": "Mandà",
+  "report.target": "Signalamentu",
+  "search.placeholder": "Circà",
+  "search_popout.search_format": "Ricerca avanzata",
+  "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
+  "search_popout.tips.hashtag": "hashtag",
+  "search_popout.tips.status": "statutu",
+  "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
+  "search_popout.tips.user": "utilizatore",
+  "search_results.accounts": "Ghjente",
+  "search_results.hashtags": "Hashtags",
+  "search_results.statuses": "Statuti",
+  "search_results.total": "{count, number} {count, plural, one {risultatu} other {risultati}}",
+  "standalone.public_title": "Una vista di...",
+  "status.block": "Bluccà @{name}",
+  "status.cancel_reblog_private": "Ùn sparte più",
+  "status.cannot_reblog": "Stu statutu ùn pò micca esse spartutu",
+  "status.delete": "Toglie",
+  "status.direct": "Mandà un missaghju @{name}",
+  "status.embed": "Integrà",
+  "status.favourite": "Aghjunghje à i favuriti",
+  "status.load_more": "Vede di più",
+  "status.media_hidden": "Media piattata",
+  "status.mention": "Mintuvà @{name}",
+  "status.more": "Più",
+  "status.mute": "Piattà @{name}",
+  "status.mute_conversation": "Piattà a cunversazione",
+  "status.open": "Apre stu statutu",
+  "status.pin": "Puntarulà à u prufile",
+  "status.pinned": "Statutu puntarulatu",
+  "status.reblog": "Sparte",
+  "status.reblog_private": "Sparte à l'audienza uriginale",
+  "status.reblogged_by": "{name} hà spartutu",
+  "status.reply": "Risponde",
+  "status.replyAll": "Risponde à tutti",
+  "status.report": "Palisà @{name}",
+  "status.sensitive_toggle": "Cliccate per vede",
+  "status.sensitive_warning": "Cuntinutu sensibile",
+  "status.share": "Sparte",
+  "status.show_less": "Ripiegà",
+  "status.show_less_all": "Ripiegà tuttu",
+  "status.show_more": "Slibrà",
+  "status.show_more_all": "Slibrà tuttu",
+  "status.unmute_conversation": "Ùn piattà più a cunversazione",
+  "status.unpin": "Spuntarulà da u prufile",
+  "tabs_bar.federated_timeline": "Glubale",
+  "tabs_bar.home": "Accolta",
+  "tabs_bar.local_timeline": "Lucale",
+  "tabs_bar.notifications": "Nutificazione",
+  "tabs_bar.search": "Cercà",
+  "ui.beforeunload": "A bruttacopia sarà persa s'ellu hè chjosu Mastodon.",
+  "upload_area.title": "Drag & drop per caricà un fugliale",
+  "upload_button.label": "Aghjunghje un media",
+  "upload_form.description": "Discrive per i malvistosi",
+  "upload_form.focus": "Riquatrà",
+  "upload_form.undo": "Annullà",
+  "upload_progress.label": "Caricamentu...",
+  "video.close": "Chjudà a video",
+  "video.exit_fullscreen": "Caccià u pienu screnu",
+  "video.expand": "Ingrandà a video",
+  "video.fullscreen": "Pienu screnu",
+  "video.hide": "Piattà a video",
+  "video.mute": "Surdina",
+  "video.pause": "Pausa",
+  "video.play": "Lettura",
+  "video.unmute": "Caccià a surdina"
+}
diff --git a/app/javascript/mastodon/locales/whitelist_co.json b/app/javascript/mastodon/locales/whitelist_co.json
new file mode 100644
index 0000000000..0d4f101c7a
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_co.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/config/application.rb b/config/application.rb
index 25c926444e..b4871241a3 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -40,6 +40,7 @@ module Mastodon
       :ar,
       :bg,
       :ca,
+      :co,
       :de,
       :el,
       :eo,
diff --git a/config/locales/activerecord.co.yml b/config/locales/activerecord.co.yml
new file mode 100644
index 0000000000..af28d108fd
--- /dev/null
+++ b/config/locales/activerecord.co.yml
@@ -0,0 +1,13 @@
+---
+co:
+  activerecord:
+    errors:
+      models:
+        account:
+          attributes:
+            username:
+              invalid: solu lettere, numeri è liniette basse
+        status:
+          attributes:
+            reblog:
+              taken: di u statutu esista digià
diff --git a/config/locales/co.yml b/config/locales/co.yml
new file mode 100644
index 0000000000..abef1d36bc
--- /dev/null
+++ b/config/locales/co.yml
@@ -0,0 +1,831 @@
+---
+co:
+  about:
+    about_hashtag_html: Quessi sò statuti pubblichi taggati cù <strong>#%{hashtag}</strong>. Pudete interagisce cù elli sì voi avete un contu in qualche parte di u <em>fediverse</em>.
+    about_mastodon_html: Mastodon ghjè una rete suciale custruita incù prutucolli web aperti è lugiziali liberi. Hè decentralizatu cumu l’e-mail.
+    about_this: À prupositu
+    administered_by: 'Amministratu da:'
+    closed_registrations: Pè avà, l’arregistramenti sò chjosi nant’à st’istanza. Mà pudete truvà un’altr’istanza per fà un contu è avè accessu à listessa reta da quallà!
+    contact: Cuntattu
+    contact_missing: Mancante
+    contact_unavailable: Micca dispunibule
+    description_headline: Quale hè %{domain} ?
+    domain_count_after: altre istanze
+    domain_count_before: Cunnettati à
+    extended_description_html: |
+      <h3>Una bona piazza per e regule</h3>
+      <p>A descrizzione stesa ùn hè micca stata riempiuta.</p>
+    features:
+      humane_approach_body: Mastodon hà amparatu da i sbagli di l’altre rete suciale, è prova à fà scelte di cuncezzione più etiche per luttà contr’à l’abusu di i media suciali.
+      humane_approach_title: Una mentalità più umana
+      not_a_product_body: Mastodon ùn hè micca una rete cummerciale. Micca pubblicità, micca pruspizzione di dati, micca ambienti chjosi, è micca auturità centrale.
+      not_a_product_title: Site una parsona, micca un pruduttu
+      real_conversation_body: Cù 500 caratteri dispunibuli, diffusione persunalizata di u cuntinutu è avertimenti per media sensibili, pudete cumunicà cum’è voi vulete.
+      real_conversation_title: Fattu per una vera cunversazione
+      within_reach_body: Parechje app per iOS, Android è altre piattaforme, create cù un sistemu d’API accessibile à i prugrammatori, vi permettenu d’avè accessu à i vostri amichi senza prublemi.
+      within_reach_title: Sempre accessibile
+    generic_description: "%{domain} hè un servore di a rete"
+    hosted_on: Mastodon allughjatu nant’à %{domain}
+    learn_more: Amparà di più
+    other_instances: Lista di l’istanze
+    source_code: Codice di fonte
+    status_count_after: statuti
+    status_count_before: chì anu pubblicatu
+    user_count_after: parsone quì
+    user_count_before: Ci sò
+    what_is_mastodon: Quale hè Mastodon?
+  accounts:
+    follow: Siguità
+    followers: Abbunati
+    following: Abbunamenti
+    media: Media
+    moved_html: "%{name} hà cambiatu di contu, avà hè nant’à %{new_profile_link}:"
+    nothing_here: Ùn c’hè nunda quì!
+    people_followed_by: Seguitati da %{name}
+    people_who_follow: Seguitanu %{name}
+    posts: Statuti
+    posts_with_replies: Statuti è risposte
+    remote_follow: Siguità d’altrò
+    reserved_username: Stu cugnome hè riservatu
+    roles:
+      admin: Amministratore
+      moderator: Muderatore
+    unfollow: Ùn siguità più
+  admin:
+    account_moderation_notes:
+      create: Creà
+      created_msg: Nota di muderazione creata!
+      delete: Toglie
+      destroyed_msg: Nota di muderazione sguassata!
+    accounts:
+      are_you_sure: Site sicuru·a?
+      avatar: Ritrattu di prufile
+      by_domain: Duminiu
+      change_email:
+        changed_msg: Email di u contu cambiatu!
+        current_email: Email attuale
+        label: Mudificà l’Email
+        new_email: Novu Email
+        submit: Cambià Email
+        title: Mudificà l’Email di %{username}
+      confirm: Cunfirmà
+      confirmed: Cunfirmata
+      demote: Ritrugradà
+      disable: Disattivà
+      disable_two_factor_authentication: Disattivà l’identificazione à 2 fattori
+      disabled: Disattivatu
+      display_name: Nome pubblicu
+      domain: Duminiu
+      edit: Mudificà
+      email: E-mail
+      enable: Attivà
+      enabled: Attivatu
+      feed_url: URL di u flussu
+      followers: Abbunati
+      followers_url: URL di l’abbunati
+      follows: Abbunamenti
+      inbox_url: URL di l’inbox
+      ip: IP
+      location:
+        all: Tutti
+        local: Lucale
+        remote: D’altrò
+        title: Lucalizazione
+      login_status: Statutu di cunnessione
+      media_attachments: Media aghjunti
+      memorialize: Trasfurmà in mimuriale
+      moderation:
+        all: Tutti
+        silenced: Silenzati
+        suspended: Suspesi
+        title: Muderazione
+      moderation_notes: Note di muderazione
+      most_recent_activity: Attività più ricente
+      most_recent_ip: IP più ricente
+      not_subscribed: Micca abbunatu
+      order:
+        alphabetic: Alfabeticu
+        most_recent: Più ricente
+        title: Urdine
+      outbox_url: URL di l’outbox
+      perform_full_suspension: Fà una suspensione cumpleta
+      profile_url: URL di u prufile
+      promote: Prumove
+      protocol: Prutucollu
+      public: Pubblicu
+      push_subscription_expires: Spirata di l’abbunamentu PuSH
+      redownload: Mette à ghjornu i ritratti
+      remove_avatar: Toglie l’avatar
+      reset: Reset
+      reset_password: Riinizializà a chjave d’accessu
+      resubscribe: Riabbunassi
+      role: Auturizazione
+      roles:
+        admin: Amministratore
+        moderator: Muderatore
+        staff: Squadra
+        user: Utilizatore
+      salmon_url: URL di Salmon
+      search: Cercà
+      shared_inbox_url: URL di l’inbox spartuta
+      show:
+        created_reports: Signalamenti creati da stu contu
+        report: Signalamentu
+        targeted_reports: Signalamenti creati contr’à stu contu
+      silence: Silenzà
+      statuses: Statuti
+      subscribe: Abbunassi
+      title: Conti
+      unconfirmed_email: E-mail micca cunfirmatu
+      undo_silenced: Ùn silenzà più
+      undo_suspension: Ùn suspende più
+      unsubscribe: Disabbunassi
+      username: Cugnome
+      web: Web
+    action_logs:
+      actions:
+        assigned_to_self_report: "%{name} s’hè assignatu u signalamentu %{target}"
+        change_email_user: "%{name} hà cambiatu l’indirizzu e-mail di %{target}"
+        confirm_user: "%{name} hà cunfirmatu l’indirizzu e-mail di %{target}"
+        create_custom_emoji: "%{name} hà caricatu una nov’emoji %{target}"
+        create_domain_block: "%{name} hà bluccatu u duminiu %{target}"
+        create_email_domain_block: "%{name} hà messu u duminiu e-mail %{target} nant’a lista nera"
+        demote_user: "%{name} hà ritrugradatu l’utilizatore %{target}"
+        destroy_domain_block: "%{name} hà sbluccatu u duminiu %{target}"
+        destroy_email_domain_block: "%{name} hà messu u duminiu e-mail %{target} nant’a lista bianca"
+        destroy_status: "%{name} hà toltu u statutu di %{target}"
+        disable_2fa_user: "%{name} hà disattivatu l’identificazione à dui fattori per %{target}"
+        disable_custom_emoji: "%{name} hà disattivatu l’emoji %{target}"
+        disable_user: "%{name} hà disattivatu a cunnessione per %{target}"
+        enable_custom_emoji: "%{name} hà attivatu l’emoji %{target}"
+        enable_user: "%{name} hà attivatu a cunnessione per %{target}"
+        memorialize_account: "%{name} hà trasfurmatu u contu di %{target} in una pagina mimuriale"
+        promote_user: "%{name} hà prumossu %{target}"
+        remove_avatar_user: "%{name} hà toltu u ritrattu di %{target}"
+        reopen_report: "%{name} hà riapertu u signalamentu %{target}"
+        reset_password_user: "%{name} hà riinizializatu a chjave d’accessu di %{target}"
+        resolve_report: "%{name} hà chjosu u signalamentu %{target}"
+        silence_account: "%{name} hà silenzatu u contu di %{target}"
+        suspend_account: "%{name} hà suspesu u contu di %{target}"
+        unassigned_report: "%{name} hà disassignatu u signalamentu %{target}"
+        unsilence_account: "%{name} hà fattu che u contu di %{target} ùn hè più silenzatu"
+        unsuspend_account: "%{name} hà fattu che u contu di %{target} ùn hè più suspesu"
+        update_custom_emoji: "%{name} hà messu à ghjornu l’emoji %{target}"
+        update_status: "%{name} hà cambiatu u statutu di %{target}"
+      title: Ghjurnale d’audit
+    custom_emojis:
+      by_domain: Duminiu
+      copied_msg: Copia lucale di l’emoji creata!
+      copy: Cupià
+      copy_failed_msg: Ùn s’hè micca pussutu creà una copia di l’emoji
+      created_msg: L’emoji hè stata creata!
+      delete: Toglie
+      destroyed_msg: L’emoji hè stata tolta!
+      disable: Disattivà
+      disabled_msg: L’emoji hè stata disattivata!
+      emoji: Emoji
+      enable: Attivà
+      enabled_msg: L’emoji hè stata attivata!
+      image_hint: PNG di 50Ko o menu
+      listed: Listata
+      new:
+        title: Aghjustà una nov’emoji
+      overwrite: Soprascrive
+      shortcode: Accorta
+      shortcode_hint: 2 caratteri o più, solu lettere, numeri è liniette basse
+      title: Emoji parsunalizate
+      unlisted: Micca listata
+      update_failed_msg: Ùn s’hè micca pussutu mette à ghjornu l’emoji
+      updated_msg: L’emoji hè stata messa à ghjornu!
+      upload: Caricà
+    domain_blocks:
+      add_new: Aghjustà
+      created_msg: U blucchime di u duminiu hè attivu
+      destroyed_msg: U blucchime di u duminiu ùn hè più attivu
+      domain: Duminiu
+      new:
+        create: Creà un blucchime
+        hint: U blucchime di duminiu ùn impedirà micca a creazione di conti indè a database, mà metudi di muderazione specifiche saranu applicati.
+        severity:
+          desc_html: Cù<strong>Silenzà</strong>, solu l’abbunati di u contu viderenu i so missaghji. <strong>Suspende</strong> sguassarà tutti i cuntenuti è dati di u contu. Utilizate <strong>Nisuna</strong> s’è voi vulete solu righjittà fugliali media.
+          noop: Nisuna
+          silence: Silenzà
+          suspend: Suspende
+        title: Novu blucchime di duminiu
+      reject_media: Righjittà i fugliali media
+      reject_media_hint: Sguassa tutti i media caricati è ricusa caricamenti futuri. Inutile per una suspensione
+      severities:
+        noop: Nisuna
+        silence: Silenzà
+        suspend: Suspende
+      severity: Severità
+      show:
+        affected_accounts:
+          one: Un contu tuccatu indè a database
+          other: "%{count} conti tuccati indè a database"
+        retroactive:
+          silence: Ùn silenzà più i conti nant’à stu duminiu
+          suspend: Ùn suspende più i conti nant’à stu duminiu
+        title: Ùn bluccà più u duminiu %{domain}
+        undo: Annullà
+      title: Blucchimi di duminiu
+      undo: Annullà
+    email_domain_blocks:
+      add_new: Aghjustà
+      created_msg: U blucchime di u duminiu d’e-mail hè attivu
+      delete: Toglie
+      destroyed_msg: U blucchime di u duminiu d’e-mail ùn hè più attivu
+      domain: Duminiu
+      new:
+        create: Creà un blucchime
+        title: Nova iscrizzione nant’a lista nera e-mail
+      title: Lista nera e-mail
+    instances:
+      account_count: Conti cunnisciuti
+      domain_name: Duminiu
+      reset: Riinizializà
+      search: Cercà
+      title: Istanze cunnisciute
+    invites:
+      filter:
+        all: Tuttu
+        available: Dispunibuli
+        expired: Spirati
+        title: Filtrà
+      title: Invitazione
+    report_notes:
+      created_msg: Nota di signalamentu creata!
+      destroyed_msg: Nota di signalamentu sguassata!
+    reports:
+      account:
+        note: nota
+        report: palisà
+      action_taken_by: Intervenzione di
+      are_you_sure: Site sicuru·a?
+      assign_to_self: Assignallu à mè
+      assigned: Muderatore assignatu
+      comment:
+        none: Nisunu
+      created_at: Palisatu
+      id: ID
+      mark_as_resolved: Indicà cum’è chjosu
+      mark_as_unresolved: Indicà cum’è sempre apertu
+      notes:
+        create: Aghjunghje una nota
+        create_and_resolve: Chjude cù una nota
+        create_and_unresolve: Riapre cù una nota
+        delete: Toglie
+        placeholder: Per parlà di l’azzione piglate, o altre messe à ghjornu nant’à u signalamentu…
+      reopen: Riapre u signalamentu
+      report: 'Signalamente #%{id}'
+      report_contents: Cuntenuti
+      reported_account: Contu palisatu
+      reported_by: Palisatu da
+      resolved: Scioltu è chjosu
+      resolved_msg: Signalamentu scioltu!
+      silence_account: Silenzà u contu
+      status: Statutu
+      suspend_account: Suspende u contu
+      target: Oggettu
+      title: Signalamenti
+      unassign: Disassignà
+      unresolved: Micca sciolti
+      updated_at: Messi à ghjornu
+      view: Vede
+    settings:
+      activity_api_enabled:
+        desc_html: Numeri di statuti creati quì, utilizatori attivi, è arregistramenti novi tutte e settimane
+        title: Pubblicà statistiche nant’à l’attività di l’utilizatori
+      bootstrap_timeline_accounts:
+        desc_html: Cugnomi separati cù virgule. Solu pussibule cù conti lucali è pubblichi. Quandu a lista hè viota, tutti l’amministratori lucali saranu selezziunati.
+        title: Abbunamenti predefiniti per l’utilizatori novi
+      contact_information:
+        email: E-mail prufissiunale
+        username: Identificatore di cuntattu
+      hero:
+        desc_html: Affissatu nant’a pagina d’accolta. Ricumandemu almenu 600x100px. S’ellu ùn hè micca definiti, a vignetta di l’istanza sarà usata
+        title: Ritrattu di cuprendula
+      peers_api_enabled:
+        desc_html: Indirizzi st’istanza hà vistu indè u fediverse
+        title: Pubblicà a lista d’istanza cunnisciute
+      registrations:
+        closed_message:
+          desc_html: Affissatu nant’a pagina d’accolta quandu l’arregistramenti sò chjosi. Pudete fà usu di u furmattu HTML
+          title: Missaghju per l’arregistramenti chjosi
+        deletion:
+          desc_html: Auturizà tuttu u mondu di sguassà u so propiu contu
+          title: Auturizà à sguassà i conti
+        min_invite_role:
+          disabled: Nisunu
+          title: Auturizà l’invitazione da
+        open:
+          desc_html: Auturizà tuttu u mondu à creà un contu quì
+          title: Apre l’arregistramenti
+      show_known_fediverse_at_about_page:
+        desc_html: Quandu ghjè selezziunatu, statuti di tuttu l’istanze cunnisciute saranu affissati indè a vista di e linee. Altrimente soli i statuti lucali saranu mustrati.
+        title: Vedde tuttu u fediverse cunnisciutu nant’a vista di e linee
+      show_staff_badge:
+        desc_html: Mustrerà un badge Squadra nant’à un prufile d’utilizatore
+        title: Mustrà un badge staff
+      site_description:
+        desc_html: Paragrafu di prisentazione nant’a pagina d’accolta è i marchi meta. Pudete fà usu di marchi HTML, in particulare <code>&lt;a&gt;</code> è <code>&lt;em&gt;</code>.
+        title: Discrizzione di l’istanza
+      site_description_extended:
+        desc_html: Una bona piazza per e regule, infurmazione è altre cose chì l’utilizatori duverìanu sapè. Pudete fà usu di marchi HTML
+        title: Discrizzione stesa di u situ
+      site_terms:
+        desc_html: Quì pudete scrive e vostre regule di cunfidenzialità, cundizione d’usu o altre menzione legale. Pudete fà usu di marchi HTML
+        title: Termini persunalizati
+      site_title: Nome di l’istanza
+      thumbnail:
+        desc_html: Utilizatu per viste cù OpenGraph è l’API. Ricumandemu 1200x630px
+        title: Vignetta di l’istanza
+      timeline_preview:
+        desc_html: Vede a linea pubblica nant’a pagina d’accolta
+        title: Vista di e linee
+      title: Parametri di u situ
+    statuses:
+      back_to_account: Ritornu à a pagina di u contu
+      batch:
+        delete: Toglie
+        nsfw_off: Indicà cum’è micca sensibile
+        nsfw_on: Indicà cum’è sensibile
+      failed_to_execute: Esecuzione impussibule
+      media:
+        title: Media
+      no_media: Nisun media
+      title: Statutu di u contu
+      with_media: Cù media
+    subscriptions:
+      callback_url: URL di richjama
+      confirmed: Cunfirmatu
+      expires_in: Spira in
+      last_delivery: Ultima arricata
+      title: WebSub
+      topic: Sughjettu
+    title: Amministrazione
+  admin_mailer:
+    new_report:
+      body: "%{reporter} hà palisatu %{target}"
+      body_remote: Qualch’unu da %{domain} hà palisatu %{target}
+      subject: Novu signalamentu nant’à %{instance} (#%{id})
+  application_mailer:
+    notification_preferences: Cambià e priferenze e-mail
+    salutation: "%{name},"
+    settings: 'Cambià e priferenze e-mail: %{link}'
+    view: 'Vede:'
+    view_profile: Vede u prufile
+    view_status: Vede u statutu
+  applications:
+    created: Applicazione creata
+    destroyed: Applicazione sguassata
+    invalid_url: L’URL ch’è stata pruvista ùn hè valida
+    regenerate_token: Regenerate access token
+    token_regenerated: Access token successfully regenerated
+    warning: Be very careful with this data. Never share it with anyone!
+    your_token: Rigenerà a fiscia d’accessu
+  auth:
+    agreement_html: Arregistrassi vole dì chì site d’accunsentu per siguità <a href="%{rules_path}">e regule di l’istanza</a> è <a href="%{terms_path}">e cundizione d’usu</a>.
+    change_password: Chjave d’accessu
+    confirm_email: Cunfirmà l’e-mail
+    delete_account: Sguassà u contu
+    delete_account_html: S’è voi vulete toglie u vostru contu <a href="%{path}">ghjè quì</a>. Duverete cunfirmà a vostra scelta.
+    didnt_get_confirmation: Ùn avete micca ricevutu l’istruzione di cunfirmazione?
+    forgot_password: Chjave scurdata?
+    invalid_reset_password_token: Password reset token is invalid or expired. Please request a new one.
+    login: Cunnettassi
+    logout: Scunnettassi
+    migrate_account: Cambià di contu
+    migrate_account_html: S’è voi vulete riindirizà stu contu versu un’altru, <a href="%{path}">ghjè pussibule quì</a>.
+    or: o
+    or_log_in_with: O cunnettatevi cù
+    providers:
+      cas: CAS
+      saml: SAML
+    register: Arregistrassi
+    register_elsewhere: Arregistrassi altrò
+    resend_confirmation: Rimandà l’istruzzioni di cunfirmazione
+    reset_password: Cambià a chjave d’accessu
+    security: Sicurità
+    set_new_password: Creà una nova chjave d’accessu
+  authorize_follow:
+    already_following: You are already following this account
+    error: Peccatu, c’hè statu un prublemu ricercandu u contu.
+    follow: Siguità
+    follow_request: 'Avete dumandatu di siguità:'
+    following: 'Eccu! Avà seguitate:'
+    post_follow:
+      close: Or, you can just close this window.
+      return: Rivultà à u prufile di l’utilizatore
+      web: Andà à l’interfaccia web
+    title: Siguità %{acct}
+  datetime:
+    distance_in_words:
+      about_x_hours: "%{count}h"
+      about_x_months: "%{count}mo"
+      about_x_years: "%{count}y"
+      almost_x_years: "%{count}y"
+      half_a_minute: Avà
+      less_than_x_minutes: "%{count}m"
+      less_than_x_seconds: Avà
+      over_x_years: "%{count}y"
+      x_days: "%{count}d"
+      x_minutes: "%{count}m"
+      x_months: "%{count}mo"
+      x_seconds: "%{count}s"
+  deletes:
+    bad_password_msg: È nò! Sta chjave ùn hè curretta
+    confirm_password: Entrate a vostra chjave d’accessu attuale per verificà a vostra identità
+    description_html: U contu sarà deattivatu è u cuntenutu sarà sguassatu di manera <strong>permanente è irreversibile</strong>. Ùn sarà micca pussibule piglià stu cugnome torna per evità l’impusture.
+    proceed: Sguassà u contu
+    success_msg: U vostru contu hè statu sguassatu
+    warning_html: Pudete esse sicuru·a solu chì u cuntenutu sarà sguassatu di st’istanza. S’ellu hè statu spartutu in altrò, sarà forse sempre quallà.
+    warning_title: Dispunibilità di i cuntenuti sparsi
+  errors:
+    '403': Ùn site micca auturizatu·a à vede sta pagina.
+    '404': Sta pagina ùn esiste micca.
+    '410': Sta pagina ùn esiste più.
+    '422':
+      content: C’hè statu un prublemu cù a verificazione di sicurità. Forse bluccate cookies?
+      title: Fiascu di verificazione
+    '429': Limitatu dop’à troppu richieste
+    '500':
+      content: Scusate, mà c’hè statu un prublemu cù u nostru servore.
+      title: Sta pagina ùn hè curretta
+    noscript_html: Mastodon nant’à u web hà bisognu di JavaScript per funziunà. Pudete ancu pruvà <a href="https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md">l’applicazione native</a> per a vostra piattaforma.
+  exports:
+    archive_takeout:
+      date: Data
+      download: Scaricà l’archiviu
+      hint_html: Pudete dumandà un’archiviu di i vostri <strong>statuti è media caricati</strong>. I dati saranu in u furmattu ActivityPub è pudarenu esse letti da tutti i lugiziali chì u supportanu.
+      in_progress: Cumpilazione di l’archiviu...
+      request: Dumandà u vostr’archiviu
+      size: Pesu
+    blocks: Bluccate
+    csv: CSV
+    follows: Seguitate
+    mutes: Piattate
+    storage: I vostri media
+  followers:
+    domain: Duminiu
+    explanation_html: Per assicuravi di a cunfidenzialità di i vostri statuti, duvete avè primura di quale vi seguita. <strong>I vostri statuti privati sò mandati à tutte l’istanze induve avete abbunati</strong>. Pensate à u vostru livellu di cunfidenza in i so amministratori.
+    followers_count: Numeru d’abbunati
+    lock_link: Rendete u contu privatu
+    purge: Toglie di a lista d’abbunati
+    success:
+      one: Suppressione di l’abbunati d’un duminiu...
+      other: Suppressione di l’abbunati da %{count} duminii...
+    true_privacy_html: Ùn vi scurdate chì <strong>una vera cunfidenzialità pò solu esse ottenuta cù crittografia da un capu à l’altru</strong>.
+    unlocked_warning_html: Tuttu u mondu pò seguitavi è vede i vostri statuti privati. %{lock_link}  per pudè cunfirmà o righjittà abbunamenti.
+    unlocked_warning_title: U vostru contu hè pubblicu
+  generic:
+    changes_saved_msg: Cambiamenti salvati!
+    powered_by: mossu da %{link}
+    save_changes: Salvà e mudificazione
+    validation_errors:
+      one: Qualcosa ùn và bè! Verificate un prublemu quì sottu.
+      other: Qualcosa ùn và bè! Verificate %{count} prublemi quì sottu.
+  imports:
+    preface: Pudete impurtà certi dati cumu e persone chì seguitate o bluccate nant’à u vostru contu nant’à st’istanza à partesi di fugliali creati nant’à un’altr’istanza.
+    success: I vostri dati sò stati impurtati è saranu trattati da quì à pocu.
+    types:
+      blocking: Persone chì bluccate
+      following: Persone chì seguitate
+      muting: Persone chì piattate
+    upload: Impurtà
+  in_memoriam_html: In mimoria.
+  invites:
+    delete: Disattivà
+    expired: Spirata
+    expires_in:
+      '1800': 30 minuti
+      '21600': 6 ore
+      '3600': 1 ora
+      '43200': 12 ore
+      '604800': 1 settimana
+      '86400': 1 ghjornu
+    expires_in_prompt: Mai
+    generate: Creà
+    max_uses:
+      one: 1 usu
+      other: "%{count} usi"
+    max_uses_prompt: Micca limita
+    prompt: Create è spartete ligami cù altre parsone per dà accessu à l’istanza
+    table:
+      expires_at: Spira
+      uses: Utiliza
+    title: Invità ghjente
+  landing_strip_html: "<strong>%{name}</strong> hè nant’à %{link_to_root_path}. Pudete seguitallu·a o cumunicà cù ellu·a cù un contu in qualche parte di u fediverse."
+  landing_strip_signup_html: Pudete ancu <a href="%{sign_up_path}">arrigistravi quì</a>.
+  lists:
+    errors:
+      limit: Ùn pudete più creà altre liste.
+  media_attachments:
+    validations:
+      images_and_video: Ùn si pò micca aghjunghje un filmettu à un statutu chì hà digià ritratti
+      too_many: Ùn si pò micca aghjunghje più di 4 fugliali
+  migrations:
+    acct: cugnome@duminiu di u novu contu
+    currently_redirecting: "’U vostru prufile riindiriza tuttu versu à:’"
+    proceed: Salvà
+    updated_msg: I paramettri di migrazione sò stati messi à ghjornu!
+  moderation:
+    title: Muderazione
+  notification_mailer:
+    digest:
+      action: Vede tutte e nutificazione
+      body: Eccu cio ch’avete mancatu dapoi à a vostr’ultima visita u %{since}
+      mention: "%{name} v’hà mintuvatu·a in:"
+      new_followers_summary:
+        one: Avete ancu un’abbunatu novu!
+        other: Avete ancu %{count} abbunati novi!
+      subject:
+        one: "Una nutificazione nova dapoi à a vostr’ultima visita \U0001F418"
+        other: "%{count} nutificazione nove dapoi à a vostr’ultima visita \U0001F418"
+      title: Dapoi l’ultima volta…
+    favourite:
+      body: "%{name} hà aghjuntu u vostru statutu à i so favuriti :"
+      subject: "%{name} hà messu u vostru post in i so favuriti"
+      title: Novu favuritu
+    follow:
+      body: "%{name} s’hè abbunatu à u vostru contu !"
+      subject: "%{name} vi seguita"
+      title: Abbunatu novu
+    follow_request:
+      action: Vede e dumande d’abbunamentu
+      body: "%{name} vole abbunassi à u vostru contu"
+      subject: 'Dumanda d’abbunamentu: %{name}'
+      title: Nova dumanda d’abbunamentu
+    mention:
+      action: Risposta
+      body: "%{name} v’hà mintuvatu·a indè :"
+      subject: "%{name} v’hà mintuvatu·a"
+      title: Nova menzione
+    reblog:
+      body: 'U vostru statutu hè statu spartutu da %{name}:'
+      subject: "%{name} hà spartutu u vostru statutu"
+      title: Nova spartera
+  number:
+    human:
+      decimal_units:
+        format: "%n%u"
+        units:
+          billion: G
+          million: M
+          quadrillion: P
+          thousand: K
+          trillion: T
+          unit: ''
+  pagination:
+    newer: Più ricente
+    next: Dopu
+    older: Più vechju
+    prev: Nanzu
+    truncate: "&hellip;"
+  preferences:
+    languages: Lingue
+    other: Altre
+    publishing: Pubblicazione
+    web: Web
+  push_notifications:
+    favourite:
+      title: "%{name} hà aghjuntu u vostru statutu à i so favuriti"
+    follow:
+      title: "%{name} vi seguita"
+    group:
+      title: "%{count} nutificazioni"
+    mention:
+      action_boost: Sparte
+      action_expand: Vede di più
+      action_favourite: Aghjunghje à i favuriti
+      title: "%{name} v’hà mintuvatu·a"
+    reblog:
+      title: "%{name} hà spartutu u vostru statutu"
+  remote_follow:
+    acct: Entrate u vostru cugnome@istanza da induve vulete siguità stu contu.
+    missing_resource: Ùn avemu pussutu à truvà l’indirizzu di ridirezzione
+    proceed: Cuntinuà per siguità
+    prompt: 'Avete da siguità:'
+  remote_unfollow:
+    error: Errore
+    title: Titulu
+    unfollowed: Disabbunatu
+  sessions:
+    activity: Ultima attività
+    browser: Navigatore
+    browsers:
+      alipay: Alipay
+      blackberry: Blackberry
+      chrome: Chrome
+      edge: Microsoft Edge
+      electron: Electron
+      firefox: Firefox
+      generic: Navigatore scunnisciutu
+      ie: Internet Explorer
+      micro_messenger: MicroMessenger
+      nokia: Nokia S40 Ovi Browser
+      opera: Opera
+      otter: Otter
+      phantom_js: PhantomJS
+      qq: QQ Browser
+      safari: Safari
+      uc_browser: UCBrowser
+      weibo: Weibo
+    current_session: Sessione attuale
+    description: "%{browser} nant’à %{platform}"
+    explanation: Quessi sò i navigatori cunnettati à u vostru contu Mastodon.
+    ip: IP
+    platforms:
+      adobe_air: Adobe Air
+      android: Android
+      blackberry: Blackberry
+      chrome_os: ChromeOS
+      firefox_os: Firefox OS
+      ios: iOS
+      linux: Linux
+      mac: Mac
+      other: piattaforma scunnisciuta
+      windows: Windows
+      windows_mobile: Windows Mobile
+      windows_phone: Windows Phone
+    revoke: Rivucà
+    revoke_success: Sessione rivucata
+    title: Sessioni
+  settings:
+    authorized_apps: Applicazione auturizate
+    back: Ritornu nant’à Mastodon
+    delete: Suppressione di u contu
+    development: Sviluppu
+    edit_profile: Mudificà u prufile
+    export: Spurtazione d’infurmazione
+    followers: Abbunati auturizati
+    import: Impurtazione
+    migrate: Migrazione di u contu
+    notifications: Nutificazione
+    preferences: Priferenze
+    settings: Parametri
+    two_factor_authentication: Identificazione à dui fattori
+    your_apps: E vostre applicazione
+  statuses:
+    attached:
+      description: 'Aghjuntu: %{attached}'
+      image:
+        one: "%{count} ritrattu"
+        other: "%{count} ritratti"
+      video:
+        one: "%{count} filmettu"
+        other: "%{count} filmetti"
+    content_warning: 'Avertimentu: %{warning}'
+    disallowed_hashtags:
+      one: 'cuntene l’hashtag disattivatu: %{tags}'
+      other: 'cuntene l’hashtag disattivati: %{tags}'
+    open_in_web: Apre nant’à u web
+    over_character_limit: Site sopr’à a limita di %{max} caratteri
+    pin_errors:
+      limit: Avete digià puntarulatu u numeru massimale di statuti
+      ownership: Pudete puntarulà solu unu di i vostri propii statuti
+      private: Ùn pudete micca puntarulà un statutu ch’ùn hè micca pubblicu
+      reblog: Ùn pudete micca puntarulà una spartera
+    show_more: Vede di più
+    title: '%{name}: "%{quote}"'
+    visibilities:
+      private: Solu per l’abbunati
+      private_long: Mustrà solu à l’abbunati
+      public: Pubblicu
+      public_long: Tuttu u mondu pò vede
+      unlisted: Micca listatu
+      unlisted_long: Tuttu u mondu pò vede, mà micca indè e linee pubbliche
+  stream_entries:
+    click_to_show: Cliccà per vede
+    pinned: Statutu puntarulatu
+    reblogged: spartutu
+    sensitive_content: Cuntenutu sensibile
+  terms:
+    body_html: |
+      <h2>Privacy Policy</h2>
+      <h3 id="collect">What information do we collect?</h3>
+
+      <ul>
+        <li><em>Basic account information</em>: If you register on this server, you may be asked to enter a username, an e-mail address and a password. You may also enter additional profile information such as a display name and biography, and upload a profile picture and header image. The username, display name, biography, profile picture and header image are always listed publicly.</li>
+        <li><em>Posts, following and other public information</em>: The list of people you follow is listed publicly, the same is true for your followers. When you submit a message, the date and time is stored as well as the application you submitted the message from. Messages may contain media attachments, such as pictures and videos. Public and unlisted posts are available publicly. When you feature a post on your profile, that is also publicly available information. Your posts are delivered to your followers, in some cases it means they are delivered to different servers and copies are stored there. When you delete posts, this is likewise delivered to your followers. The action of reblogging or favouriting another post is always public.</li>
+        <li><em>Direct and followers-only posts</em>: All posts are stored and processed on the server. Followers-only posts are delivered to your followers and users who are mentioned in them, and direct posts are delivered only to users mentioned in them. In some cases it means they are delivered to different servers and copies are stored there. We make a good faith effort to limit the access to those posts only to authorized persons, but other servers may fail to do so. Therefore it’s important to review servers your followers belong to. You may toggle an option to approve and reject new followers manually in the settings. <em>Please keep in mind that the operators of the server and any receiving server may view such messages</em>, and that recipients may screenshot, copy or otherwise re-share them. <em>Do not share any dangerous information over Mastodon.</em></li>
+        <li><em>IPs and other metadata</em>: When you log in, we record the IP address you log in from, as well as the name of your browser application. All the logged in sessions are available for your review and revocation in the settings. The latest IP address used is stored for up to 12 months. We also may retain server logs which include the IP address of every request to our server.</li>
+      </ul>
+
+      <hr class="spacer" />
+
+      <h3 id="use">What do we use your information for?</h3>
+
+      <p>Any of the information we collect from you may be used in the following ways:</p>
+
+      <ul>
+        <li>To provide the core functionality of Mastodon. You can only interact with other people’s content and post your own content when you are logged in. For example, you may follow other people to view their combined posts in your own personalized home timeline.</li>
+        <li>To aid moderation of the community, for example comparing your IP address with other known ones to determine ban evasion or other violations.</li>
+        <li>The email address you provide may be used to send you information, notifications about other people interacting with your content or sending you messages, and to respond to inquiries, and/or other requests or questions.</li>
+      </ul>
+
+      <hr class="spacer" />
+
+      <h3 id="protect">How do we protect your information?</h3>
+
+      <p>We implement a variety of security measures to maintain the safety of your personal information when you enter, submit, or access your personal information. Among other things, your browser session, as well as the traffic between your applications and the API, are secured with SSL, and your password is hashed using a strong one-way algorithm. You may enable two-factor authentication to further secure access to your account.</p>
+
+      <hr class="spacer" />
+
+      <h3 id="data-retention">What is our data retention policy?</h3>
+
+      <p>We will make a good faith effort to:</p>
+
+      <ul>
+        <li>Retain server logs containing the IP address of all requests to this server, in so far as such logs are kept, no more than 90 days.</li>
+        <li>Retain the IP addresses associated with registered users no more than 12 months.</li>
+      </ul>
+
+      <p>You can request and download an archive of your content, including your posts, media attachments, profile picture, and header image.</p>
+
+      <p>You may irreversibly delete your account at any time.</p>
+
+      <hr class="spacer"/>
+
+      <h3 id="cookies">Do we use cookies?</h3>
+
+      <p>Yes. Cookies are small files that a site or its service provider transfers to your computer’s hard drive through your Web browser (if you allow). These cookies enable the site to recognize your browser and, if you have a registered account, associate it with your registered account.</p>
+
+      <p>We use cookies to understand and save your preferences for future visits.</p>
+
+      <hr class="spacer" />
+
+      <h3 id="disclose">Do we disclose any information to outside parties?</h3>
+
+      <p>We do not sell, trade, or otherwise transfer to outside parties your personally identifiable information. This does not include trusted third parties who assist us in operating our site, conducting our business, or servicing you, so long as those parties agree to keep this information confidential. We may also release your information when we believe release is appropriate to comply with the law, enforce our site policies, or protect ours or others rights, property, or safety.</p>
+
+      <p>Your public content may be downloaded by other servers in the network. Your public and followers-only posts are delivered to the servers where your followers reside, and direct messages are delivered to the servers of the recipients, in so far as those followers or recipients reside on a different server than this.</p>
+
+      <p>When you authorize an application to use your account, depending on the scope of permissions you approve, it may access your public profile information, your following list, your followers, your lists, all your posts, and your favourites. Applications can never access your e-mail address or password.</p>
+
+      <hr class="spacer" />
+
+      <h3 id="coppa">Children’s Online Privacy Protection Act Compliance</h3>
+
+      <p>Our site, products and services are all directed to people who are at least 13 years old. If this server is in the USA, and you are under the age of 13, per the requirements of COPPA (<a href="https://en.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act">Children’s Online Privacy Protection Act</a>) do not use this site.</p>
+
+      <hr class="spacer" />
+
+      <h3 id="changes">Changes to our Privacy Policy</h3>
+
+      <p>If we decide to change our privacy policy, we will post those changes on this page.</p>
+
+      <p>This document is CC-BY-SA. It was last updated March 7, 2018.</p>
+
+      <p>Originally adapted from the <a href="https://github.com/discourse/discourse">Discourse privacy policy</a>.</p>
+    title: Termini d’usu è di cunfidenzialità per %{instance}
+  themes:
+    contrast: Cuntrastu altu
+    default: Mastodon
+  time:
+    formats:
+      default: "%d %b %Y, %H:%M"
+  two_factor_authentication:
+    code_hint: Entrate u codice generatu da l’applicazione per cunfirmà
+    description_html: S’ella hè attivata <strong>l’identificazione à dui fattori</strong>, duvete avè u vostru telefuninu pè ottene un codice di cunnezzione.
+    disable: Disattivà
+    enable: Attivà
+    enabled: Identificazione à dui fattori attivata
+    enabled_success: L’identificazione à dui fattori hè stata attivata.
+    generate_recovery_codes: Creà codici di ricuperazione
+    instructions_html: "<strong>Scanate stu QR code cù Google Authenticator, Authy o qualcosa cusì nant’à u vostru telefuninu</strong>. St’applicazione hà da creà codici da entrà ogni volta chì vi cunnettate."
+    lost_recovery_codes: I codici di ricuperazione à usu unicu vi permettenu di sempre avè accessu à u vostru contu s’è voi avete persu u vostru telefuninu. S’elli sò ancu persi, pudete creà codici novi quì. I vechji codici ùn marchjeranu più.
+    manual_instructions: S’ellu ùn hè micca pussibule scanà u QR code, pudete entre sta chjave sicreta:’
+    recovery_codes: Codici di ricuperazione
+    recovery_codes_regenerated: Codici di ricuperazione ricreati
+    recovery_instructions_html: Pudete fà usu di i codici quì sottu per sempre avè accessu à u vostru contu s’ellu hè statu persu u vostru telefuninu. <strong>Guardateli in una piazza sicura</strong>. Per esempiu, stampati è cunservati cù altri ducumenti impurtanti.
+    setup: Installà
+    wrong_code: U codice ùn hè micca currettu! Site sicuru che l’ora di u telefuninu è di u servore sò esatte?
+  user_mailer:
+    backup_ready:
+      explanation: Avete dumandatu un’archiviu cumpletu di u vostru contu Mastodon. Avà hè prontu per scaricà!
+      subject: U vostru archiviu hè prontu à scaricà
+      title: Archiviu prontu
+    welcome:
+      edit_profile_action: Cunfigurazione di u prufile
+      edit_profile_step: Pudete persunalizà u vostru prufile cù un ritrattu di prufile o di cuprendula, un nome pubblicu persunalizatu, etc. Pudete ancu rende u contu privatu per duvè cunfirmà ogni dumanda d’abbunamentu.
+      explanation: Eccu alcune idee per principià
+      final_action: Principià à pustà
+      final_step: 'Andemu! Ancu senza abbunati i vostri missaghji pubblichi puderanu esse visti da altre persone, per esempiu nant’a linea lucale è l’hashtag. Pudete ancu prisintavi nant’à u hashtag #introductions!'
+      full_handle: U vostru identificatore cumplettu
+      full_handle_hint: Quessu ghjè cio chì direte à i vostri amichi per circavi, abbunassi à u vostru contu da altrò, o mandà missaghji.
+      review_preferences_action: Mudificà e priferenze
+      review_preferences_step: Quì pudete adattà u cumpurtamentu di Mastodon à e vostre priferenze, cum’è l’email che vulete riceve, u nivellu di cunfidenzialità predefinitu di i vostri statuti, o u cumpurtamentu di i GIF animati.
+      subject: Benvenutu·a nant’à Mastodon
+      tip_bridge_html: S’è voi venite di Twitter, pudete truvà i vostri amichi da quallà chì sò nant’à Mastodon cù a <a href="%{bridge_url}">bridge app</a>. Mà ùn marchja chè s’elli l’anu ancu usata!
+      tip_federated_timeline: A linea pubblica glubale mostra i statuti da altre istanze nant’a rete Mastodon, mà ùn hè micca cumpleta perchè ci sò soli i conti à quelli sò abbunati membri di a vostr’istanza.
+      tip_following: Site digià abbunatu·a à l’amministratori di u vostru servore. Per truvà d’altre parsone da siguità, pudete pruvà e linee pubbliche.
+      tip_local_timeline: A linea pubblica lucale ghjè una vista crunulogica di i statuti di a ghjente nant’à %{instance}.
+      tip_mobile_webapp: Pudete aghjunghje Mastodon à a pagina d’accolta di u vostru navigatore di telefuninu per riceve nutificazione, cum’un applicazione.
+      tips: Cunsiglii
+      title: Benvenutu·a, %{name}!
+  users:
+    invalid_email: L’indirizzu e-mail ùn hè currettu.
+    invalid_otp_token: U codice d’identificazione ùn hè currettu.
+    seamless_external_login: Site cunnettatu·a dapoi un serviziu esternu, allora i parametri di chjave d’accessu è d’indirizzu e-mail ùn so micca dispunibili.
+    signed_in_as: 'Cunnettatu·a cum’è:'
diff --git a/config/locales/devise.co.yml b/config/locales/devise.co.yml
new file mode 100644
index 0000000000..d5bb708876
--- /dev/null
+++ b/config/locales/devise.co.yml
@@ -0,0 +1,82 @@
+---
+co:
+  devise:
+    confirmations:
+      confirmed: U vostru indirizzu email hè statu cunfirmatu.
+      send_instructions: Avete da riceve un’email cù l’istruzzione di cunfirmazione in qualchì minuta. Pensate à verificà u cartulare di spam s’ellu ùn c’hè nunda.
+      send_paranoid_instructions: S’ellu esiste u vostru indirizzu email in a database, avete da riceve l’istruzzione pè cunfirmà u vostru contu in qualchì minuta. Pensate à verificà u cartulare di spam s’ellu ùn c’hè nunda.
+    failure:
+      already_authenticated: Site digià cunnettatu·a.
+      inactive: U vostru contu ùn hè ancu attivatu.
+      invalid: L’ %{authentication_keys} o a chjave d’accessu ùn sò curretti.
+      last_attempt: Avete un’ultimu tintativu nanzu chì u vostru contu sia chjosu.
+      locked: U vostru contu hè chjosu.
+      not_found_in_database: L’ %{authentication_keys} o a chjave d’accessu ùn sò curretti.
+      timeout: A vostra sezzione hè spirata. Ricunnettatevi pè cuntinuà.
+      unauthenticated: Cunnettatevi o arregistratevi pè cuntinuà.
+      unconfirmed: Duvete cunfirmà u vostru contu pè cuntinuà.
+    mailer:
+      confirmation_instructions:
+        action: Verificà l’indirizzu email
+        explanation: Avete creatu un contu nant’à %{host} cù st’indirizzu email. Pudete attivallu cù un clic, o ignurà quessu missaghji s’ellu un era micca voi.
+        extra_html: Pensate à leghje <a href="%{terms_path}">e regule di l’istanza</a> è <a href="%{policy_path}">i termini d’usu</a>.
+        subject: 'Mastodon: Istruzzione di cunfirmazione per %{instance}'
+        title: Verificà l’indirizzu email
+      email_changed:
+        explanation: 'L’indirizzu email di u vostru contu hè stata cambiata per:'
+        extra: S’ellu un era micca voi ch’avete cambiatu u vostru email, qualch’un’altru hà accessu à u vostru contu. Duvete cambià a vostra chjave d’accessu o cuntattà l’amministratore di l’istanza s’ellu ùn hè più pussibule di cunnettavi.
+        subject: 'Mastodon: Email cambiatu'
+        title: Novu indirizzu email
+      password_change:
+        explanation: A chjave d’accessu per u vostru contu hè stata cambiata.
+        extra: S’ellu un era micca voi ch’avete cambiatu a vostra chjave d’accessu, qualch’un’altru hà accessu à u vostru contu. Duvete cambià a vostra chjave d’accessu o cuntattà l’amministratore di l’istanza s’ellu ùn hè più pussibule di cunnettavi.
+        subject: 'Mastodon: Chjave d’accessu cambiata'
+        title: Chjave cambiata
+      reconfirmation_instructions:
+        explanation: Cunfirmà u novu indirizzu per cambià l’email.
+        extra: S’ellu ùn era micca voi, ignurate stu missaghju. L’email ùn cambiarà micca s’è voi ùn cliccate micca u ligame.
+        subject: 'Mastodon: Cunfirmà l’email per %{instance}'
+        title: Verificà indirizzu email
+      reset_password_instructions:
+        action: Cambià a chjave d’accessu
+        explanation: Avete dumandatu una nova chjave d’accessu per u vostru contu.
+        extra: S’ellu ùn era micca voi, ignurate stu missaghju. A chjave d’accessu ùn cambiarà micca s’è voi ùn cliccate micca u ligame.
+        subject: 'Mastodon: Cambià a chjave d’accessu'
+        title: Cambià a chjave
+      unlock_instructions:
+        subject: 'Mastodon: Riapre u contu'
+    omniauth_callbacks:
+      failure: Ùn pudemu micca cunnettavi da %{kind} perchè "%{reason}".
+      success: Vi site cunnettatu·a da %{kind}.
+    passwords:
+      no_token: Ùn pudete micca vede sta pagina senza vene d’un e-mail di cambiamentu di chjave d’accessu. S’è voi venite quì dapoi st’e-mail, assicuratevi ch’avete utilizatu l’indirizzu URL cumpletu.
+      send_instructions: Avete da riceve l’istruzzione di cambiamentu di a chjave d’accessu in qualchì minuta.
+      send_paranoid_instructions: S’ellu esiste u vostr’e-mail in a database, avete da riceve un ligame di reinizialisazione.
+      updated: A vostra chjave d’accessu hè stata cambiata, è site cunnettatu·a.
+      updated_not_active: A vostra chjave d’accessu hè stata cambiata.
+    registrations:
+      destroyed: U vostru contu hè statu sguassatu. Avvedeci!
+      signed_up: Benvinutu! Site cunnettatu·a.
+      signed_up_but_inactive: Site arregistratu·a, mà ùn pudete micca cunnettavi perchè u vostru contu deve esse attivatu.
+      signed_up_but_locked: Site arregistratu·a, mà ùn pudete micca cunnettavi perchè u vostru contu hè chjosu.
+      signed_up_but_unconfirmed: Un missaghju cù un ligame di cunfirmazione hè statu mandatu à u vostru indirizzu e-mail. Aprite stu ligame pè attivà u vostru contu. Pensate à verificà u cartulare di spam s’ellu ùn c’hè nunda.
+      update_needs_confirmation: U vostru contu hè statu messu à ghjornu mà duvemu verificà u vostru novu e-mail. Un missaghju cù un ligame di cunfirmazione hè statu mandatu. Pensate à verificà u cartulare di spam s’ellu ùn c’hè nunda.
+      updated: U vostru contu hè statu messu à ghjornu.
+    sessions:
+      already_signed_out: Scunnettatu·a.
+      signed_in: Cunnettatu·a.
+      signed_out: Scunnettatu·a.
+    unlocks:
+      send_instructions: Avete da riceve un’e-mail cù l’istruzzione pè riapre u vostru contu in qualchì minuta.
+      send_paranoid_instructions: S’ellu esiste u vostru contu, avete da riceve un’e-mail dù l’istruzzione pè riapre u vostru contu.
+      unlocked: U vostru contu hè statu riapertu, pudete cunnettavi pè cuntinuà.
+  errors:
+    messages:
+      already_confirmed: hè digià cunfirmatu, pudete pruvà à cunnettà vi
+      confirmation_period_expired: deve esse cunfirmatu nanz’à %{period}, duvete fà un’altra dumanda
+      expired: hè spiratu, duvete fà un’altra dumanda
+      not_found: ùn hè micca statu trovu
+      not_locked: ùn era micca chjosu
+      not_saved:
+        one: Un prublemu hà impeditu a cunservazione di stu (sta) %{resource}
+        other: "%{count} prublemi anu impeditu a cunservazione di stu (sta) %{resource} :"
diff --git a/config/locales/doorkeeper.co.yml b/config/locales/doorkeeper.co.yml
new file mode 100644
index 0000000000..01b082e401
--- /dev/null
+++ b/config/locales/doorkeeper.co.yml
@@ -0,0 +1,119 @@
+---
+co:
+  activerecord:
+    attributes:
+      doorkeeper/application:
+        name: Nome di l’applicazione
+        redirect_uri: URI di ridirezzione
+        scopes: Scopi
+        website: Situ di l’applicazione
+    errors:
+      models:
+        doorkeeper/application:
+          attributes:
+            redirect_uri:
+              fragment_present: ùn pò cuntene un pezzu.
+              invalid_uri: duve esse un’URI curretta.
+              relative_uri: duve esse un’URI assoluta.
+              secured_uri: duve esse un’URL HTTPS/SSL.
+  doorkeeper:
+    applications:
+      buttons:
+        authorize: Appruvà
+        cancel: Sguassà
+        destroy: Strughje
+        edit: Mudificà
+        submit: Mandà
+      confirmations:
+        destroy: Site sicuru·a?
+      edit:
+        title: Mudificà l’applicazione
+      form:
+        error: Uups! V’invitemu à verificà u vostru formulariu per vede s’elli ùn ci sò sbaglii.
+      help:
+        native_redirect_uri: Utilizate %{native_redirect_uri} pè e prove lucale
+        redirect_uri: Utilizzate una linea per ogni URI
+        scopes: Separate i scopi cù spazii. Lasciate viotu per utilizzà i scopi predefiniti.
+      index:
+        application: Applicazione
+        callback_url: URL di richjama
+        delete: Toglie
+        name: Nome
+        new: Applicazione nova
+        scopes: Scopi
+        show: Vede
+        title: E vostre applicazione
+      new:
+        title: Applicazione nova
+      show:
+        actions: Azzioni
+        application_id: Chjave di u clientu
+        callback_urls: URL di richjama
+        scopes: Scopi
+        secret: Sicretu di u clientu
+        title: 'Applicazione : %{name}'
+    authorizations:
+      buttons:
+        authorize: Appruvà
+        deny: Ricusà
+      error:
+        title: C’hè statu un prublemu
+      new:
+        able_to: St’applicazione puderà
+        prompt: Parmette %{client_name} d’utilizzà u vostru contu?
+        title: Permessu riquestu
+      show:
+        title: Codice d’auturizazione da cupià indè l’applicazione
+    authorized_applications:
+      buttons:
+        revoke: Sguassà
+      confirmations:
+        revoke: Site sicuru·a?
+      index:
+        application: Applicazione
+        created_at: Auturizata u
+        date_format: "%d-%m-%Y %H:%M:%S"
+        scopes: Scopi
+        title: E vostre applicazione auturizate
+    errors:
+      messages:
+        access_denied: U pruprietariu di a risorsa o u servore d’autitinficazione hà ricusatu a dumanda.
+        credential_flow_not_configured: U flussu di l’identificazione di u pruprietariu di a risorsa hà fiascatu perchè Doorkeeper.configure.resource_owner_from_credentials ùn hè micca cunfiguratu.
+        invalid_client: L’autintificazione di u cliente hà fiascatu perchè u cliente ùn hè micca cunnisciutu, l’identificazione di u cliente ùn hè cumpresa, o u modu d’identificazione ùn marchja micca.
+        invalid_grant: L’accunsentu d’auturizazione furnitu ùn hè currettu, hè spiratu, sguassatu, ùn và micca cù l’indirizzu di ridirezzione usatu in a dumanda d’auturizazione, o hè statu emessu per un’altru cliente.
+        invalid_redirect_uri: L’URI di ridirezzione ùn hè curretta.
+        invalid_request: Ci manca un parametru riquestu indè a dumanda, cuntene un parametru ch’ùn esiste micca, o altru sbagliu di forma.
+        invalid_resource_owner: L’idintificanti di u pruprietariu di a risorsa ùn sò curretti, o u pruprietariu ùn pò micca esse trovu.
+        invalid_scope: U scopu dumandatu ùn hè currettu, hè scunnisciutu, o altru sbagliu di forma.
+        invalid_token:
+          expired: A marca d’accessu hè spirata
+          revoked: A marca d’accessu hè stata rivucata
+          unknown: A marca d’accessu ùn hè curretta
+        resource_owner_authenticator_not_configured: Ùn c’hè micca pussutu ricercà u pruprietariu di a risorsa perchè Doorkeeper.configure.resource_owner_authenticator ùn hè micca cunfiguratu.
+        server_error: C’hè statu un prublemu cù u servore d’auturizazione.
+        temporarily_unavailable: U servore d’auturizazione ùn pò micca trattà a dumanda avà perchè hè sopraccaricatu o in mantenimentu.
+        unauthorized_client: U cliente ùn pò micca fà sta dumanda cusì.
+        unsupported_grant_type: Stu tippu d’accunsentu ùn marchja micca nant’à stu servore d’auturizazione.
+        unsupported_response_type: Sta risposta ùn marchja micca nant’à stu servore d’auturizazione.
+    flash:
+      applications:
+        create:
+          notice: Applicazione creata.
+        destroy:
+          notice: Applicazione sguassata.
+        update:
+          notice: Applicazione messa à ghjornu.
+      authorized_applications:
+        destroy:
+          notice: Applicazione sguassata.
+    layouts:
+      admin:
+        nav:
+          applications: Applicazione
+          oauth2_provider: Furnitore OAuth2
+      application:
+        title: Auturizazione OAuth riquestata.
+    scopes:
+      follow: bluccà, sbluccà, è reghje l’abbunamenti
+      read: leghje l’infurmazione di u vostru contu
+      write: mandà missaghji per voi
diff --git a/config/locales/simple_form.co.yml b/config/locales/simple_form.co.yml
new file mode 100644
index 0000000000..21404897b3
--- /dev/null
+++ b/config/locales/simple_form.co.yml
@@ -0,0 +1,79 @@
+---
+co:
+  simple_form:
+    hints:
+      defaults:
+        avatar: Furmatu PNG, GIF o JPG. 2Mo o menu. Sarà ridottu à 400x400px
+        digest: Solu mandatu dopu à una longa perioda d’inattività, è solu s’elli ci sò novi missaghji diretti
+        display_name:
+          one: Ci ferma <span class="name-counter">1</span> caratteru
+          other: Ci fermanu <span class="name-counter">%{count}</span> caratteri
+        fields: Pudete avè fin’à 4 elementi mustrati cum’un tavulone nant’à u vostru prufile
+        header: Furmatu PNG, GIF o JPG. 2Mo o menu. Sarà ridottu à 700x335px
+        locked: Duvarete appruvà e dumande d’abbunamentu
+        note:
+          one: Ci ferma <span class="name-counter">1</span> caratteru
+          other: Ci fermanu <span class="name-counter">%{count}</span> caratteri
+        setting_noindex: Tocca à u vostru prufile pubblicu è i vostri statuti
+        setting_theme: Tocca à l’apparenza di Mastodon quandu site cunnettatu·a da qualch’apparechju.
+      imports:
+        data: Un fugliale CSV da un’altr’istanza di Mastodon
+      sessions:
+        otp: Entrate u codice d’identificazione à dui fattori nant’à u vostru telefuninu, o unu di i vostri codici di ricuperazione.
+      user:
+        filtered_languages: Ùn viderete micca e lingue selezziunate nant’à e linee pubbliche.
+    labels:
+      account:
+        fields:
+          name: Label
+          value: Content
+      defaults:
+        avatar: Ritrattu di prufile
+        confirm_new_password: Cunfirmà a nova chjave d’accessu
+        confirm_password: Cunfirmà a chjave d’accessu
+        current_password: Chjave d’accessu attuale
+        data: Dati
+        display_name: Nome pubblicu
+        email: Indirizzu e-mail
+        expires_in: Spira dopu à
+        fields: Metadata di u prufile
+        filtered_languages: Lingue filtrate
+        header: Ritrattu di cuprendula
+        locale: Lingua
+        locked: Privatizà u contu
+        max_uses: Numeru massimale d’utilizazione
+        new_password: Nova chjave d’accessu
+        note: Descrizzione
+        otp_attempt: Codice d’identificazione à dui fattori
+        password: Chjave d’accessu
+        setting_auto_play_gif: Lettura autumatica di i GIF animati
+        setting_boost_modal: Mustrà una cunfirmazione per sparte un statutu
+        setting_default_privacy: Cunfidenzialità di i statuti
+        setting_default_sensitive: Sempre cunsiderà media cum’è sensibili
+        setting_delete_modal: Mustrà une cunfirmazione per toglie un statutu
+        setting_display_sensitive_media: Sempre mustrà media marcati cum’è sensibili
+        setting_noindex: Dumandà à i motori di ricerca internet d’un pudè micca esse truvatu·a cusì
+        setting_reduce_motion: Fà chì l’animazione vanu più pianu
+        setting_system_font_ui: Pulizza di caratteri di u sistemu
+        setting_theme: Tema di u situ
+        setting_unfollow_modal: Mustrà una cunfirmazione per siguità qualch’unu
+        severity: Severità
+        type: Tippu d’impurtazione
+        username: Cugnome
+        username_or_email: Cugnome o Email
+      interactions:
+        must_be_follower: Piattà e nutificazione di quelli·e ch’ùn vi seguitanu
+        must_be_following: Piattà e nutificazione di quelli·e ch’ùn seguitate
+        must_be_following_dm: Bluccà e missaghji diretti di quelli·e ch’ùn seguitate
+      notification_emails:
+        digest: Mandà e-mail di ricapitulazione
+        favourite: Mandà un’e-mail quandu qualch’unu aghjunghje i mo statuti à i so favuriti
+        follow: Mandà un’e-mail quandu qualch’unu mi seguita
+        follow_request: Mandà un’e-mail quandu qualch’unu vole seguitami
+        mention: Mandà un’e-mail quandu qualch’unu mi mintuva
+        reblog: Mandà un’e-mail quandu qualch’unu sparte i mo statuti
+    'no': Nò
+    required:
+      mark: "*"
+      text: riquisiti
+    'yes': Ié

From ea4e24330311a773b0ca76be5873e754f583dd7a Mon Sep 17 00:00:00 2001
From: Hugo Gameiro <hmgameiro@gmail.com>
Date: Mon, 7 May 2018 01:28:28 +0100
Subject: [PATCH 22/40] Improve OpenStack v3 compatibility (#7392)

* Update paperclip.rb

* Update .env.production.sample

* Update paperclip.rb
---
 .env.production.sample           | 2 ++
 config/initializers/paperclip.rb | 1 +
 2 files changed, 3 insertions(+)

diff --git a/.env.production.sample b/.env.production.sample
index c936546da8..24b6b01439 100644
--- a/.env.production.sample
+++ b/.env.production.sample
@@ -113,6 +113,8 @@ SMTP_FROM_ADDRESS=notifications@example.com
 # For Keystone V3, the value for SWIFT_TENANT should be the project name
 # SWIFT_TENANT=
 # SWIFT_PASSWORD=
+# Some OpenStack V3 providers require PROJECT_ID (optional)
+# SWIFT_PROJECT_ID=
 # Keystone V2 and V3 URLs are supported. Use a V3 URL if possible to avoid
 # issues with token rate-limiting during high load.
 # SWIFT_AUTH_URL=
diff --git a/config/initializers/paperclip.rb b/config/initializers/paperclip.rb
index 17a520aa20..c134bc5b8f 100644
--- a/config/initializers/paperclip.rb
+++ b/config/initializers/paperclip.rb
@@ -60,6 +60,7 @@ elsif ENV['SWIFT_ENABLED'] == 'true'
     fog_credentials: {
       provider: 'OpenStack',
       openstack_username: ENV['SWIFT_USERNAME'],
+      openstack_project_id: ENV['SWIFT_PROJECT_ID'],
       openstack_project_name: ENV['SWIFT_TENANT'],
       openstack_tenant: ENV['SWIFT_TENANT'], # Some OpenStack-v2 ignores project_name but needs tenant
       openstack_api_key: ENV['SWIFT_PASSWORD'],

From db012b57c2c3e65cfbd7d9b51e0c3081ed883400 Mon Sep 17 00:00:00 2001
From: abcang <abcang1015@gmail.com>
Date: Mon, 7 May 2018 16:30:18 +0900
Subject: [PATCH 23/40] Fix distribute_add_activity and
 distribute_remove_activity (#7393)

---
 app/controllers/api/v1/statuses/pins_controller.rb | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/app/controllers/api/v1/statuses/pins_controller.rb b/app/controllers/api/v1/statuses/pins_controller.rb
index bba6a6f480..54f8be667d 100644
--- a/app/controllers/api/v1/statuses/pins_controller.rb
+++ b/app/controllers/api/v1/statuses/pins_controller.rb
@@ -39,7 +39,7 @@ class Api::V1::Statuses::PinsController < Api::BaseController
       adapter: ActivityPub::Adapter
     ).as_json
 
-    ActivityPub::RawDistributionWorker.perform_async(Oj.dump(json), current_account)
+    ActivityPub::RawDistributionWorker.perform_async(Oj.dump(json), current_account.id)
   end
 
   def distribute_remove_activity!
@@ -49,6 +49,6 @@ class Api::V1::Statuses::PinsController < Api::BaseController
       adapter: ActivityPub::Adapter
     ).as_json
 
-    ActivityPub::RawDistributionWorker.perform_async(Oj.dump(json), current_account)
+    ActivityPub::RawDistributionWorker.perform_async(Oj.dump(json), current_account.id)
   end
 end

From d185f3ddafc941e280de7efc6d448449ab5ce2c9 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Mon, 7 May 2018 09:30:38 +0200
Subject: [PATCH 24/40] Use plaintext value for field value tooltips in web UI
 (#7388)

Fix #7383
---
 app/javascript/mastodon/actions/importer/normalizer.js    | 2 ++
 app/javascript/mastodon/actions/notifications.js          | 8 +-------
 .../mastodon/features/account/components/header.js        | 2 +-
 app/javascript/mastodon/utils/html.js                     | 6 ++++++
 4 files changed, 10 insertions(+), 8 deletions(-)
 create mode 100644 app/javascript/mastodon/utils/html.js

diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js
index 057bff58bd..c015d3a994 100644
--- a/app/javascript/mastodon/actions/importer/normalizer.js
+++ b/app/javascript/mastodon/actions/importer/normalizer.js
@@ -1,5 +1,6 @@
 import escapeTextContentForBrowser from 'escape-html';
 import emojify from '../../features/emoji/emoji';
+import { unescapeHTML } from '../../utils/html';
 
 const domParser = new DOMParser();
 
@@ -22,6 +23,7 @@ export function normalizeAccount(account) {
       ...pair,
       name_emojified: emojify(escapeTextContentForBrowser(pair.name)),
       value_emojified: emojify(pair.value, emojiMap),
+      value_plain: unescapeHTML(pair.value),
     }));
   }
 
diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js
index 7aa070f569..393268811e 100644
--- a/app/javascript/mastodon/actions/notifications.js
+++ b/app/javascript/mastodon/actions/notifications.js
@@ -8,6 +8,7 @@ import {
   importFetchedStatuses,
 } from './importer';
 import { defineMessages } from 'react-intl';
+import { unescapeHTML } from '../utils/html';
 
 export const NOTIFICATIONS_UPDATE      = 'NOTIFICATIONS_UPDATE';
 export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP';
@@ -31,13 +32,6 @@ const fetchRelatedRelationships = (dispatch, notifications) => {
   }
 };
 
-const unescapeHTML = (html) => {
-  const wrapper = document.createElement('div');
-  html = html.replace(/<br \/>|<br>|\n/g, ' ');
-  wrapper.innerHTML = html;
-  return wrapper.textContent;
-};
-
 export function updateNotifications(notification, intlMessages, intlLocale) {
   return (dispatch, getState) => {
     const showInColumn = getState().getIn(['settings', 'notifications', 'shows', notification.type], true);
diff --git a/app/javascript/mastodon/features/account/components/header.js b/app/javascript/mastodon/features/account/components/header.js
index def6257956..47915e6fb8 100644
--- a/app/javascript/mastodon/features/account/components/header.js
+++ b/app/javascript/mastodon/features/account/components/header.js
@@ -146,7 +146,7 @@ export default class Header extends ImmutablePureComponent {
               {fields.map((pair, i) => (
                 <dl key={i}>
                   <dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} />
-                  <dd dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} title={pair.get('value')} />
+                  <dd dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} title={pair.get('value_plain')} />
                 </dl>
               ))}
             </div>
diff --git a/app/javascript/mastodon/utils/html.js b/app/javascript/mastodon/utils/html.js
new file mode 100644
index 0000000000..0b646ce58f
--- /dev/null
+++ b/app/javascript/mastodon/utils/html.js
@@ -0,0 +1,6 @@
+export const unescapeHTML = (html) => {
+  const wrapper = document.createElement('div');
+  html = html.replace(/<br \/>|<br>|\n/g, ' ');
+  wrapper.innerHTML = html;
+  return wrapper.textContent;
+};

From 0f0cc3f2eba9c8a701d735cd54206629eeea4ddb Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Mon, 7 May 2018 09:30:53 +0200
Subject: [PATCH 25/40] Support explicitly supplying language code for status
 via REST API (#7389)

---
 app/services/post_status_service.rb | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb
index 9d2c7a4dc5..3080fb2447 100644
--- a/app/services/post_status_service.rb
+++ b/app/services/post_status_service.rb
@@ -31,7 +31,7 @@ class PostStatusService < BaseService
                                         sensitive: (options[:sensitive].nil? ? account.user&.setting_default_sensitive : options[:sensitive]),
                                         spoiler_text: options[:spoiler_text] || '',
                                         visibility: options[:visibility] || account.user&.setting_default_privacy,
-                                        language: LanguageDetector.instance.detect(text, account),
+                                        language: language_from_option(options[:language]) || LanguageDetector.instance.detect(text, account),
                                         application: options[:application])
     end
 
@@ -65,6 +65,10 @@ class PostStatusService < BaseService
     media
   end
 
+  def language_from_option(str)
+    ISO_639.find(str)&.alpha2
+  end
+
   def process_mentions_service
     ProcessMentionsService.new
   end

From 42cd363542abf0a12a4e877b3ad26024f24577ef Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Mon, 7 May 2018 09:31:07 +0200
Subject: [PATCH 26/40] Bot nameplates (#7391)

* Store actor type in database

* Add bot nameplate to web UI, add setting to preferences, API, AP
Fix #7365

* Fix code style issues
---
 .../api/v1/accounts/credentials_controller.rb         |  2 +-
 app/controllers/settings/profiles_controller.rb       |  2 +-
 .../mastodon/features/account/components/header.js    |  4 ++++
 app/javascript/styles/mastodon/components.scss        |  6 ++++++
 app/javascript/styles/mastodon/forms.scss             |  4 ++++
 app/models/account.rb                                 | 11 +++++++++++
 app/serializers/activitypub/actor_serializer.rb       |  2 +-
 app/serializers/rest/account_serializer.rb            |  2 +-
 app/services/activitypub/process_account_service.rb   |  1 +
 app/views/accounts/_header.html.haml                  |  6 +++++-
 app/views/settings/profiles/show.html.haml            |  3 +++
 config/locales/en.yml                                 |  1 +
 config/locales/simple_form.en.yml                     |  2 ++
 .../20180506221944_add_actor_type_to_accounts.rb      |  5 +++++
 db/schema.rb                                          |  3 ++-
 15 files changed, 48 insertions(+), 6 deletions(-)
 create mode 100644 db/migrate/20180506221944_add_actor_type_to_accounts.rb

diff --git a/app/controllers/api/v1/accounts/credentials_controller.rb b/app/controllers/api/v1/accounts/credentials_controller.rb
index a3c4008e64..259d07be87 100644
--- a/app/controllers/api/v1/accounts/credentials_controller.rb
+++ b/app/controllers/api/v1/accounts/credentials_controller.rb
@@ -21,7 +21,7 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
   private
 
   def account_params
-    params.permit(:display_name, :note, :avatar, :header, :locked, fields_attributes: [:name, :value])
+    params.permit(:display_name, :note, :avatar, :header, :locked, :bot, fields_attributes: [:name, :value])
   end
 
   def user_settings_params
diff --git a/app/controllers/settings/profiles_controller.rb b/app/controllers/settings/profiles_controller.rb
index 5d81668dea..1b01fc75f0 100644
--- a/app/controllers/settings/profiles_controller.rb
+++ b/app/controllers/settings/profiles_controller.rb
@@ -27,7 +27,7 @@ class Settings::ProfilesController < ApplicationController
   private
 
   def account_params
-    params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, fields_attributes: [:name, :value])
+    params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, :bot, fields_attributes: [:name, :value])
   end
 
   def set_account
diff --git a/app/javascript/mastodon/features/account/components/header.js b/app/javascript/mastodon/features/account/components/header.js
index 47915e6fb8..7358053da0 100644
--- a/app/javascript/mastodon/features/account/components/header.js
+++ b/app/javascript/mastodon/features/account/components/header.js
@@ -131,6 +131,7 @@ export default class Header extends ImmutablePureComponent {
     const content         = { __html: account.get('note_emojified') };
     const displayNameHtml = { __html: account.get('display_name_html') };
     const fields          = account.get('fields');
+    const badge           = account.get('bot') ? (<div className='roles'><div className='account-role bot'><FormattedMessage id='account.badges.bot' defaultMessage='Bot' /></div></div>) : null;
 
     return (
       <div className={classNames('account__header', { inactive: !!account.get('moved') })} style={{ backgroundImage: `url(${account.get('header')})` }}>
@@ -139,6 +140,9 @@ export default class Header extends ImmutablePureComponent {
 
           <span className='account__header__display-name' dangerouslySetInnerHTML={displayNameHtml} />
           <span className='account__header__username'>@{account.get('acct')} {lockedIcon}</span>
+
+          {badge}
+
           <div className='account__header__content' dangerouslySetInnerHTML={content} />
 
           {fields.size > 0 && (
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 3e2a1ae104..158bf68511 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -5159,6 +5159,12 @@ noscript {
   }
 }
 
+.account__header .roles {
+  margin-top: 20px;
+  margin-bottom: 20px;
+  padding: 0 15px;
+}
+
 .account__header .account__header__fields {
   font-size: 14px;
   line-height: 20px;
diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss
index f978901870..de16784a87 100644
--- a/app/javascript/styles/mastodon/forms.scss
+++ b/app/javascript/styles/mastodon/forms.scss
@@ -87,6 +87,10 @@ code {
       align-items: flex-start;
     }
 
+    &.file .label_input {
+      flex-wrap: nowrap;
+    }
+
     &.select .label_input {
       align-items: initial;
     }
diff --git a/app/models/account.rb b/app/models/account.rb
index 4467d1512a..2b3ef5cdc1 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -45,6 +45,7 @@
 #  moved_to_account_id     :bigint(8)
 #  featured_collection_url :string
 #  fields                  :jsonb
+#  actor_type              :string
 #
 
 class Account < ApplicationRecord
@@ -149,6 +150,16 @@ class Account < ApplicationRecord
     moved_to_account_id.present?
   end
 
+  def bot?
+    %w(Application Service).include? actor_type
+  end
+
+  alias bot bot?
+
+  def bot=(val)
+    self.actor_type = ActiveModel::Type::Boolean.new.cast(val) ? 'Service' : 'Person'
+  end
+
   def acct
     local? ? username : "#{username}@#{domain}"
   end
diff --git a/app/serializers/activitypub/actor_serializer.rb b/app/serializers/activitypub/actor_serializer.rb
index fcf3bdf175..41c9aa44e8 100644
--- a/app/serializers/activitypub/actor_serializer.rb
+++ b/app/serializers/activitypub/actor_serializer.rb
@@ -37,7 +37,7 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer
   end
 
   def type
-    'Person'
+    object.bot? ? 'Service' : 'Person'
   end
 
   def following
diff --git a/app/serializers/rest/account_serializer.rb b/app/serializers/rest/account_serializer.rb
index 8761bbb5e7..6adcd70390 100644
--- a/app/serializers/rest/account_serializer.rb
+++ b/app/serializers/rest/account_serializer.rb
@@ -3,7 +3,7 @@
 class REST::AccountSerializer < ActiveModel::Serializer
   include RoutingHelper
 
-  attributes :id, :username, :acct, :display_name, :locked, :created_at,
+  attributes :id, :username, :acct, :display_name, :locked, :bot, :created_at,
              :note, :url, :avatar, :avatar_static, :header, :header_static,
              :followers_count, :following_count, :statuses_count
 
diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb
index f67ebb443a..cc416b6717 100644
--- a/app/services/activitypub/process_account_service.rb
+++ b/app/services/activitypub/process_account_service.rb
@@ -71,6 +71,7 @@ class ActivityPub::ProcessAccountService < BaseService
     @account.note                    = @json['summary'] || ''
     @account.locked                  = @json['manuallyApprovesFollowers'] || false
     @account.fields                  = property_values || {}
+    @account.actor_type              = @json['type']
   end
 
   def set_fetchable_attributes!
diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml
index 13dcaf616a..4098d6778a 100644
--- a/app/views/accounts/_header.html.haml
+++ b/app/views/accounts/_header.html.haml
@@ -10,7 +10,11 @@
         %span>< @#{account.local_username_and_domain}
         = fa_icon('lock') if account.locked?
 
-    - if Setting.show_staff_badge
+    - if account.bot?
+      .roles
+        .account-role.bot
+          = t 'accounts.roles.bot'
+    - elsif Setting.show_staff_badge
       - if account.user_admin?
         .roles
           .account-role.admin
diff --git a/app/views/settings/profiles/show.html.haml b/app/views/settings/profiles/show.html.haml
index f28834d72e..a84f8a7da7 100644
--- a/app/views/settings/profiles/show.html.haml
+++ b/app/views/settings/profiles/show.html.haml
@@ -19,6 +19,9 @@
   .fields-group
     = f.input :locked, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.locked')
 
+  .fields-group
+    = f.input :bot, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.bot')
+
   .fields-group
     .input.with_block_label
       %label= t('simple_form.labels.defaults.fields')
diff --git a/config/locales/en.yml b/config/locales/en.yml
index e18354eac4..5369311ac5 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -49,6 +49,7 @@ en:
     reserved_username: The username is reserved
     roles:
       admin: Admin
+      bot: Bot
       moderator: Mod
     unfollow: Unfollow
   admin:
diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml
index 7ba32906d4..495c6166b4 100644
--- a/config/locales/simple_form.en.yml
+++ b/config/locales/simple_form.en.yml
@@ -4,6 +4,7 @@ en:
     hints:
       defaults:
         avatar: PNG, GIF or JPG. At most 2MB. Will be downscaled to 400x400px
+        bot: Warns people that the account does not represent a person
         digest: Only sent after a long period of inactivity and only if you have received any personal messages in your absence
         display_name:
           one: <span class="name-counter">1</span> character left
@@ -29,6 +30,7 @@ en:
           value: Content
       defaults:
         avatar: Avatar
+        bot: This is a bot account
         confirm_new_password: Confirm new password
         confirm_password: Confirm password
         current_password: Current password
diff --git a/db/migrate/20180506221944_add_actor_type_to_accounts.rb b/db/migrate/20180506221944_add_actor_type_to_accounts.rb
new file mode 100644
index 0000000000..7cfed640f7
--- /dev/null
+++ b/db/migrate/20180506221944_add_actor_type_to_accounts.rb
@@ -0,0 +1,5 @@
+class AddActorTypeToAccounts < ActiveRecord::Migration[5.2]
+  def change
+    add_column :accounts, :actor_type, :string
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 566a320d87..f7fa24b835 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 2018_04_16_210259) do
+ActiveRecord::Schema.define(version: 2018_05_06_221944) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -75,6 +75,7 @@ ActiveRecord::Schema.define(version: 2018_04_16_210259) do
     t.bigint "moved_to_account_id"
     t.string "featured_collection_url"
     t.jsonb "fields"
+    t.string "actor_type"
     t.index "(((setweight(to_tsvector('simple'::regconfig, (display_name)::text), 'A'::\"char\") || setweight(to_tsvector('simple'::regconfig, (username)::text), 'B'::\"char\")) || setweight(to_tsvector('simple'::regconfig, (COALESCE(domain, ''::character varying))::text), 'C'::\"char\")))", name: "search_index", using: :gin
     t.index "lower((username)::text), lower((domain)::text)", name: "index_accounts_on_username_and_domain_lower"
     t.index ["uri"], name: "index_accounts_on_uri"

From 6208ea5a531e8ece85ec29dd42dbc051e34a29e4 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Mon, 7 May 2018 14:49:13 +0200
Subject: [PATCH 27/40] If an OStatus message contains nsfw hashtag, mark it as
 sensitive (#7398)

* If an OStatus message contains nsfw hashtag, mark it as sensitive

Undo parts of #7048

* Put nsfw hashtag on OStatus messages if they have any media

* Fix code style issues
---
 app/lib/ostatus/activity/creation.rb | 8 +++++++-
 app/lib/ostatus/atom_serializer.rb   | 1 +
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/app/lib/ostatus/activity/creation.rb b/app/lib/ostatus/activity/creation.rb
index 1e7f470299..dbccc83302 100644
--- a/app/lib/ostatus/activity/creation.rb
+++ b/app/lib/ostatus/activity/creation.rb
@@ -46,7 +46,8 @@ class OStatus::Activity::Creation < OStatus::Activity::Base
         visibility: visibility_scope,
         conversation: find_or_create_conversation,
         thread: thread? ? find_status(thread.first) || find_activitypub_status(thread.first, thread.second) : nil,
-        media_attachment_ids: media_attachments.map(&:id)
+        media_attachment_ids: media_attachments.map(&:id),
+        sensitive: sensitive?
       )
 
       save_mentions(status)
@@ -105,6 +106,11 @@ class OStatus::Activity::Creation < OStatus::Activity::Base
 
   private
 
+  def sensitive?
+    # OStatus-specific convention (not standard)
+    @xml.xpath('./xmlns:category', xmlns: OStatus::TagManager::XMLNS).any? { |category| category['term'] == 'nsfw' }
+  end
+
   def find_or_create_conversation
     uri = @xml.at_xpath('./ostatus:conversation', ostatus: OStatus::TagManager::OS_XMLNS)&.attribute('ref')&.content
     return if uri.nil?
diff --git a/app/lib/ostatus/atom_serializer.rb b/app/lib/ostatus/atom_serializer.rb
index 7c66f2066e..698f2ee229 100644
--- a/app/lib/ostatus/atom_serializer.rb
+++ b/app/lib/ostatus/atom_serializer.rb
@@ -368,6 +368,7 @@ class OStatus::AtomSerializer
       append_element(entry, 'link', nil, rel: :enclosure, type: media.file_content_type, length: media.file_file_size, href: full_asset_url(media.file.url(:original, false)))
     end
 
+    append_element(entry, 'category', nil, term: 'nsfw') if status.sensitive? && status.media_attachments.any?
     append_element(entry, 'mastodon:scope', status.visibility)
 
     status.emojis.each do |emoji|

From 993e68a7dd40a3fcb8312ee13f873da278e9e3fa Mon Sep 17 00:00:00 2001
From: ThibG <thib@sitedethib.com>
Date: Tue, 8 May 2018 03:36:59 +0200
Subject: [PATCH 28/40] Fix hashtags not being federated on mentions (fixes
 #6900) (#7406)

---
 app/services/post_status_service.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb
index 3080fb2447..8a1a120c38 100644
--- a/app/services/post_status_service.rb
+++ b/app/services/post_status_service.rb
@@ -35,8 +35,8 @@ class PostStatusService < BaseService
                                         application: options[:application])
     end
 
-    process_mentions_service.call(status)
     process_hashtags_service.call(status)
+    process_mentions_service.call(status)
 
     LinkCrawlWorker.perform_async(status.id) unless status.spoiler_text?
     DistributionWorker.perform_async(status.id)

From b1938d7853f1d52f9bd99244c550cc609dd81202 Mon Sep 17 00:00:00 2001
From: ThibG <thib@sitedethib.com>
Date: Tue, 8 May 2018 13:28:55 +0200
Subject: [PATCH 29/40] Do not crash in getStatusIds when there is a gap in the
 timeline (fixes #7400) (#7415)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Fixes a crash occurring when the “gap” disconnection indicator is to be
displayed in a filtered timeline.
---
 .../mastodon/features/ui/containers/status_list_container.js    | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/app/javascript/mastodon/features/ui/containers/status_list_container.js b/app/javascript/mastodon/features/ui/containers/status_list_container.js
index 4efacda65e..e5b1edc4a0 100644
--- a/app/javascript/mastodon/features/ui/containers/status_list_container.js
+++ b/app/javascript/mastodon/features/ui/containers/status_list_container.js
@@ -21,6 +21,8 @@ const makeGetStatusIds = () => createSelector([
   }
 
   return statusIds.filter(id => {
+    if (id === null) return true;
+
     const statusForId = statuses.get(id);
     let showStatus    = true;
 

From 01dfd6dbc8905dfaa5ab81fb9b38e52079e5ed7f Mon Sep 17 00:00:00 2001
From: Surinna Curtis <ekiru.0@gmail.com>
Date: Tue, 8 May 2018 06:30:04 -0500
Subject: [PATCH 30/40] Take the first recognized actor_type. (#7410)

---
 app/services/activitypub/process_account_service.rb | 10 +++++++++-
 spec/services/resolve_account_service_spec.rb       |  1 +
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb
index cc416b6717..721c9c9283 100644
--- a/app/services/activitypub/process_account_service.rb
+++ b/app/services/activitypub/process_account_service.rb
@@ -71,7 +71,7 @@ class ActivityPub::ProcessAccountService < BaseService
     @account.note                    = @json['summary'] || ''
     @account.locked                  = @json['manuallyApprovesFollowers'] || false
     @account.fields                  = property_values || {}
-    @account.actor_type              = @json['type']
+    @account.actor_type              = actor_type
   end
 
   def set_fetchable_attributes!
@@ -96,6 +96,14 @@ class ActivityPub::ProcessAccountService < BaseService
     ActivityPub::SynchronizeFeaturedCollectionWorker.perform_async(@account.id)
   end
 
+  def actor_type
+    if @json['type'].is_a?(Array)
+      @json['type'].find { |type| ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES.include?(type) }
+    else
+      @json['type']
+    end
+  end
+
   def image_url(key)
     value = first_of_value(@json[key])
 
diff --git a/spec/services/resolve_account_service_spec.rb b/spec/services/resolve_account_service_spec.rb
index f4c810f758..dd7561587d 100644
--- a/spec/services/resolve_account_service_spec.rb
+++ b/spec/services/resolve_account_service_spec.rb
@@ -116,6 +116,7 @@ RSpec.describe ResolveAccountService, type: :service do
         expect(account.activitypub?).to eq true
         expect(account.domain).to eq 'ap.example.com'
         expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox'
+        expect(account.actor_type).to eq 'Person'
       end
     end
 

From e0b1e17bd04d7f9b533ab462aee4544a5f5fb926 Mon Sep 17 00:00:00 2001
From: Yamagishi Kazutoshi <ykzts@desire.sh>
Date: Tue, 8 May 2018 20:33:09 +0900
Subject: [PATCH 31/40] Show media modal on public timeline (#7413)

---
 app/javascript/mastodon/components/status.js         |  2 +-
 .../mastodon/containers/timeline_container.js        | 12 ++++++++++--
 .../mastodon/features/ui/components/modal_root.js    | 11 +++++++++++
 app/views/about/show.html.haml                       |  2 ++
 app/views/tags/show.html.haml                        |  2 ++
 5 files changed, 26 insertions(+), 3 deletions(-)

diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js
index 402d558c4c..953d98c20d 100644
--- a/app/javascript/mastodon/components/status.js
+++ b/app/javascript/mastodon/components/status.js
@@ -206,7 +206,7 @@ export default class Status extends ImmutablePureComponent {
         );
       } else {
         media = (
-          <Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery} >
+          <Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery}>
             {Component => <Component media={status.get('media_attachments')} sensitive={status.get('sensitive')} height={110} onOpenMedia={this.props.onOpenMedia} />}
           </Bundle>
         );
diff --git a/app/javascript/mastodon/containers/timeline_container.js b/app/javascript/mastodon/containers/timeline_container.js
index 8719bb5c9e..a1a4bd024b 100644
--- a/app/javascript/mastodon/containers/timeline_container.js
+++ b/app/javascript/mastodon/containers/timeline_container.js
@@ -1,4 +1,5 @@
-import React from 'react';
+import React, { Fragment } from 'react';
+import ReactDOM from 'react-dom';
 import { Provider } from 'react-redux';
 import PropTypes from 'prop-types';
 import configureStore from '../store/configureStore';
@@ -8,6 +9,7 @@ import { getLocale } from '../locales';
 import PublicTimeline from '../features/standalone/public_timeline';
 import CommunityTimeline from '../features/standalone/community_timeline';
 import HashtagTimeline from '../features/standalone/hashtag_timeline';
+import ModalContainer from '../features/ui/containers/modal_container';
 import initialState from '../initial_state';
 
 const { localeData, messages } = getLocale();
@@ -47,7 +49,13 @@ export default class TimelineContainer extends React.PureComponent {
     return (
       <IntlProvider locale={locale} messages={messages}>
         <Provider store={store}>
-          {timeline}
+          <Fragment>
+            {timeline}
+            {ReactDOM.createPortal(
+              <ModalContainer />,
+              document.getElementById('modal-container'),
+            )}
+          </Fragment>
         </Provider>
       </IntlProvider>
     );
diff --git a/app/javascript/mastodon/features/ui/components/modal_root.js b/app/javascript/mastodon/features/ui/components/modal_root.js
index 4185cba322..a334318ce5 100644
--- a/app/javascript/mastodon/features/ui/components/modal_root.js
+++ b/app/javascript/mastodon/features/ui/components/modal_root.js
@@ -40,6 +40,17 @@ export default class ModalRoot extends React.PureComponent {
     onClose: PropTypes.func.isRequired,
   };
 
+  getSnapshotBeforeUpdate () {
+    const visible = !!this.props.type;
+    return {
+      overflowY: visible ? 'hidden' : null,
+    };
+  }
+
+  componentDidUpdate (prevProps, prevState, { overflowY }) {
+    document.body.style.overflowY = overflowY;
+  }
+
   renderLoading = modalId => () => {
     return ['MEDIA', 'VIDEO', 'BOOST', 'CONFIRM', 'ACTIONS'].indexOf(modalId) === -1 ? <ModalLoading /> : null;
   }
diff --git a/app/views/about/show.html.haml b/app/views/about/show.html.haml
index 870dafdf52..fba46d54b7 100644
--- a/app/views/about/show.html.haml
+++ b/app/views/about/show.html.haml
@@ -142,3 +142,5 @@
             %p
               = link_to t('about.source_code'), @instance_presenter.source_url
               = " (#{@instance_presenter.version_number})"
+
+#modal-container
diff --git a/app/views/tags/show.html.haml b/app/views/tags/show.html.haml
index f8cdc99523..d46f359992 100644
--- a/app/views/tags/show.html.haml
+++ b/app/views/tags/show.html.haml
@@ -34,3 +34,5 @@
               %p= t 'about.about_mastodon_html'
 
               = render 'features'
+
+#modal-container

From 40097f438b2b23a7a496b4b7dc96e188c196f261 Mon Sep 17 00:00:00 2001
From: Yamagishi Kazutoshi <ykzts@desire.sh>
Date: Tue, 8 May 2018 20:35:33 +0900
Subject: [PATCH 32/40] Multiple Issue templates (#7402)

* Multiple Issue templates

* Update bug_report.md
---
 .../bug_report.md}                                    |  6 ++++++
 .github/ISSUE_TEMPLATE/feature_request.md             | 11 +++++++++++
 2 files changed, 17 insertions(+)
 rename .github/{ISSUE_TEMPLATE.md => ISSUE_TEMPLATE/bug_report.md} (80%)
 create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md

diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE/bug_report.md
similarity index 80%
rename from .github/ISSUE_TEMPLATE.md
rename to .github/ISSUE_TEMPLATE/bug_report.md
index c78bcb492a..602530db0e 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -1,3 +1,9 @@
+---
+name: Bug Report
+about: Create a report to help us improve
+
+---
+
 [Issue text goes here].
 
 * * * *
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000000..46602fd2c0
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,11 @@
+---
+name: Feature Request
+about: Suggest an idea for this project
+
+---
+
+[Issue text goes here].
+
+* * * *
+
+- [ ] I searched or browsed the repo’s other issues to ensure this is not a duplicate.

From d2ee48977c8b663465504b44ae3d648cadd29aab Mon Sep 17 00:00:00 2001
From: Yamagishi Kazutoshi <ykzts@desire.sh>
Date: Wed, 9 May 2018 15:39:08 +0900
Subject: [PATCH 33/40] Rescue Mastodon::LengthValidationError in
 FetchLinkCardService (#7424)

---
 app/services/fetch_link_card_service.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/services/fetch_link_card_service.rb b/app/services/fetch_link_card_service.rb
index 77d4aa5381..f9b1b2f0c6 100644
--- a/app/services/fetch_link_card_service.rb
+++ b/app/services/fetch_link_card_service.rb
@@ -27,7 +27,7 @@ class FetchLinkCardService < BaseService
     end
 
     attach_card if @card&.persisted?
-  rescue HTTP::Error, Addressable::URI::InvalidURIError => e
+  rescue HTTP::Error, Addressable::URI::InvalidURIError, Mastodon::LengthValidationError => e
     Rails.logger.debug "Error fetching link #{@url}: #{e}"
     nil
   end

From 6832110af46e31831788c599b70ac047c7512e16 Mon Sep 17 00:00:00 2001
From: Yamagishi Kazutoshi <ykzts@desire.sh>
Date: Wed, 9 May 2018 15:41:07 +0900
Subject: [PATCH 34/40] Correct rotate of image using EXIF (#7422)

---
 app/javascript/mastodon/utils/resize_image.js | 107 ++++++++++++++----
 package.json                                  |   1 +
 yarn.lock                                     |   4 +
 3 files changed, 89 insertions(+), 23 deletions(-)

diff --git a/app/javascript/mastodon/utils/resize_image.js b/app/javascript/mastodon/utils/resize_image.js
index 6442eda38c..54459de3ec 100644
--- a/app/javascript/mastodon/utils/resize_image.js
+++ b/app/javascript/mastodon/utils/resize_image.js
@@ -1,3 +1,5 @@
+import EXIF from 'exif-js';
+
 const MAX_IMAGE_DIMENSION = 1280;
 
 const getImageUrl = inputFile => new Promise((resolve, reject) => {
@@ -28,6 +30,84 @@ const loadImage = inputFile => new Promise((resolve, reject) => {
   }).catch(reject);
 });
 
+const getOrientation = (img, type = 'image/png') => new Promise(resolve => {
+  if (type !== 'image/jpeg') {
+    resolve(1);
+    return;
+  }
+
+  EXIF.getData(img, () => {
+    const orientation = EXIF.getTag(img, 'Orientation');
+    resolve(orientation);
+  });
+});
+
+const processImage = (img, { width, height, orientation, type = 'image/png' }) => new Promise(resolve => {
+  const canvas  = document.createElement('canvas');
+  [canvas.width, canvas.height] = orientation < 5 ? [width, height] : [height, width];
+
+  const context = canvas.getContext('2d');
+
+  switch (orientation) {
+  case 2:
+    context.translate(width, 0);
+    break;
+  case 3:
+    context.translate(width, height);
+    break;
+  case 4:
+    context.translate(0, height);
+    break;
+  case 5:
+    context.rotate(0.5 * Math.PI);
+    context.translate(1, -1);
+    break;
+  case 6:
+    context.rotate(0.5 * Math.PI);
+    context.translate(0, -height);
+    break;
+  case 7:
+    context.rotate(0.5, Math.PI);
+    context.translate(width, -height);
+    break;
+  case 8:
+    context.rotate(-0.5, Math.PI);
+    context.translate(-width, 0);
+    break;
+  }
+
+  context.drawImage(img, 0, 0, width, height);
+
+  canvas.toBlob(resolve, type);
+});
+
+const resizeImage = (img, type = 'image/png') => new Promise((resolve, reject) => {
+  const { width, height } = img;
+
+  let newWidth, newHeight;
+
+  if (width > height) {
+    newHeight = height * MAX_IMAGE_DIMENSION / width;
+    newWidth  = MAX_IMAGE_DIMENSION;
+  } else if (height > width) {
+    newWidth  = width * MAX_IMAGE_DIMENSION / height;
+    newHeight = MAX_IMAGE_DIMENSION;
+  } else {
+    newWidth  = MAX_IMAGE_DIMENSION;
+    newHeight = MAX_IMAGE_DIMENSION;
+  }
+
+  getOrientation(img, type)
+    .then(orientation => processImage(img, {
+      width: newWidth,
+      height: newHeight,
+      orientation,
+      type,
+    }))
+    .then(resolve)
+    .catch(reject);
+});
+
 export default inputFile => new Promise((resolve, reject) => {
   if (!inputFile.type.match(/image.*/) || inputFile.type === 'image/gif') {
     resolve(inputFile);
@@ -35,32 +115,13 @@ export default inputFile => new Promise((resolve, reject) => {
   }
 
   loadImage(inputFile).then(img => {
-    const canvas = document.createElement('canvas');
-    const { width, height } = img;
-
-    let newWidth, newHeight;
-
-    if (width < MAX_IMAGE_DIMENSION && height < MAX_IMAGE_DIMENSION) {
+    if (img.width < MAX_IMAGE_DIMENSION && img.height < MAX_IMAGE_DIMENSION) {
       resolve(inputFile);
       return;
     }
 
-    if (width > height) {
-      newHeight = height * MAX_IMAGE_DIMENSION / width;
-      newWidth  = MAX_IMAGE_DIMENSION;
-    } else if (height > width) {
-      newWidth  = width * MAX_IMAGE_DIMENSION / height;
-      newHeight = MAX_IMAGE_DIMENSION;
-    } else {
-      newWidth  = MAX_IMAGE_DIMENSION;
-      newHeight = MAX_IMAGE_DIMENSION;
-    }
-
-    canvas.width  = newWidth;
-    canvas.height = newHeight;
-
-    canvas.getContext('2d').drawImage(img, 0, 0, newWidth, newHeight);
-
-    canvas.toBlob(resolve, inputFile.type);
+    resizeImage(img, inputFile.type)
+      .then(resolve)
+      .catch(() => resolve(inputFile));
   }).catch(reject);
 });
diff --git a/package.json b/package.json
index 75e9e9f6dc..61f38409c0 100644
--- a/package.json
+++ b/package.json
@@ -49,6 +49,7 @@
     "emoji-mart": "Gargron/emoji-mart#build",
     "es6-symbol": "^3.1.1",
     "escape-html": "^1.0.3",
+    "exif-js": "^2.3.0",
     "express": "^4.16.2",
     "extract-text-webpack-plugin": "^3.0.2",
     "file-loader": "^0.11.2",
diff --git a/yarn.lock b/yarn.lock
index c5a49a497e..50c88557d2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2601,6 +2601,10 @@ execa@^0.7.0:
     signal-exit "^3.0.0"
     strip-eof "^1.0.0"
 
+exif-js@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/exif-js/-/exif-js-2.3.0.tgz#9d10819bf571f873813e7640241255ab9ce1a814"
+
 expand-brackets@^0.1.4:
   version "0.1.5"
   resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b"

From 35eff337d5482ac251e2f75031c5be2ab583e265 Mon Sep 17 00:00:00 2001
From: Shuhei Kitagawa <shuheiktgw@users.noreply.github.com>
Date: Wed, 9 May 2018 15:41:26 +0900
Subject: [PATCH 35/40] Add tests for admin/invites_controller (#7412)

---
 .../admin/invites_controller_spec.rb          | 43 +++++++++++++++++++
 1 file changed, 43 insertions(+)
 create mode 100644 spec/controllers/admin/invites_controller_spec.rb

diff --git a/spec/controllers/admin/invites_controller_spec.rb b/spec/controllers/admin/invites_controller_spec.rb
new file mode 100644
index 0000000000..e7d9954118
--- /dev/null
+++ b/spec/controllers/admin/invites_controller_spec.rb
@@ -0,0 +1,43 @@
+require 'rails_helper'
+
+describe Admin::InvitesController do
+  render_views
+
+  let(:user) { Fabricate(:user, admin: true) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    subject { get :index, params: { available: true } }
+
+    let!(:invite) { Fabricate(:invite) }
+
+    it 'renders index page' do
+      expect(subject).to render_template :index
+      expect(assigns(:invites)).to include invite
+    end
+  end
+
+  describe 'POST #create' do
+    subject { post :create, params: { invite: { max_uses: '10', expires_in: 1800 } } }
+
+    it 'succeeds to create a invite' do
+      expect{ subject }.to change { Invite.count }.by(1)
+      expect(subject).to redirect_to admin_invites_path
+      expect(Invite.last).to have_attributes(user_id: user.id, max_uses: 10)
+    end
+  end
+
+  describe 'DELETE #destroy' do
+    let!(:invite) { Fabricate(:invite, expires_at: nil) }
+
+    subject { delete :destroy, params: { id: invite.id } }
+
+    it 'expires invite' do
+      expect(subject).to redirect_to admin_invites_path
+      expect(invite.reload).to be_expired
+    end
+  end
+end

From ce35d81db73367a016ad019aadefba0a5ecb0d2d Mon Sep 17 00:00:00 2001
From: Shuhei Kitagawa <shuheiktgw@users.noreply.github.com>
Date: Wed, 9 May 2018 15:41:46 +0900
Subject: [PATCH 36/40] Add tests for admin/roles_controller (#7421)

---
 .../admin/roles_controller_spec.rb            | 33 +++++++++++++++++++
 1 file changed, 33 insertions(+)
 create mode 100644 spec/controllers/admin/roles_controller_spec.rb

diff --git a/spec/controllers/admin/roles_controller_spec.rb b/spec/controllers/admin/roles_controller_spec.rb
new file mode 100644
index 0000000000..8e0de73cbd
--- /dev/null
+++ b/spec/controllers/admin/roles_controller_spec.rb
@@ -0,0 +1,33 @@
+require 'rails_helper'
+
+describe Admin::RolesController do
+  render_views
+
+  let(:admin) { Fabricate(:user, admin: true) }
+
+  before do
+    sign_in admin, scope: :user
+  end
+
+  describe 'POST #promote' do
+    subject { post :promote, params: { account_id: user.account_id } }
+
+    let(:user) { Fabricate(:user, moderator: false, admin: false) }
+
+    it 'promotes user' do
+      expect(subject).to redirect_to admin_account_path(user.account_id)
+      expect(user.reload).to be_moderator
+    end
+  end
+
+  describe 'POST #demote' do
+    subject { post :demote, params: { account_id: user.account_id } }
+
+    let(:user) { Fabricate(:user, moderator: true, admin: false) }
+
+    it 'demotes user' do
+      expect(subject).to redirect_to admin_account_path(user.account_id)
+      expect(user.reload).not_to be_moderator
+    end
+  end
+end

From dc010cc77a1e34a1f65b9a522b299bac6aa44329 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20Miko=C5=82ajczak?= <me@m4sk.in>
Date: Wed, 9 May 2018 12:20:49 +0200
Subject: [PATCH 37/40] =?UTF-8?q?=F0=9F=8C=8D:=20=F0=9F=87=B5=F0=9F=87=B1?=
 =?UTF-8?q?=E2=AC=86=EF=B8=8F=20(#7427)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Marcin Mikołajczak <me@m4sk.in>
---
 config/locales/pl.yml             | 14 +++++++-------
 config/locales/simple_form.pl.yml |  4 +++-
 2 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/config/locales/pl.yml b/config/locales/pl.yml
index 0d107ed53a..8133a41744 100644
--- a/config/locales/pl.yml
+++ b/config/locales/pl.yml
@@ -53,7 +53,7 @@ pl:
     unfollow: Przestań śledzić
   admin:
     account_moderation_notes:
-      create: Dodaj
+      create: Pozostaw notatkę
       created_msg: Pomyślnie dodano notatkę moderacyjną!
       delete: Usuń
       destroyed_msg: Pomyślnie usunięto notatkę moderacyjną!
@@ -70,7 +70,7 @@ pl:
         title: Zmień adres e-mail dla %{username}
       confirm: Potwierdź
       confirmed: Potwierdzono
-      confirming: Potwierdzam
+      confirming: Potwierdzanie
       demote: Degraduj
       disable: Dezaktywuj
       disable_two_factor_authentication: Wyłącz uwierzytelnianie dwuetapowe
@@ -79,7 +79,7 @@ pl:
       domain: Domena
       edit: Edytuj
       email: Adres e-mail
-      email_status: Status e-maila
+      email_status: Stan e-maila
       enable: Aktywuj
       enabled: Aktywowano
       feed_url: Adres kanału
@@ -119,8 +119,8 @@ pl:
       redownload: Odśwież awatar
       remove_avatar: Usun awatar
       resend_confirmation:
-        already_confirmed: Ten użytkownik jest już potwierdzony
-        send: Wyślij ponownie email potwierdzający
+        already_confirmed: To konto zostało już potwierdzone
+        send: Wyślij ponownie e-mail z potwierdzeniem
         success: E-mail z potwierdzeniem został wysłany!
       reset: Resetuj
       reset_password: Resetuj hasło
@@ -374,7 +374,7 @@ pl:
   admin_mailer:
     new_report:
       body: Użytkownik %{reporter} zgłosił %{target}
-      body_remote: Ktoś z %{domain} zgłosił %{target}
+      body_remote: Użytkownik instancji %{domain} zgłosił %{target}
       subject: Nowe zgłoszenie na %{instance} (#%{id})
   application_mailer:
     notification_preferences: Zmień ustawienia e-maili
@@ -464,7 +464,7 @@ pl:
     archive_takeout:
       date: Data
       download: Pobierz swoje archiwum
-      hint_html: Możesz uzyskać archiwum swoich <strong>wpisów i wysłanej zawartości multimedialnej</strong>. Wyeksportowane dane będą dostępne w formacie ActivityPub, który możesz otworzyć w obsługujących go programach.
+      hint_html: Możesz uzyskać archiwum swoich <strong>wpisów i wysłanej zawartości multimedialnej</strong>. Wyeksportowane dane będą dostępne w formacie ActivityPub, który możesz otworzyć w obsługujących go programach. Możesz wyeksportować je po 7 dniach od poprzedniego eksportu.
       in_progress: Tworzenie archiwum…
       request: Uzyskaj archiwum
       size: Rozmiar
diff --git a/config/locales/simple_form.pl.yml b/config/locales/simple_form.pl.yml
index 4c1833b1ca..9341fc9e59 100644
--- a/config/locales/simple_form.pl.yml
+++ b/config/locales/simple_form.pl.yml
@@ -4,6 +4,7 @@ pl:
     hints:
       defaults:
         avatar: PNG, GIF lub JPG. Maksymalnie 2MB. Zostanie zmniejszony do 400x400px
+        bot: Informuje użytkowników, że konto nie jest prowadzone przez człowieka
         digest: Wysyłane tylko po długiej nieaktywności, jeżeli w tym czasie otrzymaleś jakąś wiadomość bezpośrednią
         display_name:
           few: Pozostały <span class="name-counter">%{count}</span> znaki.
@@ -23,7 +24,7 @@ pl:
       imports:
         data: Plik CSV wyeksportowany z innej instancji Mastodona
       sessions:
-        otp: Wprowadź kod weryfikacji dwuetapowej z telefonu lub wykorzystaj jeden z kodów zapasowych.
+        otp: 'Wprowadź kod weryfikacji dwuetapowej z telefonu lub wykorzystaj jeden z kodów zapasowych:'
       user:
         filtered_languages: Wpisy w wybranych językach nie będą wyświetlać się na publicznych osiach czasu
     labels:
@@ -33,6 +34,7 @@ pl:
           value: Zawartość
       defaults:
         avatar: Awatar
+        bot: To konto jest prowadzone przez bota
         confirm_new_password: Potwierdź nowe hasło
         confirm_password: Potwierdź hasło
         current_password: Obecne hasło

From 16fee0335f2b10b0ce54f71965d2f2acc0e24942 Mon Sep 17 00:00:00 2001
From: Yamagishi Kazutoshi <ykzts@desire.sh>
Date: Wed, 9 May 2018 23:22:52 +0900
Subject: [PATCH 38/40] Show card modal on public pages (#7428)

---
 .../mastodon/containers/card_container.js     | 18 ------
 .../mastodon/containers/cards_container.js    | 59 +++++++++++++++++++
 app/javascript/packs/public.js                | 15 +++--
 .../styles/mastodon/containers.scss           |  1 +
 4 files changed, 70 insertions(+), 23 deletions(-)
 delete mode 100644 app/javascript/mastodon/containers/card_container.js
 create mode 100644 app/javascript/mastodon/containers/cards_container.js

diff --git a/app/javascript/mastodon/containers/card_container.js b/app/javascript/mastodon/containers/card_container.js
deleted file mode 100644
index 11b9f88d4c..0000000000
--- a/app/javascript/mastodon/containers/card_container.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Card from '../features/status/components/card';
-import { fromJS } from 'immutable';
-
-export default class CardContainer extends React.PureComponent {
-
-  static propTypes = {
-    locale: PropTypes.string,
-    card: PropTypes.array.isRequired,
-  };
-
-  render () {
-    const { card, ...props } = this.props;
-    return <Card card={fromJS(card)} {...props} />;
-  }
-
-}
diff --git a/app/javascript/mastodon/containers/cards_container.js b/app/javascript/mastodon/containers/cards_container.js
new file mode 100644
index 0000000000..894bf4ef99
--- /dev/null
+++ b/app/javascript/mastodon/containers/cards_container.js
@@ -0,0 +1,59 @@
+import React, { Fragment } from 'react';
+import ReactDOM from 'react-dom';
+import PropTypes from 'prop-types';
+import { IntlProvider, addLocaleData } from 'react-intl';
+import { getLocale } from '../locales';
+import Card from '../features/status/components/card';
+import ModalRoot from '../components/modal_root';
+import MediaModal from '../features/ui/components/media_modal';
+import { fromJS } from 'immutable';
+
+const { localeData, messages } = getLocale();
+addLocaleData(localeData);
+
+export default class CardsContainer extends React.PureComponent {
+
+  static propTypes = {
+    locale: PropTypes.string,
+    cards: PropTypes.object.isRequired,
+  };
+
+  state = {
+    media: null,
+  };
+
+  handleOpenCard = (media) => {
+    document.body.classList.add('card-standalone__body');
+    this.setState({ media });
+  }
+
+  handleCloseCard = () => {
+    document.body.classList.remove('card-standalone__body');
+    this.setState({ media: null });
+  }
+
+  render () {
+    const { locale, cards } = this.props;
+
+    return (
+      <IntlProvider locale={locale} messages={messages}>
+        <Fragment>
+          {[].map.call(cards, container => {
+            const { card, ...props } = JSON.parse(container.getAttribute('data-props'));
+
+            return ReactDOM.createPortal(
+              <Card card={fromJS(card)} onOpenMedia={this.handleOpenCard} {...props} />,
+              container,
+            );
+          })}
+          <ModalRoot onClose={this.handleCloseCard}>
+            {this.state.media && (
+              <MediaModal media={this.state.media} index={0} onClose={this.handleCloseCard} />
+            )}
+          </ModalRoot>
+        </Fragment>
+      </IntlProvider>
+    );
+  }
+
+}
diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js
index 7096b9b4f2..3a1f1a16b4 100644
--- a/app/javascript/packs/public.js
+++ b/app/javascript/packs/public.js
@@ -25,7 +25,6 @@ function main() {
   const { getLocale } = require('../mastodon/locales');
   const { localeData } = getLocale();
   const VideoContainer = require('../mastodon/containers/video_container').default;
-  const CardContainer = require('../mastodon/containers/card_container').default;
   const React = require('react');
   const ReactDOM = require('react-dom');
 
@@ -75,10 +74,16 @@ function main() {
       ReactDOM.render(<VideoContainer locale={locale} {...props} />, content);
     });
 
-    [].forEach.call(document.querySelectorAll('[data-component="Card"]'), (content) => {
-      const props = JSON.parse(content.getAttribute('data-props'));
-      ReactDOM.render(<CardContainer locale={locale} {...props} />, content);
-    });
+    const cards = document.querySelectorAll('[data-component="Card"]');
+
+    if (cards.length > 0) {
+      import(/* webpackChunkName: "containers/cards_container" */ '../mastodon/containers/cards_container').then(({ default: CardsContainer }) => {
+        const content = document.createElement('div');
+
+        ReactDOM.render(<CardsContainer locale={locale} cards={cards} />, content);
+        document.body.appendChild(content);
+      }).catch(error => console.error(error));
+    }
 
     const mediaGalleries = document.querySelectorAll('[data-component="MediaGallery"]');
 
diff --git a/app/javascript/styles/mastodon/containers.scss b/app/javascript/styles/mastodon/containers.scss
index 9d5ab66a48..c40b38a5a5 100644
--- a/app/javascript/styles/mastodon/containers.scss
+++ b/app/javascript/styles/mastodon/containers.scss
@@ -60,6 +60,7 @@
   }
 }
 
+.card-standalone__body,
 .media-gallery-standalone__body {
   overflow: hidden;
 }

From edf882320a12b2c0cfa7fc0fb7eafaa39021cb0f Mon Sep 17 00:00:00 2001
From: Yamagishi Kazutoshi <ykzts@desire.sh>
Date: Wed, 9 May 2018 23:59:39 +0900
Subject: [PATCH 39/40] Support gzip encoding on Request (#7425)

---
 app/lib/request.rb | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/app/lib/request.rb b/app/lib/request.rb
index 00f94dacf5..fc7d398e04 100644
--- a/app/lib/request.rb
+++ b/app/lib/request.rb
@@ -51,7 +51,7 @@ class Request
   end
 
   def headers
-    (@account ? @headers.merge('Signature' => signature) : @headers).without(REQUEST_TARGET)
+    (@account ? @headers.merge('Signature' => signature) : @headers).reverse_merge('Accept-Encoding' => 'gzip').without(REQUEST_TARGET)
   end
 
   private
@@ -100,7 +100,7 @@ class Request
   end
 
   def http_client
-    @http_client ||= HTTP.timeout(:per_operation, timeout).follow(max_hops: 2)
+    @http_client ||= HTTP.use(:auto_inflate).timeout(:per_operation, timeout).follow(max_hops: 2)
   end
 
   def use_proxy?

From ac788ad47e32a3cf84a46ac87f84f376185cdad4 Mon Sep 17 00:00:00 2001
From: Yamagishi Kazutoshi <ykzts@desire.sh>
Date: Wed, 9 May 2018 23:59:58 +0900
Subject: [PATCH 40/40] Disable simplecov on CircleCI (#7416)

* Disable simplecov on CircleCI

* Remove --format progress
---
 .circleci/config.yml |  1 +
 spec/spec_helper.rb  | 13 +++++++------
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/.circleci/config.yml b/.circleci/config.yml
index fc9c7e22b7..02b505457b 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -11,6 +11,7 @@ aliases:
           RAILS_ENV: test
           PARALLEL_TEST_PROCESSORS: 4
           ALLOW_NOPAM: true
+          DISABLE_SIMPLECOV: true
     working_directory: ~/projects/mastodon/
 
   - &attach_workspace
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index a0466dd4bf..0cd1f91d02 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,11 +1,12 @@
-require 'simplecov'
-
 GC.disable
 
-SimpleCov.start 'rails' do
-  add_group 'Services', 'app/services'
-  add_group 'Presenters', 'app/presenters'
-  add_group 'Validators', 'app/validators'
+if ENV['DISABLE_SIMPLECOV'] != 'true'
+  require 'simplecov'
+  SimpleCov.start 'rails' do
+    add_group 'Services', 'app/services'
+    add_group 'Presenters', 'app/presenters'
+    add_group 'Validators', 'app/validators'
+  end
 end
 
 gc_counter = -1