about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock19
-rw-r--r--app/controllers/api/base_controller.rb15
-rw-r--r--app/controllers/api/v1/accounts/lists_controller.rb20
-rw-r--r--app/controllers/api/v1/lists_controller.rb38
-rw-r--r--app/controllers/api/web/push_subscriptions_controller.rb2
-rw-r--r--app/controllers/concerns/rate_limit_headers.rb3
-rw-r--r--app/controllers/oauth/authorizations_controller.rb5
-rw-r--r--app/helpers/settings_helper.rb1
-rw-r--r--app/javascript/mastodon/actions/favourites.js6
-rw-r--r--app/javascript/mastodon/actions/push_notifications.js11
-rw-r--r--app/javascript/mastodon/features/favourited_statuses/index.js10
-rw-r--r--app/javascript/mastodon/features/status/components/card.js10
-rw-r--r--app/javascript/mastodon/features/ui/components/video_modal.js1
-rw-r--r--app/javascript/mastodon/features/video/index.js65
-rw-r--r--app/javascript/mastodon/locales/ar.json2
-rw-r--r--app/javascript/mastodon/locales/bg.json2
-rw-r--r--app/javascript/mastodon/locales/ca.json2
-rw-r--r--app/javascript/mastodon/locales/de.json2
-rw-r--r--app/javascript/mastodon/locales/en.json2
-rw-r--r--app/javascript/mastodon/locales/eo.json2
-rw-r--r--app/javascript/mastodon/locales/es.json2
-rw-r--r--app/javascript/mastodon/locales/fa.json2
-rw-r--r--app/javascript/mastodon/locales/fi.json2
-rw-r--r--app/javascript/mastodon/locales/fr.json40
-rw-r--r--app/javascript/mastodon/locales/gl.json259
-rw-r--r--app/javascript/mastodon/locales/he.json2
-rw-r--r--app/javascript/mastodon/locales/hr.json2
-rw-r--r--app/javascript/mastodon/locales/hu.json2
-rw-r--r--app/javascript/mastodon/locales/id.json2
-rw-r--r--app/javascript/mastodon/locales/io.json2
-rw-r--r--app/javascript/mastodon/locales/it.json2
-rw-r--r--app/javascript/mastodon/locales/ja.json2
-rw-r--r--app/javascript/mastodon/locales/ko.json2
-rw-r--r--app/javascript/mastodon/locales/nl.json22
-rw-r--r--app/javascript/mastodon/locales/no.json2
-rw-r--r--app/javascript/mastodon/locales/oc.json60
-rw-r--r--app/javascript/mastodon/locales/pt-BR.json32
-rw-r--r--app/javascript/mastodon/locales/pt.json2
-rw-r--r--app/javascript/mastodon/locales/ru.json2
-rw-r--r--app/javascript/mastodon/locales/sv.json2
-rw-r--r--app/javascript/mastodon/locales/th.json2
-rw-r--r--app/javascript/mastodon/locales/tr.json2
-rw-r--r--app/javascript/mastodon/locales/uk.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_gl.json2
-rw-r--r--app/javascript/mastodon/locales/zh-HK.json2
-rw-r--r--app/javascript/mastodon/locales/zh-TW.json2
-rw-r--r--app/javascript/mastodon/middleware/sounds.js2
-rw-r--r--app/javascript/mastodon/reducers/status_lists.js12
-rw-r--r--app/javascript/mastodon/settings.js46
-rw-r--r--app/javascript/mastodon/web_push_subscription.js32
-rw-r--r--app/javascript/styles/mastodon/components.scss78
-rw-r--r--app/lib/activitypub/activity/create.rb22
-rw-r--r--app/lib/ostatus/activity/creation.rb21
-rw-r--r--app/models/account.rb3
-rw-r--r--app/models/list.rb8
-rw-r--r--app/models/preview_card.rb2
-rw-r--r--app/models/tag.rb2
-rw-r--r--app/services/account_search_service.rb2
-rw-r--r--app/services/fetch_link_card_service.rb2
-rw-r--r--app/services/resolve_remote_account_service.rb2
-rw-r--r--app/validators/status_pin_validator.rb8
-rw-r--r--app/views/stream_entries/_detailed_status.html.haml20
-rw-r--r--app/views/stream_entries/show.html.haml3
-rw-r--r--app/views/tags/show.html.haml7
-rw-r--r--config/application.rb1
-rw-r--r--config/initializers/paperclip.rb47
-rw-r--r--config/initializers/rack_attack.rb57
-rw-r--r--config/locales/activerecord.ar.yml13
-rw-r--r--config/locales/ar.yml18
-rw-r--r--config/locales/de.yml31
-rw-r--r--config/locales/devise.fr.yml2
-rw-r--r--config/locales/devise.ja.yml10
-rw-r--r--config/locales/devise.no.yml6
-rw-r--r--config/locales/doorkeeper.ar.yml10
-rw-r--r--config/locales/doorkeeper.ja.yml16
-rw-r--r--config/locales/doorkeeper.nl.yml4
-rw-r--r--config/locales/doorkeeper.no.yml8
-rw-r--r--config/locales/doorkeeper.oc.yml6
-rw-r--r--config/locales/en.yml8
-rw-r--r--config/locales/fr.yml4
-rw-r--r--config/locales/ja.yml20
-rw-r--r--config/locales/nl.yml115
-rw-r--r--config/locales/no.yml36
-rw-r--r--config/locales/oc.yml58
-rw-r--r--config/locales/pt-BR.yml75
-rw-r--r--config/locales/simple_form.ar.yml9
-rw-r--r--config/locales/simple_form.ja.yml10
-rw-r--r--config/locales/simple_form.nl.yml24
-rw-r--r--config/locales/simple_form.oc.yml6
-rw-r--r--config/locales/zh-CN.yml29
-rw-r--r--config/routes.rb1
-rw-r--r--db/migrate/20171201000000_change_account_id_nonnullable_in_lists.rb7
-rw-r--r--db/schema.rb4
-rw-r--r--lib/mastodon/version.rb2
-rw-r--r--spec/controllers/api/v1/accounts/lists_controller_spec.rb23
-rw-r--r--spec/controllers/api/web/push_subscriptions_controller_spec.rb17
-rw-r--r--spec/controllers/concerns/rate_limit_headers_spec.rb2
-rw-r--r--spec/fabricators/preview_card_fabricator.rb4
99 files changed, 1203 insertions, 427 deletions
diff --git a/Gemfile b/Gemfile
index e43b1a256..f6acb431a 100644
--- a/Gemfile
+++ b/Gemfile
@@ -14,7 +14,7 @@ gem 'pg', '~> 0.20'
 gem 'pghero', '~> 1.7'
 gem 'dotenv-rails', '~> 2.2'
 
-gem 'fog-aws', '~> 1.4', require: false
+gem 'aws-sdk', '~> 2.10', require: false
 gem 'fog-core', '~> 1.45'
 gem 'fog-local', '~> 0.4', require: false
 gem 'fog-openstack', '~> 0.1', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index 5f050d031..febfb8561 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -57,6 +57,14 @@ GEM
       encryptor (~> 3.0.0)
     av (0.9.0)
       cocaine (~> 0.5.3)
+    aws-sdk (2.10.100)
+      aws-sdk-resources (= 2.10.100)
+    aws-sdk-core (2.10.100)
+      aws-sigv4 (~> 1.0)
+      jmespath (~> 1.0)
+    aws-sdk-resources (2.10.100)
+      aws-sdk-core (= 2.10.100)
+    aws-sigv4 (1.0.2)
     bcrypt (3.1.11)
     better_errors (2.4.0)
       coderay (>= 1.0.0)
@@ -152,11 +160,6 @@ GEM
       i18n (~> 0.5)
     fast_blank (1.0.0)
     ffi (1.9.18)
-    fog-aws (1.4.1)
-      fog-core (~> 1.38)
-      fog-json (~> 1.0)
-      fog-xml (~> 0.1)
-      ipaddress (~> 0.8)
     fog-core (1.45.0)
       builder
       excon (~> 0.58)
@@ -170,9 +173,6 @@ GEM
       fog-core (>= 1.40)
       fog-json (>= 1.0)
       ipaddress (>= 0.8)
-    fog-xml (0.1.3)
-      fog-core
-      nokogiri (>= 1.5.11, < 2.0.0)
     formatador (0.2.5)
     fuubar (2.2.0)
       rspec-core (~> 3.0)
@@ -228,6 +228,7 @@ GEM
     idn-ruby (0.1.0)
     ipaddress (0.8.3)
     iso-639 (0.2.8)
+    jmespath (1.3.1)
     json (2.1.0)
     json-ld (2.1.7)
       multi_json (~> 1.12)
@@ -544,6 +545,7 @@ DEPENDENCIES
   active_record_query_trace (~> 1.5)
   addressable (~> 2.5)
   annotate (~> 2.7)
+  aws-sdk (~> 2.10)
   better_errors (~> 2.4)
   binding_of_caller (~> 0.7)
   bootsnap
@@ -566,7 +568,6 @@ DEPENDENCIES
   fabrication (~> 2.18)
   faker (~> 1.7)
   fast_blank (~> 1.0)
-  fog-aws (~> 1.4)
   fog-core (~> 1.45)
   fog-local (~> 0.4)
   fog-openstack (~> 0.1)
diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb
index 7cfe8fe71..5983c0fbe 100644
--- a/app/controllers/api/base_controller.rb
+++ b/app/controllers/api/base_controller.rb
@@ -72,19 +72,4 @@ class Api::BaseController < ApplicationController
   def render_empty
     render json: {}, status: 200
   end
-
-  def set_maps(statuses) # rubocop:disable Style/AccessorMethodName
-    if current_account.nil?
-      @reblogs_map    = {}
-      @favourites_map = {}
-      @mutes_map      = {}
-      return
-    end
-
-    status_ids       = statuses.compact.flat_map { |s| [s.id, s.reblog_of_id] }.uniq
-    conversation_ids = statuses.compact.map(&:conversation_id).compact.uniq
-    @reblogs_map     = Status.reblogs_map(status_ids, current_account)
-    @favourites_map  = Status.favourites_map(status_ids, current_account)
-    @mutes_map       = Status.mutes_map(conversation_ids, current_account)
-  end
 end
diff --git a/app/controllers/api/v1/accounts/lists_controller.rb b/app/controllers/api/v1/accounts/lists_controller.rb
new file mode 100644
index 000000000..a7ba89ce2
--- /dev/null
+++ b/app/controllers/api/v1/accounts/lists_controller.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class Api::V1::Accounts::ListsController < Api::BaseController
+  before_action -> { doorkeeper_authorize! :read }
+  before_action :require_user!
+  before_action :set_account
+
+  respond_to :json
+
+  def index
+    @lists = @account.lists.where(account: current_account)
+    render json: @lists, each_serializer: REST::ListSerializer
+  end
+
+  private
+
+  def set_account
+    @account = Account.find(params[:account_id])
+  end
+end
diff --git a/app/controllers/api/v1/lists_controller.rb b/app/controllers/api/v1/lists_controller.rb
index 9437373bd..180a91d81 100644
--- a/app/controllers/api/v1/lists_controller.rb
+++ b/app/controllers/api/v1/lists_controller.rb
@@ -1,18 +1,14 @@
 # frozen_string_literal: true
 
 class Api::V1::ListsController < Api::BaseController
-  LISTS_LIMIT = 50
-
   before_action -> { doorkeeper_authorize! :read },    only: [:index, :show]
   before_action -> { doorkeeper_authorize! :write }, except: [:index, :show]
 
   before_action :require_user!
   before_action :set_list, except: [:index, :create]
 
-  after_action :insert_pagination_headers, only: :index
-
   def index
-    @lists = List.where(account: current_account).paginate_by_max_id(limit_param(LISTS_LIMIT), params[:max_id], params[:since_id])
+    @lists = List.where(account: current_account).all
     render json: @lists, each_serializer: REST::ListSerializer
   end
 
@@ -44,36 +40,4 @@ class Api::V1::ListsController < Api::BaseController
   def list_params
     params.permit(:title)
   end
-
-  def insert_pagination_headers
-    set_pagination_headers(next_path, prev_path)
-  end
-
-  def next_path
-    if records_continue?
-      api_v1_lists_url pagination_params(max_id: pagination_max_id)
-    end
-  end
-
-  def prev_path
-    unless @lists.empty?
-      api_v1_lists_url pagination_params(since_id: pagination_since_id)
-    end
-  end
-
-  def pagination_max_id
-    @lists.last.id
-  end
-
-  def pagination_since_id
-    @lists.first.id
-  end
-
-  def records_continue?
-    @lists.size == limit_param(LISTS_LIMIT)
-  end
-
-  def pagination_params(core_params)
-    params.permit(:limit).merge(core_params)
-  end
 end
diff --git a/app/controllers/api/web/push_subscriptions_controller.rb b/app/controllers/api/web/push_subscriptions_controller.rb
index d66237feb..52e250d02 100644
--- a/app/controllers/api/web/push_subscriptions_controller.rb
+++ b/app/controllers/api/web/push_subscriptions_controller.rb
@@ -28,6 +28,8 @@ class Api::Web::PushSubscriptionsController < Api::BaseController
       },
     }
 
+    data.deep_merge!(params[:data]) if params[:data]
+
     web_subscription = ::Web::PushSubscription.create!(
       endpoint: params[:subscription][:endpoint],
       key_p256dh: params[:subscription][:keys][:p256dh],
diff --git a/app/controllers/concerns/rate_limit_headers.rb b/app/controllers/concerns/rate_limit_headers.rb
index 36cb91075..b79c558d8 100644
--- a/app/controllers/concerns/rate_limit_headers.rb
+++ b/app/controllers/concerns/rate_limit_headers.rb
@@ -44,7 +44,8 @@ module RateLimitHeaders
   end
 
   def api_throttle_data
-    request.env['rack.attack.throttle_data']['api']
+    most_limited_type, = request.env['rack.attack.throttle_data'].min_by { |_, v| v[:limit] }
+    request.env['rack.attack.throttle_data'][most_limited_type]
   end
 
   def request_time
diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb
index e9cdf9fa8..eb977510b 100644
--- a/app/controllers/oauth/authorizations_controller.rb
+++ b/app/controllers/oauth/authorizations_controller.rb
@@ -5,6 +5,7 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
 
   before_action :store_current_location
   before_action :authenticate_resource_owner!
+  before_action :set_pack
 
   include Localized
 
@@ -13,4 +14,8 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
   def store_current_location
     store_location_for(:user, request.url)
   end
+
+  def set_pack
+    use_pack 'auth'
+  end
 end
diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb
index abce85812..1d4cb8a57 100644
--- a/app/helpers/settings_helper.rb
+++ b/app/helpers/settings_helper.rb
@@ -10,6 +10,7 @@ module SettingsHelper
     eo: 'Esperanto',
     es: 'Español',
     fa: 'فارسی',
+    gl: 'Galego',
     fi: 'Suomi',
     fr: 'Français',
     he: 'עברית',
diff --git a/app/javascript/mastodon/actions/favourites.js b/app/javascript/mastodon/actions/favourites.js
index 09ce51fce..93094c526 100644
--- a/app/javascript/mastodon/actions/favourites.js
+++ b/app/javascript/mastodon/actions/favourites.js
@@ -10,6 +10,10 @@ export const FAVOURITED_STATUSES_EXPAND_FAIL    = 'FAVOURITED_STATUSES_EXPAND_FA
 
 export function fetchFavouritedStatuses() {
   return (dispatch, getState) => {
+    if (getState().getIn(['status_lists', 'favourites', 'isLoading'])) {
+      return;
+    }
+
     dispatch(fetchFavouritedStatusesRequest());
 
     api(getState).get('/api/v1/favourites').then(response => {
@@ -46,7 +50,7 @@ export function expandFavouritedStatuses() {
   return (dispatch, getState) => {
     const url = getState().getIn(['status_lists', 'favourites', 'next'], null);
 
-    if (url === null) {
+    if (url === null || getState().getIn(['status_lists', 'favourites', 'isLoading'])) {
       return;
     }
 
diff --git a/app/javascript/mastodon/actions/push_notifications.js b/app/javascript/mastodon/actions/push_notifications.js
index 55661d2b0..de06385f9 100644
--- a/app/javascript/mastodon/actions/push_notifications.js
+++ b/app/javascript/mastodon/actions/push_notifications.js
@@ -1,4 +1,5 @@
 import axios from 'axios';
+import { pushNotificationsSetting } from '../settings';
 
 export const SET_BROWSER_SUPPORT = 'PUSH_NOTIFICATIONS_SET_BROWSER_SUPPORT';
 export const SET_SUBSCRIPTION = 'PUSH_NOTIFICATIONS_SET_SUBSCRIPTION';
@@ -42,11 +43,15 @@ export function saveSettings() {
     const state = getState().get('push_notifications');
     const subscription = state.get('subscription');
     const alerts = state.get('alerts');
+    const data = { alerts };
 
     axios.put(`/api/web/push_subscriptions/${subscription.get('id')}`, {
-      data: {
-        alerts,
-      },
+      data,
+    }).then(() => {
+      const me = getState().getIn(['meta', 'me']);
+      if (me) {
+        pushNotificationsSetting.set(me, data);
+      }
     });
   };
 }
diff --git a/app/javascript/mastodon/features/favourited_statuses/index.js b/app/javascript/mastodon/features/favourited_statuses/index.js
index 1e1f5873c..67b107bc8 100644
--- a/app/javascript/mastodon/features/favourited_statuses/index.js
+++ b/app/javascript/mastodon/features/favourited_statuses/index.js
@@ -9,6 +9,7 @@ import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
 import StatusList from '../../components/status_list';
 import { defineMessages, injectIntl } from 'react-intl';
 import ImmutablePureComponent from 'react-immutable-pure-component';
+import { debounce } from 'lodash';
 
 const messages = defineMessages({
   heading: { id: 'column.favourites', defaultMessage: 'Favourites' },
@@ -16,6 +17,7 @@ const messages = defineMessages({
 
 const mapStateToProps = state => ({
   statusIds: state.getIn(['status_lists', 'favourites', 'items']),
+  isLoading: state.getIn(['status_lists', 'favourites', 'isLoading'], true),
   hasMore: !!state.getIn(['status_lists', 'favourites', 'next']),
 });
 
@@ -30,6 +32,7 @@ export default class Favourites extends ImmutablePureComponent {
     columnId: PropTypes.string,
     multiColumn: PropTypes.bool,
     hasMore: PropTypes.bool,
+    isLoading: PropTypes.bool,
   };
 
   componentWillMount () {
@@ -59,12 +62,12 @@ export default class Favourites extends ImmutablePureComponent {
     this.column = c;
   }
 
-  handleScrollToBottom = () => {
+  handleScrollToBottom = debounce(() => {
     this.props.dispatch(expandFavouritedStatuses());
-  }
+  }, 300, { leading: true })
 
   render () {
-    const { intl, statusIds, columnId, multiColumn, hasMore } = this.props;
+    const { intl, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
     const pinned = !!columnId;
 
     return (
@@ -85,6 +88,7 @@ export default class Favourites extends ImmutablePureComponent {
           statusIds={statusIds}
           scrollKey={`favourited_statuses-${columnId}`}
           hasMore={hasMore}
+          isLoading={isLoading}
           onScrollToBottom={this.handleScrollToBottom}
         />
       </Column>
diff --git a/app/javascript/mastodon/features/status/components/card.js b/app/javascript/mastodon/features/status/components/card.js
index d3e322c36..f7d980066 100644
--- a/app/javascript/mastodon/features/status/components/card.js
+++ b/app/javascript/mastodon/features/status/components/card.js
@@ -59,6 +59,8 @@ export default class Card extends React.PureComponent {
 
   renderLink () {
     const { card, maxDescription } = this.props;
+    const { width }  = this.state;
+    const horizontal = card.get('width') > card.get('height') && (card.get('width') + 100 >= width);
 
     let image    = '';
     let provider = card.get('provider_name');
@@ -75,17 +77,15 @@ export default class Card extends React.PureComponent {
       provider = decodeIDNA(getHostname(card.get('url')));
     }
 
-    const className = classnames('status-card', {
-      'horizontal': card.get('width') > card.get('height'),
-    });
+    const className = classnames('status-card', { horizontal });
 
     return (
-      <a href={card.get('url')} className={className} target='_blank' rel='noopener'>
+      <a href={card.get('url')} className={className} target='_blank' rel='noopener' ref={this.setRef}>
         {image}
 
         <div className='status-card__content'>
           <strong className='status-card__title' title={card.get('title')}>{card.get('title')}</strong>
-          <p className='status-card__description'>{(card.get('description') || '').substring(0, maxDescription)}</p>
+          {!horizontal && <p className='status-card__description'>{(card.get('description') || '').substring(0, maxDescription)}</p>}
           <span className='status-card__host'>{provider}</span>
         </div>
       </a>
diff --git a/app/javascript/mastodon/features/ui/components/video_modal.js b/app/javascript/mastodon/features/ui/components/video_modal.js
index 1437deeb0..6a883759f 100644
--- a/app/javascript/mastodon/features/ui/components/video_modal.js
+++ b/app/javascript/mastodon/features/ui/components/video_modal.js
@@ -23,6 +23,7 @@ export default class VideoModal extends ImmutablePureComponent {
             src={media.get('url')}
             startTime={time}
             onCloseVideo={onClose}
+            detailed
             description={media.get('description')}
           />
         </div>
diff --git a/app/javascript/mastodon/features/video/index.js b/app/javascript/mastodon/features/video/index.js
index 003bf23a8..0ee8bb6c8 100644
--- a/app/javascript/mastodon/features/video/index.js
+++ b/app/javascript/mastodon/features/video/index.js
@@ -17,6 +17,18 @@ const messages = defineMessages({
   exit_fullscreen: { id: 'video.exit_fullscreen', defaultMessage: 'Exit full screen' },
 });
 
+const formatTime = secondsNum => {
+  let hours   = Math.floor(secondsNum / 3600);
+  let minutes = Math.floor((secondsNum - (hours * 3600)) / 60);
+  let seconds = secondsNum - (hours * 3600) - (minutes * 60);
+
+  if (hours   < 10) hours   = '0' + hours;
+  if (minutes < 10) minutes = '0' + minutes;
+  if (seconds < 10) seconds = '0' + seconds;
+
+  return (hours === '00' ? '' : `${hours}:`) + `${minutes}:${seconds}`;
+};
+
 const findElementPosition = el => {
   let box;
 
@@ -83,11 +95,13 @@ export default class Video extends React.PureComponent {
     startTime: PropTypes.number,
     onOpenVideo: PropTypes.func,
     onCloseVideo: PropTypes.func,
+    detailed: PropTypes.bool,
     intl: PropTypes.object.isRequired,
   };
 
   state = {
-    progress: 0,
+    currentTime: 0,
+    duration: 0,
     paused: true,
     dragging: false,
     fullscreen: false,
@@ -117,7 +131,10 @@ export default class Video extends React.PureComponent {
   }
 
   handleTimeUpdate = () => {
-    this.setState({ progress: 100 * (this.video.currentTime / this.video.duration) });
+    this.setState({
+      currentTime: Math.floor(this.video.currentTime),
+      duration: Math.floor(this.video.duration),
+    });
   }
 
   handleMouseDown = e => {
@@ -143,8 +160,10 @@ export default class Video extends React.PureComponent {
 
   handleMouseMove = throttle(e => {
     const { x } = getPointerPosition(this.seek, e);
-    this.video.currentTime = this.video.duration * x;
-    this.setState({ progress: x * 100 });
+    const currentTime = Math.floor(this.video.duration * x);
+
+    this.video.currentTime = currentTime;
+    this.setState({ currentTime });
   }, 60);
 
   togglePlay = () => {
@@ -226,11 +245,12 @@ export default class Video extends React.PureComponent {
   }
 
   render () {
-    const { preview, src, width, height, startTime, onOpenVideo, onCloseVideo, intl, alt } = this.props;
-    const { progress, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
+    const { preview, src, width, height, startTime, onOpenVideo, onCloseVideo, intl, alt, detailed } = this.props;
+    const { currentTime, duration, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
+    const progress = (currentTime / duration) * 100;
 
     return (
-      <div className={classNames('video-player', { inactive: !revealed, inline: width && height && !fullscreen, fullscreen })} style={{ width, height }} ref={this.setPlayerRef} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
+      <div className={classNames('video-player', { inactive: !revealed, detailed, inline: width && height && !fullscreen, fullscreen })} style={{ width, height }} ref={this.setPlayerRef} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
         <video
           ref={this.setVideoRef}
           src={src}
@@ -267,16 +287,27 @@ export default class Video extends React.PureComponent {
             />
           </div>
 
-          <div className='video-player__buttons left'>
-            <button aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} onClick={this.togglePlay}><i className={classNames('fa fa-fw', { 'fa-play': paused, 'fa-pause': !paused })} /></button>
-            <button aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} onClick={this.toggleMute}><i className={classNames('fa fa-fw', { 'fa-volume-off': muted, 'fa-volume-up': !muted })} /></button>
-            {!onCloseVideo && <button aria-label={intl.formatMessage(messages.hide)} onClick={this.toggleReveal}><i className='fa fa-fw fa-eye' /></button>}
-          </div>
-
-          <div className='video-player__buttons right'>
-            {(!fullscreen && onOpenVideo) && <button aria-label={intl.formatMessage(messages.expand)} onClick={this.handleOpenVideo}><i className='fa fa-fw fa-expand' /></button>}
-            {onCloseVideo && <button aria-label={intl.formatMessage(messages.close)} onClick={this.handleCloseVideo}><i className='fa fa-fw fa-times' /></button>}
-            <button aria-label={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)} onClick={this.toggleFullscreen}><i className={classNames('fa fa-fw', { 'fa-arrows-alt': !fullscreen, 'fa-compress': fullscreen })} /></button>
+          <div className='video-player__buttons-bar'>
+            <div className='video-player__buttons left'>
+              <button aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} onClick={this.togglePlay}><i className={classNames('fa fa-fw', { 'fa-play': paused, 'fa-pause': !paused })} /></button>
+              <button aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} onClick={this.toggleMute}><i className={classNames('fa fa-fw', { 'fa-volume-off': muted, 'fa-volume-up': !muted })} /></button>
+
+              {!onCloseVideo && <button aria-label={intl.formatMessage(messages.hide)} onClick={this.toggleReveal}><i className='fa fa-fw fa-eye' /></button>}
+
+              {(detailed || fullscreen) &&
+                <span>
+                  <span className='video-player__time-current'>{formatTime(currentTime)}</span>
+                  <span className='video-player__time-sep'>/</span>
+                  <span className='video-player__time-total'>{formatTime(duration)}</span>
+                </span>
+              }
+            </div>
+
+            <div className='video-player__buttons right'>
+              {(!fullscreen && onOpenVideo) && <button aria-label={intl.formatMessage(messages.expand)} onClick={this.handleOpenVideo}><i className='fa fa-fw fa-expand' /></button>}
+              {onCloseVideo && <button aria-label={intl.formatMessage(messages.close)} onClick={this.handleCloseVideo}><i className='fa fa-fw fa-compress' /></button>}
+              <button aria-label={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)} onClick={this.toggleFullscreen}><i className={classNames('fa fa-fw', { 'fa-arrows-alt': !fullscreen, 'fa-compress': fullscreen })} /></button>
+            </div>
           </div>
         </div>
       </div>
diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json
index a984b38d2..ec66a0027 100644
--- a/app/javascript/mastodon/locales/ar.json
+++ b/app/javascript/mastodon/locales/ar.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "تحميل ...",
   "media_gallery.toggle_visible": "عرض / إخفاء",
diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json
index d20120b11..1c04b3bfa 100644
--- a/app/javascript/mastodon/locales/bg.json
+++ b/app/javascript/mastodon/locales/bg.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "Зареждане...",
   "media_gallery.toggle_visible": "Toggle visibility",
diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json
index bfa931fc0..f705937fd 100644
--- a/app/javascript/mastodon/locales/ca.json
+++ b/app/javascript/mastodon/locales/ca.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "Carregant...",
   "media_gallery.toggle_visible": "Alternar visibilitat",
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index b6d9e27a7..6354f18b6 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "Wird geladen …",
   "media_gallery.toggle_visible": "Sichtbarkeit umschalten",
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index f5154634c..0f766af6a 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -135,7 +135,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "Loading...",
   "media_gallery.toggle_visible": "Toggle visibility",
diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
index 619c7320a..9e66c379f 100644
--- a/app/javascript/mastodon/locales/eo.json
+++ b/app/javascript/mastodon/locales/eo.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "Ŝarganta…",
   "media_gallery.toggle_visible": "Baskuli videblecon",
diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json
index 411615744..6122a79ab 100644
--- a/app/javascript/mastodon/locales/es.json
+++ b/app/javascript/mastodon/locales/es.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "Cargando…",
   "media_gallery.toggle_visible": "Cambiar visibilidad",
diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json
index aa5c21feb..75057a7dd 100644
--- a/app/javascript/mastodon/locales/fa.json
+++ b/app/javascript/mastodon/locales/fa.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "بارگیری...",
   "media_gallery.toggle_visible": "تغییر پیدایی",
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
index db9319e2e..4ddc1cca7 100644
--- a/app/javascript/mastodon/locales/fi.json
+++ b/app/javascript/mastodon/locales/fi.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "Ladataan...",
   "media_gallery.toggle_visible": "Toggle visibility",
diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json
index 9b9469bc2..a7a8876d0 100644
--- a/app/javascript/mastodon/locales/fr.json
+++ b/app/javascript/mastodon/locales/fr.json
@@ -63,8 +63,8 @@
   "confirmations.block.message": "Confirmez-vous le blocage de {name} ?",
   "confirmations.delete.confirm": "Supprimer",
   "confirmations.delete.message": "Confirmez-vous la suppression de ce pouet ?",
-  "confirmations.delete_list.confirm": "Delete",
-  "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?",
+  "confirmations.delete_list.confirm": "Supprimer",
+  "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",
@@ -114,27 +114,27 @@
   "keyboard_shortcuts.description": "Description",
   "keyboard_shortcuts.down": "descendre dans la liste",
   "keyboard_shortcuts.enter": "to open status",
-  "keyboard_shortcuts.favourite": "to 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.favourite": "vers les favoris",
+  "keyboard_shortcuts.heading": "Raccourcis clavier",
+  "keyboard_shortcuts.hotkey": "Raccourci",
+  "keyboard_shortcuts.legend": "pour afficher cette légende",
+  "keyboard_shortcuts.mention": "pour mentionner l'auteur",
+  "keyboard_shortcuts.reply": "pour répondre",
   "keyboard_shortcuts.search": "to focus search",
-  "keyboard_shortcuts.toot": "to start a brand new toot",
+  "keyboard_shortcuts.toot": "pour démarrer un tout nouveau pouet",
   "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
   "keyboard_shortcuts.up": "to move up in the list",
   "lightbox.close": "Fermer",
   "lightbox.next": "Suivant",
   "lightbox.previous": "Précédent",
-  "lists.account.add": "Add to list",
-  "lists.account.remove": "Remove from list",
-  "lists.delete": "Delete list",
-  "lists.edit": "Edit list",
-  "lists.new.create": "Add list",
-  "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
-  "lists.subheading": "Your lists",
+  "lists.account.add": "Ajouter à la liste",
+  "lists.account.remove": "Supprimer de la liste",
+  "lists.delete": "Effacer la liste",
+  "lists.edit": "Éditer la liste",
+  "lists.new.create": "Ajouter une liste",
+  "lists.new.title_placeholder": "Titre de la nouvelle liste",
+  "lists.search": "Rechercher parmi les gens que vous suivez",
+  "lists.subheading": "Vos listes",
   "loading_indicator.label": "Chargement…",
   "media_gallery.toggle_visible": "Modifier la visibilité",
   "missing_indicator.label": "Non trouvé",
@@ -145,8 +145,8 @@
   "navigation_bar.favourites": "Favoris",
   "navigation_bar.follow_requests": "Demandes de suivi",
   "navigation_bar.info": "Plus d’informations",
-  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
-  "navigation_bar.lists": "Lists",
+  "navigation_bar.keyboard_shortcuts": "Raccourcis clavier",
+  "navigation_bar.lists": "Listes",
   "navigation_bar.logout": "Déconnexion",
   "navigation_bar.mutes": "Comptes masqués",
   "navigation_bar.pins": "Pouets épinglés",
@@ -241,7 +241,7 @@
   "tabs_bar.home": "Accueil",
   "tabs_bar.local_timeline": "Fil public local",
   "tabs_bar.notifications": "Notifications",
-  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
+  "ui.beforeunload": "Votre brouillon sera perdu si vous quittez Mastodon.",
   "upload_area.title": "Glissez et déposez pour envoyer",
   "upload_button.label": "Joindre un média",
   "upload_form.description": "Décrire pour les malvoyants",
diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json
new file mode 100644
index 000000000..bb0b1a9fd
--- /dev/null
+++ b/app/javascript/mastodon/locales/gl.json
@@ -0,0 +1,259 @@
+{
+  "account.block": "Bloquear @{name}",
+  "account.block_domain": "Ocultar calquer contido de {domain}",
+  "account.disclaimer_full": "A información inferior podería mostrar un perfil incompleto da usuaria.",
+  "account.edit_profile": "Editar perfil",
+  "account.follow": "Seguir",
+  "account.followers": "Seguidoras",
+  "account.follows": "Seguindo",
+  "account.follows_you": "Séguena",
+  "account.hide_reblogs": "Ocultar repeticións de @{name}",
+  "account.media": "Medios",
+  "account.mention": "Mencionar @{name}",
+  "account.moved_to": "{name} marchou a:",
+  "account.mute": "Acalar @{name}",
+  "account.mute_notifications": "Acalar as notificacións de @{name}",
+  "account.posts": "Publicacións",
+  "account.report": "Informar sobre @{name}",
+  "account.requested": "Agardando aceptación. Pulse para cancelar a solicitude de seguimento",
+  "account.share": "Compartir o perfil de @{name}",
+  "account.show_reblogs": "Mostrar repeticións de @{name}",
+  "account.unblock": "Desbloquear @{name}",
+  "account.unblock_domain": "Non ocultar {domain}",
+  "account.unfollow": "Non seguir",
+  "account.unmute": "Non acalar @{name}",
+  "account.unmute_notifications": "Desbloquear as notificacións de @{name}",
+  "account.view_full_profile": "Ver o perfil completo",
+  "boost_modal.combo": "Pulse {combo} para saltar esto a próxima vez",
+  "bundle_column_error.body": "Houbo un fallo mentras se cargaba este compoñente.",
+  "bundle_column_error.retry": "Inténteo de novo",
+  "bundle_column_error.title": "Fallo na rede",
+  "bundle_modal_error.close": "Pechar",
+  "bundle_modal_error.message": "Algo fallou mentras se cargaba este compoñente.",
+  "bundle_modal_error.retry": "Inténteo de novo",
+  "column.blocks": "Usuarias bloqueadas",
+  "column.community": "Liña temporal local",
+  "column.favourites": "Favoritas",
+  "column.follow_requests": "Peticións de seguimento",
+  "column.home": "Inicio",
+  "column.lists": "Lists",
+  "column.mutes": "Usuarias acaladas",
+  "column.notifications": "Notificacións",
+  "column.pins": "Mensaxes fixadas",
+  "column.public": "Liña temporal federada",
+  "column_back_button.label": "Atrás",
+  "column_header.hide_settings": "Agochar axustes",
+  "column_header.moveLeft_settings": "Mover a columna hacia a esquerda",
+  "column_header.moveRight_settings": "Mover a columna hacia a dereita",
+  "column_header.pin": "Fixar",
+  "column_header.show_settings": "Mostras axustes",
+  "column_header.unpin": "Soltar",
+  "column_subheading.navigation": "Navegación",
+  "column_subheading.settings": "Axustes",
+  "compose_form.lock_disclaimer": "A súa conta non está {locked}. Calquera pode seguila para ver as súas mensaxes só-para-seguidoras.",
+  "compose_form.lock_disclaimer.lock": "bloqueado",
+  "compose_form.placeholder": "A qué andas?",
+  "compose_form.publish": "Toot",
+  "compose_form.publish_loud": "{publish}!",
+  "compose_form.sensitive": "Marcar medios como sensibles",
+  "compose_form.spoiler": "Agochar texto detrás de un aviso",
+  "compose_form.spoiler_placeholder": "Escriba o aviso aquí",
+  "confirmation_modal.cancel": "Cancelar",
+  "confirmations.block.confirm": "Bloquear",
+  "confirmations.block.message": "Está segura de querer bloquear a {name}?",
+  "confirmations.delete.confirm": "Borrar",
+  "confirmations.delete.message": "Está segura de que quere eliminar este estado?",
+  "confirmations.delete_list.confirm": "Delete",
+  "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?",
+  "confirmations.domain_block.confirm": "Agochar un dominio completo",
+  "confirmations.domain_block.message": "Realmente está segura de que quere bloquear por completo o dominio {domain}? Normalmente é suficiente, e preferible, bloquear de xeito selectivo varios elementos.",
+  "confirmations.mute.confirm": "Acalar",
+  "confirmations.mute.message": "Está segura de que quere acalar a {name}?",
+  "confirmations.unfollow.confirm": "Deixar de seguir",
+  "confirmations.unfollow.message": "Quere deixar de seguir a {name}?",
+  "embed.instructions": "Copie o código inferior para incrustar no seu sitio web este estado.",
+  "embed.preview": "Así será mostrado:",
+  "emoji_button.activity": "Actividade",
+  "emoji_button.custom": "Personalizado",
+  "emoji_button.flags": "Marcas",
+  "emoji_button.food": "Comida e Bebida",
+  "emoji_button.label": "Insertar emoji",
+  "emoji_button.nature": "Natureza",
+  "emoji_button.not_found": "Sen emojos!! (╯°□°)╯︵ ┻━┻",
+  "emoji_button.objects": "Obxetos",
+  "emoji_button.people": "Xente",
+  "emoji_button.recent": "Utilizadas con frecuencia",
+  "emoji_button.search": "Buscar...",
+  "emoji_button.search_results": "Resultados da busca",
+  "emoji_button.symbols": "Símbolos",
+  "emoji_button.travel": "Viaxes e Lugares",
+  "empty_column.community": "A liña temporal local está baldeira. Escriba algo de xeito público para que rule!",
+  "empty_column.hashtag": "Aínda non hai nada con esta etiqueta.",
+  "empty_column.home": "A súa liña temporal de inicio está baldeira! Visite {public} ou utilice a busca para atopar outras usuarias.",
+  "empty_column.home.public_timeline": "a liña temporal pública",
+  "empty_column.list": "Aínda non hai nada en esta lista.",
+  "empty_column.notifications": "Aínda non ten notificacións. Interactúe con outras para iniciar unha conversa.",
+  "empty_column.public": "Nada por aquí! Escriba algo de xeito público, ou siga manualmente usuarias de outras instancias para ir enchéndoa",
+  "follow_request.authorize": "Autorizar",
+  "follow_request.reject": "Rexeitar",
+  "getting_started.appsshort": "Aplicacións",
+  "getting_started.faq": "PMF",
+  "getting_started.heading": "Comezando",
+  "getting_started.open_source_notice": "Mastodon é software de código aberto. Pode contribuír ou informar de fallos en GitHub en {github}.",
+  "getting_started.userguide": "Guía de usuaria",
+  "home.column_settings.advanced": "Avanzado",
+  "home.column_settings.basic": "Básico",
+  "home.column_settings.filter_regex": "Filtrar expresións regulares",
+  "home.column_settings.show_reblogs": "Mostrar repeticións",
+  "home.column_settings.show_replies": "Mostrar respostas",
+  "home.settings": "Axustes da columna",
+  "keyboard_shortcuts.back": "voltar atrás",
+  "keyboard_shortcuts.boost": "repetir",
+  "keyboard_shortcuts.column": "destacar un estado en unha das columnas",
+  "keyboard_shortcuts.compose": "Foco no área de escritura",
+  "keyboard_shortcuts.description": "Descrición",
+  "keyboard_shortcuts.down": "ir hacia abaixo na lista",
+  "keyboard_shortcuts.enter": "abrir estado",
+  "keyboard_shortcuts.favourite": "marcar como favorito",
+  "keyboard_shortcuts.heading": "Atallos do teclado",
+  "keyboard_shortcuts.hotkey": "Tecla de acceso directo",
+  "keyboard_shortcuts.legend": "para mostrar esta lenda",
+  "keyboard_shortcuts.mention": "para mencionar o autor",
+  "keyboard_shortcuts.reply": "para responder",
+  "keyboard_shortcuts.search": "para centrar a busca",
+  "keyboard_shortcuts.toot": "escribir un toot novo",
+  "keyboard_shortcuts.unfocus": "quitar o foco do área de escritura/busca",
+  "keyboard_shortcuts.up": "ir hacia arriba na lista",
+  "lightbox.close": "Fechar",
+  "lightbox.next": "Seguinte",
+  "lightbox.previous": "Anterior",
+  "lists.account.add": "Add to list",
+  "lists.account.remove": "Remove from list",
+  "lists.delete": "Delete list",
+  "lists.edit": "Edit list",
+  "lists.new.create": "Add list",
+  "lists.new.title_placeholder": "New list title",
+  "lists.search": "Search among people you follow",
+  "lists.subheading": "Your lists",
+  "loading_indicator.label": "Cargando...",
+  "media_gallery.toggle_visible": "Dar visibilidade",
+  "missing_indicator.label": "Non atopado",
+  "mute_modal.hide_notifications": "Esconder notificacións deste usuario?",
+  "navigation_bar.blocks": "Usuarios bloqueados",
+  "navigation_bar.community_timeline": "Liña temporal local",
+  "navigation_bar.edit_profile": "Editar perfil",
+  "navigation_bar.favourites": "Favoritas",
+  "navigation_bar.follow_requests": "Peticións de seguimento",
+  "navigation_bar.info": "Sobre esta instancia",
+  "navigation_bar.keyboard_shortcuts": "Atallos do teclado",
+  "navigation_bar.lists": "Lists",
+  "navigation_bar.logout": "Sair",
+  "navigation_bar.mutes": "Usuarias acaladas",
+  "navigation_bar.pins": "Mensaxes fixadas",
+  "navigation_bar.preferences": "Preferencias",
+  "navigation_bar.public_timeline": "Liña temporal federada",
+  "notification.favourite": "{name} marcou como favorito o seu estado",
+  "notification.follow": "{name} está a seguila",
+  "notification.mention": "{name} mencionoute",
+  "notification.reblog": "{name} promocionou o seu estado",
+  "notifications.clear": "Limpar notificacións",
+  "notifications.clear_confirmation": "Estás seguro de que queres limpar permanentemente todas as túas notificacións?",
+  "notifications.column_settings.alert": "Notificacións de escritorio",
+  "notifications.column_settings.favourite": "Favoritas:",
+  "notifications.column_settings.follow": "Novos seguidores:",
+  "notifications.column_settings.mention": "Mencións:",
+  "notifications.column_settings.push": "Enviar notificacións",
+  "notifications.column_settings.push_meta": "Este aparello",
+  "notifications.column_settings.reblog": "Promocións:",
+  "notifications.column_settings.show": "Mostrar en columna",
+  "notifications.column_settings.sound": "Reproducir son",
+  "onboarding.done": "Feito",
+  "onboarding.next": "Seguinte",
+  "onboarding.page_five.public_timelines": "A liña de tempo local mostra as publicacións públicas de todos en {domain}. A liña de tempo federada mostra as publicacións públicas de todos os que as persoas en {domain} seguen. Estas son as Liñas de tempo públicas, unha boa forma de descubrir novas persoas.",
+  "onboarding.page_four.home": "A liña de tempo local mostra as publicacións das persoas que segues.",
+  "onboarding.page_four.notifications": "A columna de notificacións mostra cando alguén interactúa contigo.",
+  "onboarding.page_one.federation": "Mastodon é unha rede de servidores independentes que se unen para facer unha rede social máis grande. Chamamos instancias a estes servidores.",
+  "onboarding.page_one.handle": "Estás en {domain}, polo que o teu nome de usuario completo é {handle}",
+  "onboarding.page_one.welcome": "Benvido a Mastodon!",
+  "onboarding.page_six.admin": "O administrador da túa instancia é {admin}.",
+  "onboarding.page_six.almost_done": "Case feito...",
+  "onboarding.page_six.appetoot": "Que tootes ben!",
+  "onboarding.page_six.apps_available": "Hai {apps} dispoñíbeis para iOS, Android e outras plataformas.",
+  "onboarding.page_six.github": "Mastodon é un software gratuito e de código aberto. Pode informar de erros, solicitar novas funcionalidades ou contribuír ao código en {github}.",
+  "onboarding.page_six.guidelines": "directrices da comunidade",
+  "onboarding.page_six.read_guidelines": "Por favor, le as {guidelines} do {domain}!",
+  "onboarding.page_six.various_app": "aplicacións móbiles",
+  "onboarding.page_three.profile": "Edita o teu perfil para cambiar o teu avatar, bio e nome. Alí, tamén atoparás outras preferencias.",
+  "onboarding.page_three.search": "Utilice a barra de busca para atopar xente e descubrir etiquetas, como {illustration} e {introductions}. Para atopar unha usuaria que non está en esta instancia utilice o seu enderezo completo.",
+  "onboarding.page_two.compose": "Escriba mensaxes desde a columna de composición. Pode subir imaxes, mudar as opcións de intimidade e engadir avisos sobre o contido coas iconas inferiores.",
+  "onboarding.skip": "Saltar",
+  "privacy.change": "Axustar a intimidade do estado",
+  "privacy.direct.long": "Enviar exclusivamente as usuarias mencionadas",
+  "privacy.direct.short": "Directa",
+  "privacy.private.long": "Enviar só as seguidoras",
+  "privacy.private.short": "Só-seguidoras",
+  "privacy.public.long": "Publicar na liña temporal pública",
+  "privacy.public.short": "Pública",
+  "privacy.unlisted.long": "Non publicar en liñas temporais públicas",
+  "privacy.unlisted.short": "Non listada",
+  "relative_time.days": "{number}d",
+  "relative_time.hours": "{number}h",
+  "relative_time.just_now": "agora",
+  "relative_time.minutes": "{number}m",
+  "relative_time.seconds": "{number}s",
+  "reply_indicator.cancel": "Cancelar",
+  "report.placeholder": "Comentarios adicionais",
+  "report.submit": "Enviar",
+  "report.target": "Informar {target}",
+  "search.placeholder": "Buscar",
+  "search_popout.search_format": "Formato de busca avanzada",
+  "search_popout.tips.hashtag": "etiqueta",
+  "search_popout.tips.status": "estado",
+  "search_popout.tips.text": "Texto simple devolve coincidencias con nomes públicos, nomes de usuaria e etiquetas",
+  "search_popout.tips.user": "usuaria",
+  "search_results.total": "{count, number} {count,plural,one {result} outros {results}}",
+  "standalone.public_title": "Ollada dentro...",
+  "status.cannot_reblog": "Esta mensaxe non pode ser promocionada",
+  "status.delete": "Eliminar",
+  "status.embed": "Incrustar",
+  "status.favourite": "Favorita",
+  "status.load_more": "Cargar máis",
+  "status.media_hidden": "Medios ocultos",
+  "status.mention": "Mencionar @{name}",
+  "status.more": "Máis",
+  "status.mute_conversation": "Acalar conversa",
+  "status.open": "Expandir este estado",
+  "status.pin": "Fixar no perfil",
+  "status.reblog": "Promocionar",
+  "status.reblogged_by": "{name} promocionado",
+  "status.reply": "Resposta",
+  "status.replyAll": "Resposta a conversa",
+  "status.report": "Informar @{name}",
+  "status.sensitive_toggle": "Pulse para ver",
+  "status.sensitive_warning": "Contido sensible",
+  "status.share": "Compartir",
+  "status.show_less": "Mostrar menos",
+  "status.show_more": "Mostrar máis",
+  "status.unmute_conversation": "Non acalar a conversa",
+  "status.unpin": "Despegar do perfil",
+  "tabs_bar.compose": "Compoñer",
+  "tabs_bar.federated_timeline": "Federado",
+  "tabs_bar.home": "Inicio",
+  "tabs_bar.local_timeline": "Local",
+  "tabs_bar.notifications": "Notificacións",
+  "ui.beforeunload": "O borrador perderase se sae de Mastodon.",
+  "upload_area.title": "Arrastre e solte para subir",
+  "upload_button.label": "Engadir medios",
+  "upload_form.description": "Describa para deficientes visuais",
+  "upload_form.undo": "Desfacer",
+  "upload_progress.label": "Subindo...",
+  "video.close": "Pechar video",
+  "video.exit_fullscreen": "Saír da pantalla completa",
+  "video.expand": "Expandir vídeo",
+  "video.fullscreen": "Pantalla completa",
+  "video.hide": "Agochar vídeo",
+  "video.mute": "Acalar son",
+  "video.pause": "Pausar",
+  "video.play": "Reproducir",
+  "video.unmute": "Permitir son"
+}
diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json
index ec1e30dd5..5444c8e34 100644
--- a/app/javascript/mastodon/locales/he.json
+++ b/app/javascript/mastodon/locales/he.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "טוען...",
   "media_gallery.toggle_visible": "נראה\\בלתי נראה",
diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json
index c21482670..f70c66223 100644
--- a/app/javascript/mastodon/locales/hr.json
+++ b/app/javascript/mastodon/locales/hr.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "Učitavam...",
   "media_gallery.toggle_visible": "Preklopi vidljivost",
diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json
index 71dd810b6..7cb816fe9 100644
--- a/app/javascript/mastodon/locales/hu.json
+++ b/app/javascript/mastodon/locales/hu.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "Betöltés...",
   "media_gallery.toggle_visible": "Toggle visibility",
diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json
index 744423e78..429b77182 100644
--- a/app/javascript/mastodon/locales/id.json
+++ b/app/javascript/mastodon/locales/id.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "Tunggu sebentar...",
   "media_gallery.toggle_visible": "Tampil/Sembunyikan",
diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json
index b1523e626..3e5c8edb9 100644
--- a/app/javascript/mastodon/locales/io.json
+++ b/app/javascript/mastodon/locales/io.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "Kargante...",
   "media_gallery.toggle_visible": "Chanjar videbleso",
diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json
index 9a2d320fd..e2ad1632a 100644
--- a/app/javascript/mastodon/locales/it.json
+++ b/app/javascript/mastodon/locales/it.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "Carico...",
   "media_gallery.toggle_visible": "Imposta visibilità",
diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json
index 125618bf2..40ad66a7f 100644
--- a/app/javascript/mastodon/locales/ja.json
+++ b/app/javascript/mastodon/locales/ja.json
@@ -24,7 +24,7 @@
   "account.unmute": "ミュート解除",
   "account.unmute_notifications": "@{name}さんからの通知を受け取らない",
   "account.view_full_profile": "全ての情報を見る",
-  "boost_modal.combo": "次からは{combo}を押せば、これをスキップできます。",
+  "boost_modal.combo": "次からは{combo}を押せば、これをスキップできます",
   "bundle_column_error.body": "コンポーネントの読み込み中に問題が発生しました。",
   "bundle_column_error.retry": "再試行",
   "bundle_column_error.title": "ネットワークエラー",
diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json
index 3f47baa76..472a52a99 100644
--- a/app/javascript/mastodon/locales/ko.json
+++ b/app/javascript/mastodon/locales/ko.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "불러오는 중...",
   "media_gallery.toggle_visible": "표시 전환",
diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json
index 26e86308d..c290ed767 100644
--- a/app/javascript/mastodon/locales/nl.json
+++ b/app/javascript/mastodon/locales/nl.json
@@ -107,22 +107,22 @@
   "home.column_settings.show_reblogs": "Boosts tonen",
   "home.column_settings.show_replies": "Reacties tonen",
   "home.settings": "Kolom-instellingen",
-  "keyboard_shortcuts.back": "om terug te navigeren",
+  "keyboard_shortcuts.back": "om terug te gaan",
   "keyboard_shortcuts.boost": "om te boosten",
-  "keyboard_shortcuts.column": "om te focussen op een status in één van de kolommen",
-  "keyboard_shortcuts.compose": "om te focussen op het toot tekstvak",
-  "keyboard_shortcuts.description": "Description",
+  "keyboard_shortcuts.column": "om op een toot te focussen in één van de kolommen",
+  "keyboard_shortcuts.compose": "om het tekstvak voor toots te focussen",
+  "keyboard_shortcuts.description": "Omschrijving",
   "keyboard_shortcuts.down": "om naar beneden door de lijst te bewegen",
   "keyboard_shortcuts.enter": "to open status",
-  "keyboard_shortcuts.favourite": "om het te markeren als favoriet",
-  "keyboard_shortcuts.heading": "Keyboard Shortcuts",
+  "keyboard_shortcuts.favourite": "om als favoriet te markeren",
+  "keyboard_shortcuts.heading": "Sneltoetsen",
   "keyboard_shortcuts.hotkey": "Sneltoets",
   "keyboard_shortcuts.legend": "om deze legenda weer te geven",
   "keyboard_shortcuts.mention": "om de auteur te vermelden",
-  "keyboard_shortcuts.reply": "om te antwoorden",
-  "keyboard_shortcuts.search": "om te focussen op zoeken",
+  "keyboard_shortcuts.reply": "om te reageren",
+  "keyboard_shortcuts.search": "om het zoekvak te focussen",
   "keyboard_shortcuts.toot": "om een nieuwe toot te starten",
-  "keyboard_shortcuts.unfocus": "om te ontfocussen van het toot tekstvak/zoeken",
+  "keyboard_shortcuts.unfocus": "om het tekst- en zoekvak te ontfocussen",
   "keyboard_shortcuts.up": "om omhoog te bewegen in de lijst",
   "lightbox.close": "Sluiten",
   "lightbox.next": "Volgende",
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "Laden…",
   "media_gallery.toggle_visible": "Media wel/niet tonen",
@@ -204,7 +204,7 @@
   "reply_indicator.cancel": "Annuleren",
   "report.placeholder": "Extra opmerkingen",
   "report.submit": "Verzenden",
-  "report.target": "Rapporteren van",
+  "report.target": "Rapporteer {target}",
   "search.placeholder": "Zoeken",
   "search_popout.search_format": "Geavanceerd zoeken",
   "search_popout.tips.hashtag": "hashtag",
diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json
index 233b6c946..bf2b6259a 100644
--- a/app/javascript/mastodon/locales/no.json
+++ b/app/javascript/mastodon/locales/no.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "Laster...",
   "media_gallery.toggle_visible": "Veksle synlighet",
diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json
index ec7202ff6..f8b4751d6 100644
--- a/app/javascript/mastodon/locales/oc.json
+++ b/app/javascript/mastodon/locales/oc.json
@@ -10,22 +10,22 @@
   "account.hide_reblogs": "Rescondre los partages de @{name}",
   "account.media": "Mèdias",
   "account.mention": "Mencionar @{name}",
-  "account.moved_to": "{name} a mudat los catons a : ",
+  "account.moved_to": "{name} a mudat los catons a :",
   "account.mute": "Rescondre @{name}",
-  "account.mute_notifications": "Mute notifications from @{name}",
+  "account.mute_notifications": "Rescondre las notificacions de @{name}",
   "account.posts": "Estatuts",
   "account.report": "Senhalar @{name}",
-  "account.requested": "Invitacion mandada. Clicatz per anullar.",
+  "account.requested": "Invitacion mandada. Clicatz per anullar",
   "account.share": "Partejar lo perfil a @{name}",
   "account.show_reblogs": "Mostrar los partages de @{name}",
   "account.unblock": "Desblocar @{name}",
   "account.unblock_domain": "Desblocar {domain}",
   "account.unfollow": "Quitar de sègre",
   "account.unmute": "Quitar de rescondre @{name}",
-  "account.unmute_notifications": "Unmute notifications from @{name}",
+  "account.unmute_notifications": "Mostrar las notificacions de @{name}",
   "account.view_full_profile": "Veire lo perfil complet",
   "boost_modal.combo": "Podètz botar {combo} per passar aquò lo còp que ven",
-  "bundle_column_error.body": "Quicòm a fach meuca pendent lo cargament d’aqueste compausant.",
+  "bundle_column_error.body": "Quicòm a fach mèuca pendent lo cargament d’aqueste compausant.",
   "bundle_column_error.retry": "Tornar ensajar",
   "bundle_column_error.title": "Error de ret",
   "bundle_modal_error.close": "Tampar",
@@ -36,7 +36,7 @@
   "column.favourites": "Favorits",
   "column.follow_requests": "Demandas d’abonament",
   "column.home": "Acuèlh",
-  "column.lists": "Lists",
+  "column.lists": "Listas",
   "column.mutes": "Personas rescondudas",
   "column.notifications": "Notificacions",
   "column.pins": "Tuts penjats",
@@ -63,8 +63,8 @@
   "confirmations.block.message": "Sètz segur de voler blocar {name} ?",
   "confirmations.delete.confirm": "Escafar",
   "confirmations.delete.message": "Sètz segur de voler escafar l’estatut ?",
-  "confirmations.delete_list.confirm": "Delete",
-  "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?",
+  "confirmations.delete_list.confirm": "Suprimir",
+  "confirmations.delete_list.message": "Sètz segur de voler suprimir aquesta lista per totjorn ?",
   "confirmations.domain_block.confirm": "Amagar tot lo domeni",
   "confirmations.domain_block.message": "Sètz segur segur de voler blocar completament {domain} ? De còps cal pas que blocar o rescondre unas personas solament.",
   "confirmations.mute.confirm": "Rescondre",
@@ -72,7 +72,7 @@
   "confirmations.unfollow.confirm": "Quitar de sègre",
   "confirmations.unfollow.message": "Volètz vertadièrament quitar de sègre {name} ?",
   "embed.instructions": "Embarcar aqueste estatut per lo far veire sus un site Internet en copiar lo còdi çai-jos.",
-  "embed.preview": "Semblarà aquò : ",
+  "embed.preview": "Semblarà aquò :",
   "emoji_button.activity": "Activitats",
   "emoji_button.custom": "Personalizats",
   "emoji_button.flags": "Drapèus",
@@ -84,16 +84,16 @@
   "emoji_button.people": "Gents",
   "emoji_button.recent": "Sovent utilizats",
   "emoji_button.search": "Cercar…",
-  "emoji_button.search_results": "Resultat de recèrca",
+  "emoji_button.search_results": "Resultats de recèrca",
   "emoji_button.symbols": "Simbòls",
   "emoji_button.travel": "Viatges & lòcs",
   "empty_column.community": "Lo flux public local es void. Escrivètz quicòm per lo garnir !",
-  "empty_column.hashtag": "I a pas encara de contengut ligat a aqueste hashtag",
+  "empty_column.hashtag": "I a pas encara de contengut ligat a aquesta etiqueta.",
   "empty_column.home": "Vòstre flux d’acuèlh es void. Visitatz {public} o utilizatz la recèrca per vos connectar a d’autras personas.",
   "empty_column.home.public_timeline": "lo flux public",
   "empty_column.list": "I a pas res dins la lista pel moment.",
   "empty_column.notifications": "Avètz pas encara de notificacions. Respondètz a qualqu’un per començar una conversacion.",
-  "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.",
+  "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",
@@ -116,7 +116,7 @@
   "keyboard_shortcuts.enter": "per dobrir los estatuts",
   "keyboard_shortcuts.favourite": "per apondre als favorits",
   "keyboard_shortcuts.heading": "Acorchis clavièr",
-  "keyboard_shortcuts.hotkey": "Clau",
+  "keyboard_shortcuts.hotkey": "Acorchis",
   "keyboard_shortcuts.legend": "per mostrar aquesta legenda",
   "keyboard_shortcuts.mention": "per mencionar l’autor",
   "keyboard_shortcuts.reply": "per respondre",
@@ -127,35 +127,35 @@
   "lightbox.close": "Tampar",
   "lightbox.next": "Seguent",
   "lightbox.previous": "Precedent",
-  "lists.account.add": "Add to list",
-  "lists.account.remove": "Remove from list",
-  "lists.delete": "Delete list",
-  "lists.edit": "Edit list",
-  "lists.new.create": "Add list",
-  "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
-  "lists.subheading": "Your lists",
+  "lists.account.add": "Ajustar a la lista",
+  "lists.account.remove": "Levar de la lista",
+  "lists.delete": "Suprimir la lista",
+  "lists.edit": "Modificar la lista",
+  "lists.new.create": "Ajustar una lista",
+  "lists.new.title_placeholder": "Títol de la nòva lista",
+  "lists.search": "Cercar demest lo monde que seguètz",
+  "lists.subheading": "Vòstras listas",
   "loading_indicator.label": "Cargament…",
   "media_gallery.toggle_visible": "Modificar la visibilitat",
   "missing_indicator.label": "Pas trobat",
-  "mute_modal.hide_notifications": "Hide notifications from this user?",
+  "mute_modal.hide_notifications": "Rescondre las notificacions d’aquesta persona ?",
   "navigation_bar.blocks": "Personas blocadas",
   "navigation_bar.community_timeline": "Flux public local",
   "navigation_bar.edit_profile": "Modificar lo perfil",
   "navigation_bar.favourites": "Favorits",
-  "navigation_bar.follow_requests": "Demandas d'abonament",
+  "navigation_bar.follow_requests": "Demandas d’abonament",
   "navigation_bar.info": "Mai informacions",
   "navigation_bar.keyboard_shortcuts": "Acorchis clavièr",
-  "navigation_bar.lists": "Lists",
+  "navigation_bar.lists": "Listas",
   "navigation_bar.logout": "Desconnexion",
   "navigation_bar.mutes": "Personas rescondudas",
   "navigation_bar.pins": "Tuts penjats",
   "navigation_bar.preferences": "Preferéncias",
   "navigation_bar.public_timeline": "Flux public global",
-  "notification.favourite": "{name} a ajustat a sos favorits :",
+  "notification.favourite": "{name} a ajustat a sos favorits",
   "notification.follow": "{name} vos sèc",
-  "notification.mention": "{name} vos a mencionat :",
-  "notification.reblog": "{name} a partejat vòstre estatut :",
+  "notification.mention": "{name} vos a mencionat",
+  "notification.reblog": "{name} a partejat vòstre estatut",
   "notifications.clear": "Escafar",
   "notifications.clear_confirmation": "Volètz vertadièrament escafar totas vòstras las notificacions ?",
   "notifications.column_settings.alert": "Notificacions localas",
@@ -171,7 +171,7 @@
   "onboarding.next": "Seguent",
   "onboarding.page_five.public_timelines": "Lo flux local mòstra los estatuts publics del monde de vòstra instància, aquí {domain}. Lo flux federat mòstra los estatuts publics de la gent que los de {domain} sègon. Son los fluxes publics, un bon biais de trobar de mond.",
   "onboarding.page_four.home": "Lo flux d’acuèlh mòstra los estatuts del mond que seguètz.",
-  "onboarding.page_four.notifications": "La colomna de notificacions vos fa veire quand qualqu’un interagís amb vos",
+  "onboarding.page_four.notifications": "La colomna de notificacions vos fa veire quand qualqu’un interagís amb vos.",
   "onboarding.page_one.federation": "Mastodon es un malhum de servidors independents que comunican per construire un malhum mai larg. Òm los apèla instàncias.",
   "onboarding.page_one.handle": "Sètz sus {domain}, doncas vòstre identificant complet es {handle}",
   "onboarding.page_one.welcome": "Benvengut a Mastodon !",
@@ -209,7 +209,7 @@
   "search_popout.search_format": "Format recèrca avançada",
   "search_popout.tips.hashtag": "etiqueta",
   "search_popout.tips.status": "estatut",
-  "search_popout.tips.text": "Tèxt brut tòrna escais, noms d’utilizaire e etiquetas correspondents",
+  "search_popout.tips.text": "Lo tèxt brut tòrna escais, noms d’utilizaire e etiquetas correspondents",
   "search_popout.tips.user": "utilizaire",
   "search_results.total": "{count, number} {count, plural, one {resultat} other {resultats}}",
   "standalone.public_title": "Una ulhada dedins…",
@@ -225,7 +225,7 @@
   "status.open": "Desplegar aqueste estatut",
   "status.pin": "Penjar al perfil",
   "status.reblog": "Partejar",
-  "status.reblogged_by": "{name} a partejat :",
+  "status.reblogged_by": "{name} a partejat",
   "status.reply": "Respondre",
   "status.replyAll": "Respondre a la conversacion",
   "status.report": "Senhalar @{name}",
diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json
index 1df27d536..6bac65865 100644
--- a/app/javascript/mastodon/locales/pt-BR.json
+++ b/app/javascript/mastodon/locales/pt-BR.json
@@ -109,21 +109,21 @@
   "home.settings": "Configurações de colunas",
   "keyboard_shortcuts.back": "para navegar de volta",
   "keyboard_shortcuts.boost": "para compartilhar",
-  "keyboard_shortcuts.column": "to focus a status in one of the columns",
+  "keyboard_shortcuts.column": "Focar um status em uma das colunas",
   "keyboard_shortcuts.compose": "to focus the compose textarea",
   "keyboard_shortcuts.description": "Description",
-  "keyboard_shortcuts.down": "to move down in the list",
+  "keyboard_shortcuts.down": "para mover para baixo na lista",
   "keyboard_shortcuts.enter": "to open status",
-  "keyboard_shortcuts.favourite": "to favourite",
+  "keyboard_shortcuts.favourite": "para adicionar aos favoritos",
   "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.toot": "to start a brand new toot",
-  "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
-  "keyboard_shortcuts.up": "to move up in the list",
+  "keyboard_shortcuts.hotkey": "Atalho",
+  "keyboard_shortcuts.legend": "para mostrar essa legenda",
+  "keyboard_shortcuts.mention": "para mencionar o autor",
+  "keyboard_shortcuts.reply": "para responder",
+  "keyboard_shortcuts.search": "para focar a pesquisa",
+  "keyboard_shortcuts.toot": "para compor um novo toot",
+  "keyboard_shortcuts.unfocus": "para remover o foco da área de composição/pesquisa",
+  "keyboard_shortcuts.up": "para mover para cima na lista",
   "lightbox.close": "Fechar",
   "lightbox.next": "Próximo",
   "lightbox.previous": "Anterior",
@@ -133,19 +133,19 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "Carregando...",
   "media_gallery.toggle_visible": "Esconder/Mostrar",
   "missing_indicator.label": "Não encontrado",
-  "mute_modal.hide_notifications": "Hide notifications from this user?",
+  "mute_modal.hide_notifications": "Esconder notificações deste usuário?",
   "navigation_bar.blocks": "Usuários bloqueados",
   "navigation_bar.community_timeline": "Local",
   "navigation_bar.edit_profile": "Editar perfil",
   "navigation_bar.favourites": "Favoritos",
   "navigation_bar.follow_requests": "Seguidores pendentes",
   "navigation_bar.info": "Mais informações",
-  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.keyboard_shortcuts": "Atalhos de teclado",
   "navigation_bar.lists": "Lists",
   "navigation_bar.logout": "Sair",
   "navigation_bar.mutes": "Usuários silenciados",
@@ -220,7 +220,7 @@
   "status.load_more": "Carregar mais",
   "status.media_hidden": "Mídia escondida",
   "status.mention": "Mencionar @{name}",
-  "status.more": "More",
+  "status.more": "Mais",
   "status.mute_conversation": "Silenciar conversa",
   "status.open": "Expandir",
   "status.pin": "Fixar no perfil",
@@ -241,7 +241,7 @@
   "tabs_bar.home": "Página inicial",
   "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Notificações",
-  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
+  "ui.beforeunload": "Seu rascunho será perdido se você sair do Mastodon.",
   "upload_area.title": "Arraste e solte para enviar",
   "upload_button.label": "Adicionar mídia",
   "upload_form.description": "Descreva a imagem para deficientes visuais",
diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json
index 3d3e19571..728fb3a10 100644
--- a/app/javascript/mastodon/locales/pt.json
+++ b/app/javascript/mastodon/locales/pt.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "A carregar...",
   "media_gallery.toggle_visible": "Esconder/Mostrar",
diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json
index 0aef2d9df..e9925b675 100644
--- a/app/javascript/mastodon/locales/ru.json
+++ b/app/javascript/mastodon/locales/ru.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "Загрузка...",
   "media_gallery.toggle_visible": "Показать/скрыть",
diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json
index 53090452f..9d9646509 100644
--- a/app/javascript/mastodon/locales/sv.json
+++ b/app/javascript/mastodon/locales/sv.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "Laddar...",
   "media_gallery.toggle_visible": "Växla synlighet",
diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json
index 2f064a193..cc18a6096 100644
--- a/app/javascript/mastodon/locales/th.json
+++ b/app/javascript/mastodon/locales/th.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "Loading...",
   "media_gallery.toggle_visible": "Toggle visibility",
diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json
index be8103d1c..c51f3e417 100644
--- a/app/javascript/mastodon/locales/tr.json
+++ b/app/javascript/mastodon/locales/tr.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "Yükleniyor...",
   "media_gallery.toggle_visible": "Görünürlüğü değiştir",
diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json
index 273661462..86c0ce76d 100644
--- a/app/javascript/mastodon/locales/uk.json
+++ b/app/javascript/mastodon/locales/uk.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "Завантаження...",
   "media_gallery.toggle_visible": "Показати/приховати",
diff --git a/app/javascript/mastodon/locales/whitelist_gl.json b/app/javascript/mastodon/locales/whitelist_gl.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_gl.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json
index dbb9584c6..15a68c915 100644
--- a/app/javascript/mastodon/locales/zh-HK.json
+++ b/app/javascript/mastodon/locales/zh-HK.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "載入中...",
   "media_gallery.toggle_visible": "打開或關上",
diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json
index 0b05a83cd..1bdc883a8 100644
--- a/app/javascript/mastodon/locales/zh-TW.json
+++ b/app/javascript/mastodon/locales/zh-TW.json
@@ -133,7 +133,7 @@
   "lists.edit": "Edit list",
   "lists.new.create": "Add list",
   "lists.new.title_placeholder": "New list title",
-  "lists.search": "Search among follows",
+  "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "loading_indicator.label": "讀取中...",
   "media_gallery.toggle_visible": "切換可見性",
diff --git a/app/javascript/mastodon/middleware/sounds.js b/app/javascript/mastodon/middleware/sounds.js
index 3d1e3eaba..9f1bc02b9 100644
--- a/app/javascript/mastodon/middleware/sounds.js
+++ b/app/javascript/mastodon/middleware/sounds.js
@@ -15,7 +15,7 @@ const play = audio => {
     if (typeof audio.fastSeek === 'function') {
       audio.fastSeek(0);
     } else {
-      audio.seek(0);
+      audio.currentTime = 0;
     }
   }
 
diff --git a/app/javascript/mastodon/reducers/status_lists.js b/app/javascript/mastodon/reducers/status_lists.js
index c4aeb338f..6c5f33557 100644
--- a/app/javascript/mastodon/reducers/status_lists.js
+++ b/app/javascript/mastodon/reducers/status_lists.js
@@ -1,6 +1,10 @@
 import {
+  FAVOURITED_STATUSES_FETCH_REQUEST,
   FAVOURITED_STATUSES_FETCH_SUCCESS,
+  FAVOURITED_STATUSES_FETCH_FAIL,
+  FAVOURITED_STATUSES_EXPAND_REQUEST,
   FAVOURITED_STATUSES_EXPAND_SUCCESS,
+  FAVOURITED_STATUSES_EXPAND_FAIL,
 } from '../actions/favourites';
 import {
   PINNED_STATUSES_FETCH_SUCCESS,
@@ -30,6 +34,7 @@ const normalizeList = (state, listType, statuses, next) => {
   return state.update(listType, listMap => listMap.withMutations(map => {
     map.set('next', next);
     map.set('loaded', true);
+    map.set('isLoading', false);
     map.set('items', ImmutableList(statuses.map(item => item.id)));
   }));
 };
@@ -37,6 +42,7 @@ const normalizeList = (state, listType, statuses, next) => {
 const appendToList = (state, listType, statuses, next) => {
   return state.update(listType, listMap => listMap.withMutations(map => {
     map.set('next', next);
+    map.set('isLoading', false);
     map.set('items', map.get('items').concat(statuses.map(item => item.id)));
   }));
 };
@@ -55,6 +61,12 @@ const removeOneFromList = (state, listType, status) => {
 
 export default function statusLists(state = initialState, action) {
   switch(action.type) {
+  case FAVOURITED_STATUSES_FETCH_REQUEST:
+  case FAVOURITED_STATUSES_EXPAND_REQUEST:
+    return state.setIn(['favourites', 'isLoading'], true);
+  case FAVOURITED_STATUSES_FETCH_FAIL:
+  case FAVOURITED_STATUSES_EXPAND_FAIL:
+    return state.setIn(['favourites', 'isLoading'], false);
   case FAVOURITED_STATUSES_FETCH_SUCCESS:
     return normalizeList(state, 'favourites', action.statuses, action.next);
   case FAVOURITED_STATUSES_EXPAND_SUCCESS:
diff --git a/app/javascript/mastodon/settings.js b/app/javascript/mastodon/settings.js
new file mode 100644
index 000000000..dbd969cb1
--- /dev/null
+++ b/app/javascript/mastodon/settings.js
@@ -0,0 +1,46 @@
+export default class Settings {
+
+  constructor(keyBase = null) {
+    this.keyBase = keyBase;
+  }
+
+  generateKey(id) {
+    return this.keyBase ? [this.keyBase, `id${id}`].join('.') : id;
+  }
+
+  set(id, data) {
+    const key = this.generateKey(id);
+    try {
+      const encodedData = JSON.stringify(data);
+      localStorage.setItem(key, encodedData);
+      return data;
+    } catch (e) {
+      return null;
+    }
+  }
+
+  get(id) {
+    const key = this.generateKey(id);
+    try {
+      const rawData = localStorage.getItem(key);
+      return JSON.parse(rawData);
+    } catch (e) {
+      return null;
+    }
+  }
+
+  remove(id) {
+    const data = this.get(id);
+    if (data) {
+      const key = this.generateKey(id);
+      try {
+        localStorage.removeItem(key);
+      } catch (e) {
+      }
+    }
+    return data;
+  }
+
+}
+
+export const pushNotificationsSetting = new Settings('mastodon_push_notification_data');
diff --git a/app/javascript/mastodon/web_push_subscription.js b/app/javascript/mastodon/web_push_subscription.js
index 3dbed09ea..17aca4060 100644
--- a/app/javascript/mastodon/web_push_subscription.js
+++ b/app/javascript/mastodon/web_push_subscription.js
@@ -1,6 +1,7 @@
 import axios from 'axios';
 import { store } from './containers/mastodon';
 import { setBrowserSupport, setSubscription, clearSubscription } from './actions/push_notifications';
+import { pushNotificationsSetting } from './settings';
 
 // Taken from https://www.npmjs.com/package/web-push
 const urlBase64ToUint8Array = (base64String) => {
@@ -35,16 +36,33 @@ const subscribe = (registration) =>
 const unsubscribe = ({ registration, subscription }) =>
   subscription ? subscription.unsubscribe().then(() => registration) : registration;
 
-const sendSubscriptionToBackend = (subscription) =>
-  axios.post('/api/web/push_subscriptions', {
-    subscription,
-  }).then(response => response.data);
+const sendSubscriptionToBackend = (subscription) => {
+  const params = { subscription };
+
+  const me = store.getState().getIn(['meta', 'me']);
+  if (me) {
+    const data = pushNotificationsSetting.get(me);
+    if (data) {
+      params.data = data;
+    }
+  }
+
+  return axios.post('/api/web/push_subscriptions', params).then(response => response.data);
+};
 
 // Last one checks for payload support: https://web-push-book.gauntface.com/chapter-06/01-non-standards-browsers/#no-payload
 const supportsPushNotifications = ('serviceWorker' in navigator && 'PushManager' in window && 'getKey' in PushSubscription.prototype);
 
 export function register () {
   store.dispatch(setBrowserSupport(supportsPushNotifications));
+  const me = store.getState().getIn(['meta', 'me']);
+
+  if (me && !pushNotificationsSetting.get(me)) {
+    const alerts = store.getState().getIn(['push_notifications', 'alerts']);
+    if (alerts) {
+      pushNotificationsSetting.set(me, { alerts: alerts });
+    }
+  }
 
   if (supportsPushNotifications) {
     if (!getApplicationServerKey()) {
@@ -79,6 +97,9 @@ export function register () {
         // it means that the backend subscription is valid (and was set during hydration)
         if (!(subscription instanceof PushSubscription)) {
           store.dispatch(setSubscription(subscription));
+          if (me) {
+            pushNotificationsSetting.set(me, { alerts: subscription.alerts });
+          }
         }
       })
       .catch(error => {
@@ -90,6 +111,9 @@ export function register () {
 
         // Clear alerts and hide UI settings
         store.dispatch(clearSubscription());
+        if (me) {
+          pushNotificationsSetting.remove(me);
+        }
 
         try {
           getRegistration()
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 17322264e..da789ba06 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -803,7 +803,7 @@
     .emojione {
       width: 24px;
       height: 24px;
-      margin: -3px 0 0;
+      margin: -1px 0 0;
     }
   }
 
@@ -2273,14 +2273,19 @@ button.icon-button.active i.fa-retweet {
   .status-card__image-image {
     border-radius: 4px 4px 0 0;
   }
+
+  .status-card__title {
+    white-space: inherit;
+  }
 }
 
 .status-card__image-image {
   border-radius: 4px 0 0 4px;
   display: block;
-  height: auto;
   margin: 0;
   width: 100%;
+  height: 100%;
+  object-fit: cover;
 }
 
 .load-more {
@@ -3998,6 +4003,7 @@ button.icon-button.active i.fa-retweet {
   position: relative;
   background: $base-shadow-color;
   max-width: 100%;
+  border-radius: 4px;
 
   video {
     height: 100%;
@@ -4032,8 +4038,8 @@ button.icon-button.active i.fa-retweet {
     left: 0;
     right: 0;
     box-sizing: border-box;
-    background: linear-gradient(0deg, rgba($base-shadow-color, 0.8) 0, rgba($base-shadow-color, 0.35) 60%, transparent);
-    padding: 0 10px;
+    background: linear-gradient(0deg, rgba($base-shadow-color, 0.85) 0, rgba($base-shadow-color, 0.45) 60%, transparent);
+    padding: 0 15px;
     opacity: 0;
     transition: opacity .1s ease;
 
@@ -4086,40 +4092,67 @@ button.icon-button.active i.fa-retweet {
     }
   }
 
-  &__buttons {
+  &__buttons-bar {
+    display: flex;
+    justify-content: space-between;
     padding-bottom: 10px;
+  }
+
+  &__buttons {
     font-size: 16px;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
 
     &.left {
-      float: left;
-
       button {
-        padding-right: 10px;
+        padding-left: 0;
       }
     }
 
     &.right {
-      float: right;
-
       button {
-        padding-left: 10px;
+        padding-right: 0;
       }
     }
 
     button {
       background: transparent;
-      padding: 0;
+      padding: 2px 10px;
+      font-size: 16px;
       border: 0;
-      color: $white;
+      color: rgba($white, 0.75);
 
       &:active,
       &:hover,
       &:focus {
-        color: $ui-highlight-color;
+        color: $white;
       }
     }
   }
 
+  &__time-sep,
+  &__time-total,
+  &__time-current {
+    font-size: 14px;
+    font-weight: 500;
+  }
+
+  &__time-current {
+    color: $white;
+    margin-left: 10px;
+  }
+
+  &__time-sep {
+    display: inline-block;
+    margin: 0 6px;
+  }
+
+  &__time-sep,
+  &__time-total {
+    color: $white;
+  }
+
   &__seek {
     cursor: pointer;
     height: 24px;
@@ -4129,6 +4162,7 @@ button.icon-button.active i.fa-retweet {
       content: "";
       width: 100%;
       background: rgba($white, 0.35);
+      border-radius: 4px;
       display: block;
       position: absolute;
       height: 4px;
@@ -4140,8 +4174,9 @@ button.icon-button.active i.fa-retweet {
       display: block;
       position: absolute;
       height: 4px;
+      border-radius: 4px;
       top: 10px;
-      background: $ui-highlight-color;
+      background: lighten($ui-highlight-color, 8%);
     }
 
     &__buffer {
@@ -4158,7 +4193,8 @@ button.icon-button.active i.fa-retweet {
       top: 6px;
       margin-left: -6px;
       transition: opacity .1s ease;
-      background: $ui-highlight-color;
+      background: lighten($ui-highlight-color, 8%);
+      box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2);
       pointer-events: none;
 
       &.active {
@@ -4172,6 +4208,16 @@ button.icon-button.active i.fa-retweet {
       }
     }
   }
+
+  &.detailed,
+  &.fullscreen {
+    .video-player__buttons {
+      button {
+        padding-top: 10px;
+        padding-bottom: 10px;
+      }
+    }
+  }
 }
 
 .media-spoiler-video {
diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb
index 31e0abe39..3a985c19b 100644
--- a/app/lib/activitypub/activity/create.rb
+++ b/app/lib/activitypub/activity/create.rb
@@ -20,11 +20,13 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
   private
 
   def process_status
+    media_attachments = process_attachments
+
     ApplicationRecord.transaction do
       @status = Status.create!(status_params)
 
       process_tags(@status)
-      process_attachments(@status)
+      attach_media(@status, media_attachments)
     end
 
     resolve_thread(@status)
@@ -105,22 +107,36 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
     emoji.save
   end
 
-  def process_attachments(status)
+  def process_attachments
     return if @object['attachment'].nil?
 
+    media_attachments = []
+
     as_array(@object['attachment']).each do |attachment|
       next if unsupported_media_type?(attachment['mediaType']) || attachment['url'].blank?
 
       href             = Addressable::URI.parse(attachment['url']).normalize.to_s
-      media_attachment = MediaAttachment.create(status: status, account: status.account, remote_url: href, description: attachment['name'].presence)
+      media_attachment = MediaAttachment.create(account: @account, remote_url: href, description: attachment['name'].presence)
+      media_attachments << media_attachment
 
       next if skip_download?
 
       media_attachment.file_remote_url = href
       media_attachment.save
     end
+
+    media_attachments
   rescue Addressable::URI::InvalidURIError => e
     Rails.logger.debug e
+
+    media_attachments
+  end
+
+  def attach_media(status, media_attachments)
+    return if media_attachments.blank?
+
+    media = MediaAttachment.where(status_id: nil, id: media_attachments.take(4).map(&:id))
+    media.update(status_id: status.id)
   end
 
   def resolve_thread(status)
diff --git a/app/lib/ostatus/activity/creation.rb b/app/lib/ostatus/activity/creation.rb
index 3418e2420..f210e134a 100644
--- a/app/lib/ostatus/activity/creation.rb
+++ b/app/lib/ostatus/activity/creation.rb
@@ -26,6 +26,8 @@ class OStatus::Activity::Creation < OStatus::Activity::Base
     cached_reblog = reblog
     status = nil
 
+    media_attachments = save_media
+
     ApplicationRecord.transaction do
       status = Status.create!(
         uri: id,
@@ -44,7 +46,7 @@ class OStatus::Activity::Creation < OStatus::Activity::Base
 
       save_mentions(status)
       save_hashtags(status)
-      save_media(status)
+      attach_media(status, media_attachments)
       save_emojis(status)
     end
 
@@ -126,18 +128,20 @@ class OStatus::Activity::Creation < OStatus::Activity::Base
     ProcessHashtagsService.new.call(parent, tags)
   end
 
-  def save_media(parent)
-    do_not_download = DomainBlock.find_by(domain: parent.account.domain)&.reject_media?
+  def save_media
+    do_not_download = DomainBlock.find_by(domain: @account.domain)&.reject_media?
+    media_attachments = []
 
     @xml.xpath('./xmlns:link[@rel="enclosure"]', xmlns: OStatus::TagManager::XMLNS).each do |link|
       next unless link['href']
 
-      media = MediaAttachment.where(status: parent, remote_url: link['href']).first_or_initialize(account: parent.account, status: parent, remote_url: link['href'])
+      media = MediaAttachment.where(status: nil, remote_url: link['href']).first_or_initialize(account: @account, status: nil, remote_url: link['href'])
       parsed_url = Addressable::URI.parse(link['href']).normalize
 
       next if !%w(http https).include?(parsed_url.scheme) || parsed_url.host.empty?
 
       media.save
+      media_attachments << media
 
       next if do_not_download
 
@@ -148,6 +152,15 @@ class OStatus::Activity::Creation < OStatus::Activity::Base
         next
       end
     end
+
+    media_attachments
+  end
+
+  def attach_media(parent, media_attachments)
+    return if media_attachments.blank?
+
+    media = MediaAttachment.where(status_id: nil, id: media_attachments.take(4).map(&:id))
+    media.update(status_id: parent.id)
   end
 
   def save_emojis(parent)
diff --git a/app/models/account.rb b/app/models/account.rb
index 48b17bbb8..c75ea028e 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -287,6 +287,7 @@ class Account < ApplicationRecord
         FROM accounts
         WHERE #{query} @@ #{textsearch}
           AND accounts.suspended = false
+          AND accounts.moved_to_account_id IS NULL
         ORDER BY rank DESC
         LIMIT ?
       SQL
@@ -312,6 +313,7 @@ class Account < ApplicationRecord
           WHERE accounts.id IN (SELECT * FROM first_degree)
             AND #{query} @@ #{textsearch}
             AND accounts.suspended = false
+            AND accounts.moved_to_account_id IS NULL
           GROUP BY accounts.id
           ORDER BY rank DESC
           LIMIT ?
@@ -327,6 +329,7 @@ class Account < ApplicationRecord
           LEFT OUTER JOIN follows AS f ON (accounts.id = f.account_id AND f.target_account_id = ?) OR (accounts.id = f.target_account_id AND f.account_id = ?)
           WHERE #{query} @@ #{textsearch}
             AND accounts.suspended = false
+            AND accounts.moved_to_account_id IS NULL
           GROUP BY accounts.id
           ORDER BY rank DESC
           LIMIT ?
diff --git a/app/models/list.rb b/app/models/list.rb
index 910864b26..be85c3b87 100644
--- a/app/models/list.rb
+++ b/app/models/list.rb
@@ -4,7 +4,7 @@
 # Table name: lists
 #
 #  id         :integer          not null, primary key
-#  account_id :integer
+#  account_id :integer          not null
 #  title      :string           default(""), not null
 #  created_at :datetime         not null
 #  updated_at :datetime         not null
@@ -13,6 +13,8 @@
 class List < ApplicationRecord
   include Paginable
 
+  PER_ACCOUNT_LIMIT = 50
+
   belongs_to :account
 
   has_many :list_accounts, inverse_of: :list, dependent: :destroy
@@ -20,6 +22,10 @@ class List < ApplicationRecord
 
   validates :title, presence: true
 
+  validates_each :account_id, on: :create do |record, _attr, value|
+    record.errors.add(:base, I18n.t('lists.errors.limit')) if List.where(account_id: value).count >= PER_ACCOUNT_LIMIT
+  end
+
   before_destroy :clean_feed_manager
 
   private
diff --git a/app/models/preview_card.rb b/app/models/preview_card.rb
index 5baddba8a..716b82243 100644
--- a/app/models/preview_card.rb
+++ b/app/models/preview_card.rb
@@ -33,7 +33,7 @@ class PreviewCard < ApplicationRecord
 
   has_and_belongs_to_many :statuses
 
-  has_attached_file :image, styles: { original: '280x280>' }, convert_options: { all: '-quality 80 -strip' }
+  has_attached_file :image, styles: { original: '400x400>' }, convert_options: { all: '-quality 80 -strip' }
 
   include Attachmentable
   include Remotable
diff --git a/app/models/tag.rb b/app/models/tag.rb
index 0fa08e157..dc2c8d129 100644
--- a/app/models/tag.rb
+++ b/app/models/tag.rb
@@ -23,7 +23,7 @@ class Tag < ApplicationRecord
 
   class << self
     def search_for(term, limit = 5)
-      pattern = sanitize_sql_like(term) + '%'
+      pattern = sanitize_sql_like(term.strip) + '%'
       Tag.where('lower(name) like lower(?)', pattern).order(:name).limit(limit)
     end
   end
diff --git a/app/services/account_search_service.rb b/app/services/account_search_service.rb
index a289ceac4..3be110665 100644
--- a/app/services/account_search_service.rb
+++ b/app/services/account_search_service.rb
@@ -4,7 +4,7 @@ class AccountSearchService < BaseService
   attr_reader :query, :limit, :options, :account
 
   def call(query, limit, account = nil, options = {})
-    @query   = query
+    @query   = query.strip
     @limit   = limit
     @options = options
     @account = account
diff --git a/app/services/fetch_link_card_service.rb b/app/services/fetch_link_card_service.rb
index cec96d927..7f4518ea7 100644
--- a/app/services/fetch_link_card_service.rb
+++ b/app/services/fetch_link_card_service.rb
@@ -38,7 +38,7 @@ class FetchLinkCardService < BaseService
     @card ||= PreviewCard.new(url: @url)
     res     = Request.new(:head, @url).perform
 
-    return if res.code != 200 || res.mime_type != 'text/html'
+    return if res.code != 405 && (res.code != 200 || res.mime_type != 'text/html')
 
     attempt_oembed || attempt_opengraph
   end
diff --git a/app/services/resolve_remote_account_service.rb b/app/services/resolve_remote_account_service.rb
index 3293fe40f..d7d0be210 100644
--- a/app/services/resolve_remote_account_service.rb
+++ b/app/services/resolve_remote_account_service.rb
@@ -44,7 +44,7 @@ class ResolveRemoteAccountService < BaseService
       if lock.acquired?
         @account = Account.find_remote(@username, @domain)
 
-        if activitypub_ready?
+        if activitypub_ready? || @account&.activitypub?
           handle_activitypub
         else
           handle_ostatus
diff --git a/app/validators/status_pin_validator.rb b/app/validators/status_pin_validator.rb
index 9760e1138..64da04120 100644
--- a/app/validators/status_pin_validator.rb
+++ b/app/validators/status_pin_validator.rb
@@ -2,9 +2,9 @@
 
 class StatusPinValidator < ActiveModel::Validator
   def validate(pin)
-    pin.errors.add(:status, I18n.t('statuses.pin_errors.reblog')) if pin.status.reblog?
-    pin.errors.add(:status, I18n.t('statuses.pin_errors.ownership')) if pin.account_id != pin.status.account_id
-    pin.errors.add(:status, I18n.t('statuses.pin_errors.private')) unless %w(public unlisted).include?(pin.status.visibility)
-    pin.errors.add(:status, I18n.t('statuses.pin_errors.limit')) if pin.account.status_pins.count > 4
+    pin.errors.add(:base, I18n.t('statuses.pin_errors.reblog')) if pin.status.reblog?
+    pin.errors.add(:base, I18n.t('statuses.pin_errors.ownership')) if pin.account_id != pin.status.account_id
+    pin.errors.add(:base, I18n.t('statuses.pin_errors.private')) unless %w(public unlisted).include?(pin.status.visibility)
+    pin.errors.add(:base, I18n.t('statuses.pin_errors.limit')) if pin.account.status_pins.count > 4
   end
 end
diff --git a/app/views/stream_entries/_detailed_status.html.haml b/app/views/stream_entries/_detailed_status.html.haml
index b488bd9ba..d88ec8280 100644
--- a/app/views/stream_entries/_detailed_status.html.haml
+++ b/app/views/stream_entries/_detailed_status.html.haml
@@ -17,16 +17,16 @@
       %p{ style: 'margin-bottom: 0' }<
         %span.p-summary> #{Formatter.instance.format_spoiler(status)}&nbsp;
         %a.status__content__spoiler-link{ href: '#' }= t('statuses.show_more')
-    .e-content{ lang: status.language, style: "display: #{status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }<
-      = Formatter.instance.format(status, custom_emojify: true)
-      - if !status.media_attachments.empty?
-        - if status.media_attachments.first.video?
-          - video = status.media_attachments.first
-          %div{ data: { component: 'Video', props: Oj.dump(src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive?, width: 670, height: 380) }}<
-        - else
-          %div{ data: { component: 'MediaGallery', props: Oj.dump(height: 380, sensitive: status.sensitive?, standalone: true, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, 'reduceMotion': current_account&.user&.setting_reduce_motion, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }) }}<
-      - elsif status.preview_cards.first
-        %div{ data: { component: 'Card', props: Oj.dump('maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_cards.first, serializer: REST::PreviewCardSerializer).as_json) }}<
+    .e-content{ lang: status.language, style: "display: #{status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }= Formatter.instance.format(status, custom_emojify: true)
+
+  - if !status.media_attachments.empty?
+    - if status.media_attachments.first.video?
+      - video = status.media_attachments.first
+      %div{ data: { component: 'Video', props: Oj.dump(src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive?, width: 670, height: 380, detailed: true) }}<
+    - else
+      %div{ data: { component: 'MediaGallery', props: Oj.dump(height: 380, sensitive: status.sensitive?, standalone: true, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, 'reduceMotion': current_account&.user&.setting_reduce_motion, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }) }}<
+  - elsif status.preview_cards.first
+    %div{ data: { component: 'Card', props: Oj.dump('maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_cards.first, serializer: REST::PreviewCardSerializer).as_json) }}
 
   .detailed-status__meta
     %data.dt-published{ value: status.created_at.to_time.iso8601 }
diff --git a/app/views/stream_entries/show.html.haml b/app/views/stream_entries/show.html.haml
index 895a61247..b52334a28 100644
--- a/app/views/stream_entries/show.html.haml
+++ b/app/views/stream_entries/show.html.haml
@@ -1,3 +1,6 @@
+- content_for :page_title do
+  = t('statuses.title', name: display_name(@account), quote: truncate(@stream_entry.activity.text, length: 50, omission: '…'))
+
 - content_for :header_tags do
   - if @account.user&.setting_noindex
     %meta{ name: 'robots', content: 'noindex' }/
diff --git a/app/views/tags/show.html.haml b/app/views/tags/show.html.haml
index e05fe1c39..03f19e20a 100644
--- a/app/views/tags/show.html.haml
+++ b/app/views/tags/show.html.haml
@@ -19,8 +19,11 @@
           %p= t 'about.about_hashtag_html', hashtag: @tag.name
 
           .cta
-            = link_to t('auth.login'), new_user_session_path, class: 'button button-secondary'
-            = link_to t('about.learn_more'), root_url, class: 'button button-alternative'
+            - if user_signed_in?
+              = link_to t('settings.back'), root_path, class: 'button button-secondary'
+            - else
+              = link_to t('auth.login'), new_user_session_path, class: 'button button-secondary'
+            = link_to t('about.learn_more'), about_path, class: 'button button-alternative'
 
         .features-list
           .features-list__row
diff --git a/config/application.rb b/config/application.rb
index b54ea1c40..26df20513 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -40,6 +40,7 @@ module Mastodon
       :fa,
       :fi,
       :fr,
+      :gl,
       :he,
       :hr,
       :hu,
diff --git a/config/initializers/paperclip.rb b/config/initializers/paperclip.rb
index 14bd034e6..8aa1d1b6e 100644
--- a/config/initializers/paperclip.rb
+++ b/config/initializers/paperclip.rb
@@ -14,40 +14,45 @@ Paperclip::Attachment.default_options.merge!(
 )
 
 if ENV['S3_ENABLED'] == 'true'
-  require 'fog/aws'
+  require 'aws-sdk'
+  Aws.eager_autoload!(services: %w(S3))
 
-  s3_protocol           = ENV.fetch('S3_PROTOCOL') { 'https' }
-  s3_hostname           = ENV.fetch('S3_HOSTNAME') { "s3-#{ENV['S3_REGION']}.amazonaws.com" }
-  aws_signature_version = ENV['S3_SIGNATURE_VERSION'] == 's3' ? 2 : ENV['S3_SIGNATURE_VERSION'].to_i
-  aws_signature_version = 4 if aws_signature_version.zero?
+  s3_region   = ENV.fetch('S3_REGION')   { 'us-east-1' }
+  s3_protocol = ENV.fetch('S3_PROTOCOL') { 'https' }
+  s3_hostname = ENV.fetch('S3_HOSTNAME') { "s3-#{s3_region}.amazonaws.com" }
 
   Paperclip::Attachment.default_options.merge!(
-    fog_credentials: {
-      provider: 'AWS',
-      aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
-      aws_secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'],
-      aws_signature_version: aws_signature_version,
-      region: ENV.fetch('S3_REGION') { 'us-east-1' },
-      scheme: s3_protocol,
-      host: s3_hostname
+    storage: :s3,
+    s3_protocol: s3_protocol,
+    s3_host_name: s3_hostname,
+    s3_headers: {
+      'Cache-Control' => 'max-age=315576000',
+    },
+    s3_permissions: ENV.fetch('S3_PERMISSION') { 'public-read' },
+    s3_region: s3_region,
+    s3_credentials: {
+      bucket: ENV['S3_BUCKET'],
+      access_key_id: ENV['AWS_ACCESS_KEY_ID'],
+      secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'],
     },
-    fog_directory: ENV['S3_BUCKET'],
-    fog_options: {
-      acl: ENV.fetch('S3_PERMISSION') { 'public-read' },
-      cache_control: 'max-age=315576000',
+    s3_options: {
+      signature_version: ENV.fetch('S3_SIGNATURE_VERSION') { 'v4' },
     }
   )
 
   if ENV.has_key?('S3_ENDPOINT')
-    Paperclip::Attachment.default_options[:fog_credentials].merge!(
+    Paperclip::Attachment.default_options[:s3_options].merge!(
       endpoint: ENV['S3_ENDPOINT'],
-      path_style: true
+      force_path_style: true
     )
-    Paperclip::Attachment.default_options[:fog_host] = "#{s3_protocol}://#{s3_hostname}/#{ENV['S3_BUCKET']}"
+    Paperclip::Attachment.default_options[:url] = ':s3_path_url'
   end
 
   if ENV.has_key?('S3_CLOUDFRONT_HOST')
-    Paperclip::Attachment.default_options[:fog_host] = "#{s3_protocol}://#{ENV['S3_CLOUDFRONT_HOST']}"
+    Paperclip::Attachment.default_options.merge!(
+      url: ':s3_alias_url',
+      s3_host_alias: ENV['S3_CLOUDFRONT_HOST']
+    )
   end
 elsif ENV['SWIFT_ENABLED'] == 'true'
   require 'fog/openstack'
diff --git a/config/initializers/rack_attack.rb b/config/initializers/rack_attack.rb
index 53cb106ca..b38fb302b 100644
--- a/config/initializers/rack_attack.rb
+++ b/config/initializers/rack_attack.rb
@@ -1,6 +1,43 @@
 # frozen_string_literal: true
 
+require 'doorkeeper/grape/authorization_decorator'
+
 class Rack::Attack
+  class Request
+    def authenticated_token
+      return @token if defined?(@token)
+
+      @token = Doorkeeper::OAuth::Token.authenticate(
+        Doorkeeper::Grape::AuthorizationDecorator.new(self),
+        *Doorkeeper.configuration.access_token_methods
+      )
+    end
+
+    def authenticated_user_id
+      authenticated_token&.resource_owner_id
+    end
+
+    def unauthenticated?
+      !authenticated_user_id
+    end
+
+    def api_request?
+      path.start_with?('/api')
+    end
+
+    def web_request?
+      !api_request?
+    end
+  end
+
+  PROTECTED_PATHS = %w(
+    /auth/sign_in
+    /auth
+    /auth/password
+  ).freeze
+
+  PROTECTED_PATHS_REGEX = Regexp.union(PROTECTED_PATHS.map { |path| /\A#{Regexp.escape(path)}/ })
+
   # Always allow requests from localhost
   # (blocklist & throttles are skipped)
   Rack::Attack.safelist('allow from localhost') do |req|
@@ -8,24 +45,16 @@ class Rack::Attack
     '127.0.0.1' == req.ip || '::1' == req.ip
   end
 
-  # Rate limits for the API
-  throttle('api', limit: 300, period: 5.minutes) do |req|
-    req.ip if req.path =~ /\A\/api\/v/
-  end
-
-  # Rate limit logins
-  throttle('login', limit: 5, period: 5.minutes) do |req|
-    req.ip if req.path == '/auth/sign_in' && req.post?
+  throttle('throttle_authenticated_api', limit: 300, period: 5.minutes) do |req|
+    req.api_request? && req.authenticated_user_id
   end
 
-  # Rate limit sign-ups
-  throttle('register', limit: 5, period: 5.minutes) do |req|
-    req.ip if req.path == '/auth' && req.post?
+  throttle('throttle_unauthenticated_api', limit: 7_500, period: 5.minutes) do |req|
+    req.ip if req.api_request?
   end
 
-  # Rate limit forgotten passwords
-  throttle('reminder', limit: 5, period: 5.minutes) do |req|
-    req.ip if req.path == '/auth/password' && req.post?
+  throttle('protected_paths', limit: 5, period: 5.minutes) do |req|
+    req.ip if req.post? && req.path =~ PROTECTED_PATHS_REGEX
   end
 
   self.throttled_response = lambda do |env|
diff --git a/config/locales/activerecord.ar.yml b/config/locales/activerecord.ar.yml
new file mode 100644
index 000000000..d5d44aaa6
--- /dev/null
+++ b/config/locales/activerecord.ar.yml
@@ -0,0 +1,13 @@
+---
+ar:
+  activerecord:
+    errors:
+      models:
+        account:
+          attributes:
+            username:
+              invalid: فقط حروف و أرقام و تسطير سفلي
+        status:
+          attributes:
+            reblog:
+              taken: المنشور موجود
diff --git a/config/locales/ar.yml b/config/locales/ar.yml
index a96b353c1..cc9594179 100644
--- a/config/locales/ar.yml
+++ b/config/locales/ar.yml
@@ -57,6 +57,19 @@ ar:
       order:
         title: الترتيب
       profile_url: رابط الملف الشخصي
+    custom_emojis:
+      delete: حذف
+    email_domain_blocks:
+      delete: حذف
+    reports:
+      delete: حذف
+    settings:
+      registrations:
+        deletion:
+          desc_html: السماح لأي مستخدم إغلاق حسابه
+    statuses:
+      batch:
+        delete: حذف
   application_mailer:
     settings: 'تغيير تفضيلات البريد الإلكتروني : %{link}'
     signature: إشعارات ماستدون من %{instance}
@@ -65,6 +78,7 @@ ar:
     invalid_url: إن الرابط المقدم غير صالح
   auth:
     change_password: الهوية
+    delete_account: حذف حساب
     didnt_get_confirmation: لم تتلق تعليمات التأكيد ؟
     forgot_password: نسيت كلمة المرور ؟
     login: تسجيل الدخول
@@ -91,6 +105,8 @@ ar:
       x_minutes: "%{count}د"
       x_months: "%{count} شه"
       x_seconds: "%{count}ث"
+  deletes:
+    proceed: حذف حساب
   exports:
     blocks: قمت بحظر
     csv: CSV
@@ -197,7 +213,7 @@ ar:
     recovery_codes: النسخ الإحتياطي لرموز الإسترجاع
     recovery_codes_regenerated: تم إعادة توليد رموز الإسترجاع الإحتياطية بنجاح
     setup: تنشيط
-    wrong_code: الرمز الذي أدخلته غير صالح. تحقق من صحة الوقت على الخادم و الجهاز.
+    wrong_code: الرمز الذي أدخلته غير صالح ! تحقق من صحة الوقت على الخادم و الجهاز ؟
   users:
     invalid_email: عنوان البريد الإلكتروني غير صالح
     invalid_otp_token: الرمز الثنائي غير صالح
diff --git a/config/locales/de.yml b/config/locales/de.yml
index 470395767..39867e373 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -49,7 +49,7 @@ de:
     reserved_username: Dieser Profilname ist belegt
     roles:
       admin: Admin
-      moderator: Mod
+      moderator: Moderator
     unfollow: Entfolgen
   admin:
     account_moderation_notes:
@@ -174,6 +174,7 @@ de:
       shortcode: Shortcode
       shortcode_hint: Mindestens 2 Zeichen, nur Buchstaben, Ziffern und Unterstriche
       title: Eigene Emojis
+      updated_msg: Emoji erfolgreich aktualisiert!
       upload: Hochladen
     domain_blocks:
       add_new: Neu hinzufügen
@@ -223,6 +224,13 @@ de:
       reset: Zurücksetzen
       search: Suchen
       title: Bekannte Instanzen
+    invites:
+      filter:
+        all: Alle
+        available: Verfügbar
+        expired: Ausgelaufen
+        title: Filter
+      title: Einladungen
     reports:
       action_taken_by: Maßnahme ergriffen durch
       are_you_sure: Bist du dir sicher?
@@ -261,6 +269,8 @@ de:
         deletion:
           desc_html: Allen erlauben, ihr Konto eigenmächtig zu löschen
           title: Kontolöschung erlauben
+        min_invite_role:
+          disabled: Niemand
         open:
           desc_html: Allen erlauben, ein Konto zu erstellen
           title: Registrierung öffnen
@@ -412,12 +422,31 @@ de:
       following: Folgeliste
       muting: Stummschaltungsliste
     upload: Hochladen
+  invites:
+    delete: Deaktivieren
+    expires_in:
+      '1800': 30 Minuten
+      '21600': 6 Stunden
+      '3600': 1 Stunde
+      '43200': 12 Stunden
+      '86400': 1 Tag
+    expires_in_prompt: Nie
+    generate: Generieren
+    max_uses:
+      one: 1 mal verwendet
+      other: "%{count} mal verwendet"
+    max_uses_prompt: Kein Limit
   landing_strip_html: "<strong>%{name}</strong> hat ein Profil auf %{link_to_root_path}. Du kannst folgen oder interagieren, sofern du ein Konto irgendwo im Fediversum hast."
   landing_strip_signup_html: Wenn nicht, kannst du dich <a href="%{sign_up_path}">hier anmelden</a>.
   media_attachments:
     validations:
       images_and_video: Es kann kein Video an einen Beitrag, der bereits Bilder enthält, angehängt werden
       too_many: Es können nicht mehr als 4 Bilder angehängt werden
+  migrations:
+    acct: benutzername@domain des neuen Accounts
+    proceed: Speichern
+  moderation:
+    title: Moderation
   notification_mailer:
     digest:
       body: 'Hier ist eine kurze Zusammenfasung dessen, was du auf %{instance} seit deinem letzten Besuch am %{since} verpasst hast:'
diff --git a/config/locales/devise.fr.yml b/config/locales/devise.fr.yml
index cb4d5d5f6..a9817044d 100644
--- a/config/locales/devise.fr.yml
+++ b/config/locales/devise.fr.yml
@@ -19,7 +19,7 @@ fr:
       confirmation_instructions:
         subject: Merci de confirmer votre inscription sur %{instance}
       password_change:
-        subject: Votre mot de passe a été modifié avec succés.
+        subject: Votre mot de passe a été modifié avec succès.
       reset_password_instructions:
         subject: Instructions pour changer votre mot de passe
       unlock_instructions:
diff --git a/config/locales/devise.ja.yml b/config/locales/devise.ja.yml
index aa333920e..2cd20732f 100644
--- a/config/locales/devise.ja.yml
+++ b/config/locales/devise.ja.yml
@@ -51,11 +51,11 @@ ja:
       unlocked: アカウントロックは正常に解除されました。続行するにはログインしてください。
   errors:
     messages:
-      already_confirmed: は確認されました。ログインを試してください。
-      confirmation_period_expired: "%{period}以内に確認が必要です。再度試してください。"
-      expired: は期限切れです。再度試してください。
-      not_found: 見つかりません。
-      not_locked: ロックされていません。
+      already_confirmed: は確認されました。ログインを試してください
+      confirmation_period_expired: "%{period}以内に確認が必要です。再度試してください"
+      expired: は期限切れです。再度試してください
+      not_found: 見つかりません
+      not_locked: ロックされていません
       not_saved:
         one: エラーが発生したため、%{resource}の保存に失敗しました。
         other: "%{count}個のエラーが発生したため、保存に失敗しました。 %{resource}"
diff --git a/config/locales/devise.no.yml b/config/locales/devise.no.yml
index 1bb14d265..5d3e71495 100644
--- a/config/locales/devise.no.yml
+++ b/config/locales/devise.no.yml
@@ -34,7 +34,7 @@
       updated: Passordet ditt er endret. Du er nå logget inn.
       updated_not_active: Passordet ditt er endret.
     registrations:
-      destroyed: Adjø! Kontoen din er slettet. På gjensyn!
+      destroyed: Adjø! Kontoen din er slettet. På gjensyn.
       signed_up: Velkommen! Registreringen var vellykket.
       signed_up_but_inactive: Registreringen var vellykket. Vi kunne dessverre ikke logge deg inn fordi kontoen din ennå ikke har blitt aktivert.
       signed_up_but_locked: Registreringen var vellykket. Vi kunne dessverre ikke logge deg inn fordi kontoen din har blitt låst.
@@ -51,8 +51,8 @@
       unlocked: Kontoen din ble åpnet uten problemer. Logg på for å fortsette.
   errors:
     messages:
-      already_confirmed: har allerede blitt bekreftet, prøv å logge på istedet.
-      confirmation_period_expired: må bekreftes innen %{period}. Spør om en ny e-mail for bekreftelse istedet.
+      already_confirmed: har allerede blitt bekreftet, prøv å logge på istedet
+      confirmation_period_expired: må bekreftes innen %{period}. Spør om en ny e-post for bekreftelse istedet
       expired: har utløpt, spør om en ny en istedet
       not_found: ikke funnet
       not_locked: var ikke låst
diff --git a/config/locales/doorkeeper.ar.yml b/config/locales/doorkeeper.ar.yml
index 1925d5a65..107677837 100644
--- a/config/locales/doorkeeper.ar.yml
+++ b/config/locales/doorkeeper.ar.yml
@@ -5,6 +5,8 @@ ar:
       doorkeeper/application:
         name: التسمية
         redirect_uri: Redirect URI
+        scopes: المجالات
+        website: تطبيق الويب
     errors:
       models:
         doorkeeper/application:
@@ -33,9 +35,13 @@ ar:
         redirect_uri: إستخدم خطا واحدا لكل رابط
         scopes: Separate scopes with spaces. Leave blank to use the default scopes.
       index:
+        application: تطبيق
         callback_url: رابط رد النداء
+        delete: حذف
         name: التسمية
         new: تطبيق جديد
+        scopes: المجالات
+        show: عرض
         title: تطبيقاتك
       new:
         title: تطبيق جديد
@@ -43,7 +49,7 @@ ar:
         actions: Actions
         application_id: معرف التطبيق
         callback_urls: روابط رد النداء
-        scopes: Scopes
+        scopes: المجالات
         secret: السر
         title: 'تطبيق : %{name}'
     authorizations:
@@ -67,7 +73,7 @@ ar:
         application: التطبيق
         created_at: صُرّح له في
         date_format: "%d-%m-%Y %H:%M:%S"
-        scopes: Scopes
+        scopes: المجالات
         title: تطبيقاتك المرخص لها
     errors:
       messages:
diff --git a/config/locales/doorkeeper.ja.yml b/config/locales/doorkeeper.ja.yml
index 1f145eaa3..96956c60f 100644
--- a/config/locales/doorkeeper.ja.yml
+++ b/config/locales/doorkeeper.ja.yml
@@ -29,10 +29,10 @@ ja:
       edit:
         title: アプリの編集
       form:
-        error: フォームにエラーが無いか確認してください。
+        error: フォームにエラーが無いか確認してください
       help:
         native_redirect_uri: ローカルテストに %{native_redirect_uri} を使用
-        redirect_uri: 一行に一つのURLを入力してください。
+        redirect_uri: 一行に一つのURLを入力してください
         scopes: アクセス権は半角スペースで区切ることができます。 空白のままにするとデフォルトを使用します。
       index:
         application: アプリ
@@ -57,11 +57,11 @@ ja:
         authorize: 承認
         deny: 拒否
       error:
-        title: エラーが発生しました。
+        title: エラーが発生しました
       new:
         able_to: このアプリは以下のことができます
-        prompt: アプリ %{client_name} があなたのアカウントへのアクセスを要求しています。
-        title: 認証が必要です。
+        prompt: アプリ %{client_name} があなたのアカウントへのアクセスを要求しています
+        title: 認証が必要です
       show:
         title: 認証コードをコピーしてアプリに貼り付けて下さい。
     authorized_applications:
@@ -83,12 +83,12 @@ ja:
         invalid_grant: 指定された認証許可は無効であるか、期限切れ、取り消されている、リダイレクトURIの不一致、または別のクライアントに発行されています。
         invalid_redirect_uri: 無効なリダイレクトURIが含まれています。
         invalid_request: リクエストに必要なパラメータが欠けているか、サポートされていないパラメータが含まれている、または不正なフォーマットです。
-        invalid_resource_owner: 指定されたリソース所有者のクレデンシャルが無効であるか、リソース所有者が見つかりません。
+        invalid_resource_owner: 指定されたリソース所有者のクレデンシャルが無効であるか、リソース所有者が見つかりません
         invalid_scope: 要求されたアクセス権は無効であるか、不明、または不正なフォーマットです。
         invalid_token:
           expired: アクセストークンの有効期限が切れています
-          revoked: アクセストークンは取り消されています。
-          unknown: アクセストークンが無効です。
+          revoked: アクセストークンは取り消されています
+          unknown: アクセストークンが無効です
         resource_owner_authenticator_not_configured: Doorkeeper.configure.resource_owner_authenticator が設定されていないため、リソース所有者の検索に失敗しました。
         server_error: 認証サーバーに予期せぬ例外が発生したため、リクエストを実行できなくなりました。
         temporarily_unavailable: 現在、認証サーバーに一時的な過負荷が掛かっているか、またはメンテナンス中のため、リクエストを処理できません。
diff --git a/config/locales/doorkeeper.nl.yml b/config/locales/doorkeeper.nl.yml
index 3dd0a7d26..5c9c9047f 100644
--- a/config/locales/doorkeeper.nl.yml
+++ b/config/locales/doorkeeper.nl.yml
@@ -60,10 +60,10 @@ nl:
         title: Er is een fout opgetreden
       new:
         able_to: Deze toepassing zal in staat zijn om
-        prompt: "%{client_name} autoriseren om je account te gebruiken"
+        prompt: "%{client_name} autoriseren om jouw account te gebruiken"
         title: Autorisatie vereist
       show:
-        title: Kopieer deze autorisatiecode en plak het in de applicatie.
+        title: Kopieer deze autorisatiecode en plak het in de toepassing.
     authorized_applications:
       buttons:
         revoke: Intrekken
diff --git a/config/locales/doorkeeper.no.yml b/config/locales/doorkeeper.no.yml
index ba061e0ca..5b4dc9d6c 100644
--- a/config/locales/doorkeeper.no.yml
+++ b/config/locales/doorkeeper.no.yml
@@ -5,6 +5,7 @@
       doorkeeper/application:
         name: Navn
         redirect_uri: Omdirigerings-URI
+        website: Applikasjonsnettside
     errors:
       models:
         doorkeeper/application:
@@ -33,9 +34,12 @@
         redirect_uri: Bruk én linje per URI
         scopes: Adskill omfang med mellomrom. La det være blankt for å bruke standard omfang.
       index:
+        application: Applikasjon
         callback_url: Callback-URL
+        delete: Fjern
         name: Navn
         new: Ny applikasjon
+        show: Vis
         title: Dine applikasjoner
       new:
         title: Nye applikasjoner
@@ -57,7 +61,7 @@
         prompt: Applikasjon %{client_name} spør om tilgang til din konto
         title: Autorisasjon påkrevd
       show:
-        title: Copy this authorization code and paste it to the application.
+        title: Kopier denne koden og lim den inn i programmet.
     authorized_applications:
       buttons:
         revoke: Opphev
@@ -77,7 +81,7 @@
         invalid_grant: Autoriseringen er ugyldig, utløpt, opphevet, stemmer ikke overens med omdirigerings-URIen eller var utstedt til en annen klient.
         invalid_redirect_uri: Den inkluderte omdirigerings-URLen er ikke gyldig.
         invalid_request: Forespørslen mangler en eller flere parametere, inkluderte en parameter som ikke støttes eller har feil struktur.
-        invalid_resource_owner: Ressurseierens detaljer er ikke gyldige, eller så kan ikke eieren finnes.
+        invalid_resource_owner: Ressurseierens detaljer er ikke gyldige, eller så er det ikke mulig å finne eieren
         invalid_scope: Det etterspurte omfanget er ugyldig, ukjent eller har feil struktur.
         invalid_token:
           expired: Tilgangsbeviset har utløpt
diff --git a/config/locales/doorkeeper.oc.yml b/config/locales/doorkeeper.oc.yml
index 1ec1b69e8..d83d07438 100644
--- a/config/locales/doorkeeper.oc.yml
+++ b/config/locales/doorkeeper.oc.yml
@@ -60,7 +60,7 @@ oc:
         title: I a agut un error
       new:
         able_to: Aquesta aplicacion poirà
-        prompt: L’aplicacion %{client_name} demanda l’accès al vòstre compte.
+        prompt: L’aplicacion %{client_name} demanda l’accès al vòstre compte
         title: Cal l’autorizacion
       show:
         title: Copiatz lo còdi d’autorizacion e pegatz-lo dins l’aplicacion.
@@ -83,8 +83,8 @@ oc:
         invalid_grant: L’acòrdi d’autorizacion donadat es pas valid, expirat, revocat, una redireccion URI utilizat en la demanda d’autorizacion no correspond, o a estat desliurat a un altre client.
         invalid_redirect_uri: L’URL de redireccion es pas valida.
         invalid_request: La demanda a un paramètre que li manca, a una valor qu’es pas suportada, o quicòm mal format.
-        invalid_resource_owner: La qualificacion del proprietari de la ressorça donada es pas valid, o lo proprietari de la ressorça se pòt pas trobar.
-        invalid_scope: L’encastre demandat es pas valid, o mal format.
+        invalid_resource_owner: La qualificacion del proprietari de la ressorça donada es pas valida, o lo proprietari de la ressorça es pas trobable
+        invalid_scope: L’encastre demandat es pas valid, o d’un marrit format.
         invalid_token:
           expired: Lo geton d’accès a expirat
           revoked: Lo geton d’accès a estat revocat
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 804f9b199..12284403c 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -343,7 +343,7 @@ en:
     warning: Be very careful with this data. Never share it with anyone!
     your_token: Your access token
   auth:
-    agreement_html: By signing up you agree to <a href="%{rules_path}">our terms of service</a> and <a href="%{terms_path}">privacy policy</a>.
+    agreement_html: By signing up you agree to follow <a href="%{rules_path}">the rules of the instance</a> and <a href="%{terms_path}">our terms of service</a>.
     change_password: Security
     delete_account: Delete account
     delete_account_html: If you wish to delete your account, you can <a href="%{path}">proceed here</a>. You will be asked for confirmation.
@@ -467,6 +467,9 @@ en:
     remove_all: Remove all
   landing_strip_html: "<strong>%{name}</strong> is a user on %{link_to_root_path}. You can follow them or interact with them if you have an account anywhere in the fediverse."
   landing_strip_signup_html: If you don't, you can <a href="%{sign_up_path}">sign up here</a>.
+  lists:
+    errors:
+      limit: You have reached the maximum amount of lists
   media_attachments:
     validations:
       images_and_video: Cannot attach a video to a status that already contains images
@@ -602,11 +605,12 @@ en:
     open_in_web: Open in web
     over_character_limit: character limit of %{max} exceeded
     pin_errors:
-      limit: Too many toots pinned
+      limit: You have already pinned the maximum number of toots
       ownership: Someone else's toot cannot be pinned
       private: Non-public toot cannot be pinned
       reblog: A boost cannot be pinned
     show_more: Show more
+    title: '%{name}: "%{quote}"'
     visibilities:
       private: Followers-only
       private_long: Only show to followers
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index 45243d07e..5a22fdccc 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -342,7 +342,7 @@ fr:
     warning: Soyez prudent⋅e avec ces données. Ne les partagez pas !
     your_token: Votre jeton d’accès
   auth:
-    agreement_html: En vous inscrivant, vous souscrivez à <a href="%{rules_path}">nos conditions d’utilisation</a> ainsi qu’à <a href="%{terms_path}">notre politique de confidentialité</a>.
+    agreement_html: En vous inscrivant, vous souscrivez <a href="%{rules_path}">aux règles de l’instance</a> et à <a href="%{terms_path}">nos conditions d’utilisation</a>.
     change_password: Sécurité
     delete_account: Supprimer le compte
     delete_account_html: Si vous désirez supprimer votre compte, vous pouvez <a href="%{path}">cliquer ici</a>. Il vous sera demandé de confirmer cette action.
@@ -454,7 +454,7 @@ fr:
     table:
       expires_at: Expire
       uses: Utilise
-    title: Personnes invitées
+    title: Inviter des gens
   landing_strip_html: <strong>%{name}</strong> utilise %{link_to_root_path}. Vous pouvez læ suivre et interagir si vous possédez un compte quelque part dans le "fediverse".
   landing_strip_signup_html: Si ce n’est pas le cas, vous pouvez <a href="%{sign_up_path}">en créer un ici</a>.
   media_attachments:
diff --git a/config/locales/ja.yml b/config/locales/ja.yml
index a8c3c9057..48f8b121d 100644
--- a/config/locales/ja.yml
+++ b/config/locales/ja.yml
@@ -24,7 +24,7 @@ ja:
       within_reach_body: デベロッパーフレンドリーな API により実現された、iOS や Android、その他様々なプラットフォームのためのアプリでどこでも友人とやりとりできます。
       within_reach_title: いつでも身近に
     find_another_instance: 他のインスタンスを探す
-    generic_description: "%{domain} は、Mastodon インスタンスの一つです。"
+    generic_description: "%{domain} は、Mastodon インスタンスの一つです"
     hosted_on: Mastodon hosted on %{domain}
     learn_more: もっと詳しく
     other_instances: 他のインスタンス
@@ -40,13 +40,13 @@ ja:
     following: フォロー中
     media: メディア
     moved_html: "%{name} さんは引っ越しました %{new_profile_link}:"
-    nothing_here: 何もありません
+    nothing_here: 何もありません!
     people_followed_by: "%{name} さんがフォロー中のアカウント"
     people_who_follow: "%{name} さんをフォロー中のアカウント"
     posts: トゥート
     posts_with_replies: トゥートと返信
     remote_follow: リモートフォロー
-    reserved_username: このユーザー名は予約されています。
+    reserved_username: このユーザー名は予約されています
     roles:
       admin: Admin
       moderator: Mod
@@ -56,9 +56,9 @@ ja:
       account: モデレータ
       create: 書き込む
       created_at: 日付
-      created_msg: モデレーションメモを書き込みました
+      created_msg: モデレーションメモを書き込みました!
       delete: 削除
-      destroyed_msg: モデレーションメモを削除しました
+      destroyed_msg: モデレーションメモを削除しました!
     accounts:
       are_you_sure: 本当に実行しますか?
       by_domain: ドメイン
@@ -163,25 +163,25 @@ ja:
       copied_msg: 絵文字のコピーをローカルに作成しました
       copy: コピー
       copy_failed_msg: 絵文字のコピーをローカルに作成できませんでした
-      created_msg: 絵文字の追加に成功しました
+      created_msg: 絵文字の追加に成功しました!
       delete: 削除
-      destroyed_msg: 絵文字の削除に成功しました
+      destroyed_msg: 絵文字の削除に成功しました!
       disable: 無効化
       disabled_msg: 絵文字を無効化しました
       emoji: 絵文字
       enable: 有効化
       enabled_msg: 絵文字を有効化しました
-      image_hint: 50KBまでのPNG画像を利用できます。
+      image_hint: 50KBまでのPNG画像を利用できます
       listed: 収載
       new:
         title: 新規カスタム絵文字の追加
       overwrite: 上書き
       shortcode: ショートコード
-      shortcode_hint: 2文字以上の半角英数字とアンダーバーのみ利用できます。
+      shortcode_hint: 2文字以上の半角英数字とアンダーバーのみ利用できます
       title: カスタム絵文字
       unlisted: 未収載
       update_failed_msg: 絵文字を更新できませんでした
-      updated_msg: 絵文字の更新に成功しました
+      updated_msg: 絵文字の更新に成功しました!
       upload: アップロード
     domain_blocks:
       add_new: 新規追加
diff --git a/config/locales/nl.yml b/config/locales/nl.yml
index 2410c1112..91c788ae6 100644
--- a/config/locales/nl.yml
+++ b/config/locales/nl.yml
@@ -85,9 +85,9 @@ nl:
         local: Lokaal
         remote: Extern
         title: Locatie
-      login_status: Aanmeld status
-      media_attachments: Media-bijlagen
-      memorialize: Verander in memoriam
+      login_status: Aanmeldstatus
+      media_attachments: Mediabijlagen
+      memorialize: Verander naar in memoriam
       moderation:
         all: Alles
         silenced: Genegeerd
@@ -115,8 +115,8 @@ nl:
       role: Permissies
       roles:
         admin: Beheerder
-        moderator: Moderateur
-        user: Persoon
+        moderator: Moderator
+        user: Gebruiker
       salmon_url: Salmon-URL
       search: Zoeken
       shared_inbox_url: Gedeelde inbox-URL
@@ -135,22 +135,30 @@ nl:
       web: Webapp
     action_logs:
       actions:
-        confirm_user: "%{name} bevestigd e-mailadres van persoon %{target}"
-        create_custom_emoji: "%{name} heeft de nieuwe emoji %{target} geupload"
-        create_domain_block: "%{name} heeft domein %{target} geblokkeerd"
-        create_email_domain_block: "%{name} heeft e-maildomein %{target} geblacklist"
-        demote_user: "%{name} heeft persoon %{target} gedegradeerd"
-        destroy_domain_block: "%{name} heeft domein %{target} vrijgegeven"
-        destroy_email_domain_block: "%{name} heeft e-maildomein %{target} gewhitelist"
-        destroy_status: "%{name} heeft status van %{target} verwijderd"
-        disable_2fa_user: "%{name} heeft tweefactor voorwaarden van persoon %{target} uitgeschakeld"
-        disable_custom_emoji: "%{name} heeft emoji %{target} uitgeschakeld"
-        disable_user: "%{name} heeft de login van persoon %{target} uitgeschakeld"
-        enable_custom_emoji: "%{name} heeft emoji %{target} ingeschakeld"
-        enable_user: "%{name} heeft de login voor persoon %{target} ingeschakeld"
-        memorialize_account: "%{name} heeft %{target}'s account gewijzigd in een memoriam pagina"
-        promote_user: "%{name} heeft persoon %{target} gepromoveerd"
-        reset_password_user: "%{name} heeft het wachtwoord van gebruiker %{target} opnieuw ingesteld"
+        confirm_user: E-mailadres van gebruiker %{target} is door %{name} bevestigd
+        create_custom_emoji: Nieuwe emoji %{target} is door %{name} geüpload
+        create_domain_block: Domein %{target} is door %{name} geblokkeerd
+        create_email_domain_block: E-maildomein %{target} is door %{name} op de zwarte lijst geplaatst
+        demote_user: Gebruiker %{target} is door %{name} gedegradeerd
+        destroy_domain_block: Domein %{target} is door %{name} gedeblokkeerd
+        destroy_email_domain_block: E-maildomein %{target} is door %{name} op de whitelist geplaatst
+        destroy_status: Toot van %{target} is door %{name} verwijderd
+        disable_2fa_user: Vereisten tweestapsverificatie van %{target} zijn door %{name} uitgeschakeld
+        disable_custom_emoji: Emoji %{target} is door %{name} uitgeschakeld
+        disable_user: Inloggen voor %{target} is door %{name} uitgeschakeld
+        enable_custom_emoji: Emoji %{target} is door %{name} ingeschakeld
+        enable_user: Inloggen voor %{target} is door %{name} ingeschakeld
+        memorialize_account: Account %{target} is door %{name} in een in-memoriampagina veranderd
+        promote_user: Gebruiker %{target} is door %{name} gepromoveerd
+        reset_password_user: Wachtwoord van gebruiker %{target} is door %{name} opnieuw ingesteld
+        resolve_report: Gerapporteerde toots van %{target} zijn door %{name} verworpen
+        silence_account: Account %{target} is door %{name} genegeerd
+        suspend_account: Account %{target} is door %{name} opgeschort
+        unsilence_account: Negeren van account %{target} is door %{name} opgeheven
+        unsuspend_account: Opschorten van account %{target} is door %{name} opgeheven
+        update_custom_emoji: Emoji %{target} is door %{name} bijgewerkt
+        update_status: De toots van %{target} zijn door %{name} bijgewerkt
+      title: Auditlog
     custom_emojis:
       copied_msg: Lokale kopie van emoji maken geslaagd
       copy: Kopiëren
@@ -164,11 +172,16 @@ nl:
       enable: Inschakelen
       enabled_msg: Inschakelen van deze emoji geslaagd
       image_hint: PNG van max. 50KB
+      listed: Weergegeven
       new:
         title: Lokale emoji toevoegen
+      overwrite: Overschrijven
       shortcode: Shortcode
       shortcode_hint: Tenminste 2 tekens (alleen alfanumeriek en underscores)
       title: Lokale emoji’s
+      unlisted: Niet weergegeven
+      update_failed_msg: Deze emoji kon niet worden bijgewerkt
+      updated_msg: Bijwerken van emoji is geslaagd!
       upload: Uploaden
     domain_blocks:
       add_new: Nieuwe toevoegen
@@ -185,7 +198,7 @@ nl:
           suspend: Opschorten
         title: Nieuwe domeinblokkade
       reject_media: Mediabestanden verwerpen
-      reject_media_hint: Verwijderd lokaal opgeslagen mediabestanden en weigert deze in de toekomst te downloaden. Irrelevant voor opgeschorte domeinen.
+      reject_media_hint: Verwijderd lokaal opgeslagen mediabestanden en weigert deze in de toekomst te downloaden. Irrelevant voor opgeschorte domeinen
       severities:
         noop: Geen
         silence: Negeren
@@ -197,7 +210,7 @@ nl:
           other: "%{count} accounts in de database aangepast"
         retroactive:
           silence: Alle genegeerde accounts van dit domein niet meer negeren
-          suspend: Alle opgeschorste accounts van dit domein niet meer opschorten
+          suspend: Alle opgeschorte accounts van dit domein niet meer opschorten
         title: Domeinblokkade voor %{domain} ongedaan maken
         undo: Ongedaan maken
       title: Domeinblokkades
@@ -218,6 +231,13 @@ nl:
       reset: Opnieuw
       search: Zoeken
       title: Bekende servers
+    invites:
+      filter:
+        all: Alles
+        available: Beschikbaar
+        expired: Verlopen
+        title: Filter
+      title: Uitnodigingen
     reports:
       action_taken_by: Actie uitgevoerd door
       are_you_sure: Weet je het zeker?
@@ -256,21 +276,27 @@ nl:
         deletion:
           desc_html: Toestaan dat iedereen hun eigen account kan verwijderen
           title: Verwijderen account toestaan
+        min_invite_role:
+          disabled: Niemand
+          title: Uitnodigingen toestaan door
         open:
           desc_html: Toestaan dat iedereen een account kan registereren
           title: Open registratie
+      show_staff_badge:
+        desc_html: Medewerkersbadge op profielpagina tonen
+        title: Medewerkersbadge tonen
       site_description:
-        desc_html: Dit wordt als een alinea op de voorpagina getoond en gebruikt als meta-tag in de paginabron.<br>Je kan HTML gebruiken, zoals <code>&lt;a&gt;</code> en <code>&lt;em&gt;</code>.
+        desc_html: Dit wordt als een alinea op de voorpagina getoond en gebruikt als meta-tag in de paginabron.<br/>Je kan HTML gebruiken, zoals <code>&lt;a&gt;</code> en <code>&lt;em&gt;</code>.
         title: Omschrijving Mastodon-server
       site_description_extended:
         desc_html: Wordt op de uitgebreide informatiepagina weergegeven<br>Je kan ook hier HTML gebruiken
         title: Uitgebreide omschrijving Mastodon-server
       site_terms:
-        desc_html: Je kan hier jouw eigen privacybeleid, gebruikersvoorwaarden en ander juridisch jargon kwijt. Je kan HTML gebruiken.
+        desc_html: Je kan hier jouw eigen privacybeleid, gebruikersvoorwaarden en ander juridisch jargon kwijt. Je kan HTML gebruiken
         title: Aangepaste gebruikersvoorwaarden
       site_title: Naam Mastodon-server
       thumbnail:
-        desc_html: Gebruikt als voorvertoning voor OpenGraph en de API. 1200x630px aanbevolen.
+        desc_html: Gebruikt als voorvertoning voor OpenGraph en de API. 1200x630px aanbevolen
         title: Thumbnail Mastodon-server
       timeline_preview:
         desc_html: Toon de openbare tijdlijn op de startpagina
@@ -326,6 +352,8 @@ nl:
     invalid_reset_password_token: De code om jouw wachtwoord opnieuw in te stellen is verlopen. Vraag een nieuwe aan.
     login: Aanmelden
     logout: Afmelden
+    migrate_account: Naar een andere account verhuizen
+    migrate_account_html: Wanneer je dit account naar een ander account wilt doorverwijzen, kun je <a href="%{path}">dit hier instellen</a>.
     register: Registreren
     resend_confirmation: Verstuur de bevestigingsinstructies nogmaals
     reset_password: Wachtwoord opnieuw instellen
@@ -369,7 +397,7 @@ nl:
     '422':
       content: Veiligheidsverificatie mislukt. Blokkeer je toevallig cookies?
       title: Veiligheidsverificatie mislukt
-    '429': Te veel verbindingsaanvragen.
+    '429': Te veel verbindingsaanvragen
     '500':
       content: Het spijt ons, er is aan onze kant iets fout gegaan.
       title: Er is iets mis
@@ -407,12 +435,40 @@ nl:
       following: Volglijst
       muting: Negeerlijst
     upload: Uploaden
+  in_memoriam_html: In memoriam.
+  invites:
+    delete: Deactiveren
+    expired: Verlopen
+    expires_in:
+      '1800': 30 minuten
+      '21600': 6 uur
+      '3600': 1 uur
+      '43200': 12 uur
+      '86400': 1 dag
+    expires_in_prompt: Nooit
+    generate: Genereren
+    max_uses:
+      one: 1 keer
+      other: "%{count} keer"
+    max_uses_prompt: Onbeperkt
+    prompt: Genereer en deel speciale links om mensen toegang tot deze Mastodon-server te geven
+    table:
+      expires_at: Verloopt op
+      uses: Aantal keer te gebruiken
+    title: Mensen uitnodigen
   landing_strip_html: "<strong>%{name}</strong> is een gebruiker op %{link_to_root_path}. Je kunt deze volgen en ermee communiceren als je op Mastodon (of ergens anders in de fediverse) een account hebt."
   landing_strip_signup_html: Als je dat niet hebt, kun je je <a href="%{sign_up_path}">hier registreren</a>.
   media_attachments:
     validations:
       images_and_video: Een video kan niet aan een toot met afbeeldingen worden gekoppeld
       too_many: Er kunnen niet meer dan 4 afbeeldingen toegevoegd worden
+  migrations:
+    acct: gebruikersnaam@domein van het nieuwe account
+    currently_redirecting: 'Jouw profiel wordt nu doorverwezen naar:'
+    proceed: Opslaan
+    updated_msg: Jouw accountmigratie-instelling is succesvol bijgewerkt!
+  moderation:
+    title: Moderatie
   notification_mailer:
     digest:
       body: 'Hier is een korte samenvatting van wat je hebt gemist op %{instance} sinds jouw laatste bezoek op %{since}:'
@@ -448,7 +504,7 @@ nl:
           quadrillion: Q
           thousand: K
           trillion: T
-          unit: ''
+          unit: " "
   pagination:
     next: Volgende
     prev: Vorige
@@ -525,6 +581,7 @@ nl:
     export: Exporteren
     followers: Geautoriseerde volgers
     import: Importeren
+    migrate: Accountmigratie
     notifications: Meldingen
     preferences: Voorkeuren
     settings: Instellingen
@@ -637,7 +694,7 @@ nl:
     manual_instructions: Hieronder vind je de geheime code in platte tekst. Voor het geval je de QR-code niet kunt scannen en het handmatig moet invoeren.
     recovery_codes: Herstelcodes back-uppen
     recovery_codes_regenerated: Opnieuw genereren herstelcodes geslaagd
-    recovery_instructions_html: Wanneer je ooit de toegang verliest tot jouw telefoon, kan je met behulp van een van de herstelcodes hieronder opnieuw toegang krijgen tot jouw account. Zorg ervoor dat je de herstelcodes op een veilige plek bewaard. (Je kunt ze bijvoorbeeld printen en ze samen met andere belangrijke documenten bewaren.)
+    recovery_instructions_html: Wanneer je ooit de toegang verliest tot jouw telefoon, kan je met behulp van een van de herstelcodes hieronder opnieuw toegang krijgen tot jouw account. <strong>Zorg ervoor dat je de herstelcodes op een veilige plek bewaard</strong>. Je kunt ze bijvoorbeeld printen en ze samen met andere belangrijke documenten bewaren.
     setup: Instellen
     wrong_code: De ingevoerde code is ongeldig! Klopt de systeemtijd van de server en die van jouw apparaat?
   users:
diff --git a/config/locales/no.yml b/config/locales/no.yml
index 207f86afc..57f8547fc 100644
--- a/config/locales/no.yml
+++ b/config/locales/no.yml
@@ -1,13 +1,23 @@
 ---
 'no':
   about:
+    about_hashtag_html: Dette er offentlige toots merket med <strong>#%{hashtag}</strong>. Du kan interagere med dem om du har en konto et sted i fediverset.
     about_mastodon_html: Mastodon er et sosialt nettverk laget med <em>fri programvare</em>. Et <em>desentralisert</em> alternativ til kommersielle plattformer. Slik kan det unngå risikoene ved å ha et enkelt selskap som monopoliserer din kommunikasjon. Velg en tjener du stoler på &mdash; uansett hvilken du velger så kan du kommunisere med alle andre. Alle kan kjøre sin egen Mastodon og delta sømløst i det sosiale nettverket.
     about_this: Om denne instansen
     closed_registrations: Registreringer er for øyeblikket lukket på denne instansen.
     contact: Kontakt
+    contact_missing: Ikke innstilt
+    contact_unavailable: Ikke tilgjengelig
     description_headline: Hva er %{domain}?
     domain_count_after: andre instanser
     domain_count_before: Koblet til
+    extended_description_html: |
+      <h3>En god plassering for regler</h3>
+      <p>En utvidet beskrivelse er ikke satt opp ennå.</p>
+    features:
+      humane_approach_title: En mer menneskelig tilnærming
+      not_a_product_body: Mastodon er ikke et kommerst nettverk. Ingen reklame, ingen datainnsamling, ingen innhegnede hager. Det finnes ingen sentral myndighet.
+      not_a_product_title: Du er en person, ikke et produkt
     other_instances: Andre instanser
     source_code: Kildekode
     status_count_after: statuser
@@ -29,7 +39,7 @@
       are_you_sure: Er du sikker?
       confirm: Bekreft
       confirmed: Bekreftet
-      disable_two_factor_authentication: Disable 2FA
+      disable_two_factor_authentication: Skru av 2FA
       display_name: Visningsnavn
       domain: Domene
       edit: Redigér
@@ -81,12 +91,12 @@
         create: Lag blokkering
         hint: Domeneblokkeringen vil ikke hindre opprettelse av kontooppføringer i databasen, men vil retroaktivt og automatisk benytte spesifikke moderasjonsmetoder på de kontoene.
         severity:
-          desc_html: "<strong>Målbind</strong> vil gjøre kontoens poster usynlige for alle som ikke følger den. <strong>Utvis</storng> fjerner alt innhold, media og profildata fra kontoen."
+          desc_html: "<strong>Målbind</strong> gjør kontoens poster usynlige for alle som ikke følger den. <strong>Utvis</strong> fjerner alt innhold, media og profildata fra kontoen. Bruk <strong>Ingen</strong> hvis du bare vil fjerne mediafiler."
           silence: Målbind
           suspend: Utvis
         title: Ny domeneblokkering
       reject_media: Avvis mediefiler
-      reject_media_hint: Fjerner lokalt lagrede mediefiler og nekter å laste dem ned i fremtiden. Irrelevant for utvisninger.
+      reject_media_hint: Fjerner lokalt lagrede mediefiler og nekter å laste dem ned i fremtiden. Irrelevant for utvisninger
       severities:
         silence: Målbind
         suspend: Utvis
@@ -136,7 +146,7 @@
         open:
           title: Åpen registrering
       site_description:
-        desc_html: Vises som et avsnitt på forsiden og brukes som en meta-tagg.<br> Du kan bruke HTML-tagger, spesielt <code>&lt;a&gt;</code> og <code>&lt;em&gt;</code>.
+        desc_html: Vises som et avsnitt på forsiden og brukes som en meta-tagg. Du kan bruke HTML-tagger, spesielt <code>&lt;a&gt;</code> og <code>&lt;em&gt;</code>.
         title: Nettstedsbeskrivelse
       site_description_extended:
         desc_html: Vises på side for utvidet informasjon.<br>Du kan bruke HTML-tagger
@@ -168,7 +178,7 @@
     reset_password: Nullstill passord
     set_new_password: Sett nytt passord
   authorize_follow:
-    error: Uheldigvis så skjedde det en feil da vi prøvde å få tak i en bruker fra en annen instans.
+    error: Uheldigvis skjedde det en feil da vi prøvde å få tak i en bruker fra en annen instans
     follow: Følg
     title: Følg %{acct}
   datetime:
@@ -191,8 +201,8 @@
     '410': Siden du leter etter finnes ikke lenger.
     '422':
       content: Sikkerhetsverifisering feilet. Blokkerer du informasjonskapsler?
-      title: Sikkerhetsverifisering feilet.
-    '429': Throttled
+      title: Sikkerhetsverifisering feilet
+    '429': Overfyllt
   exports:
     blocks: Du blokkerer
     csv: CSV
@@ -208,7 +218,7 @@
     success:
       one: I ferd med å mykblokkere følgere fra ett domene...
       other: I ferd med å mykblokkere følgere fra %{count} domener...
-    true_privacy_html: Vennligst forstå at <strong>virkelig privatliv kun kan oppnås med ende-til-ende-kryptering.</strong>
+    true_privacy_html: Merk deg at <strong>virkelig privatliv kun kan oppnås med ende-til-ende-kryptering</strong>.
     unlocked_warning_html: Alle kan følge deg for å umiddelbart se dine private statuser. %{lock_link} for å kunne se over og avvise følgere.
     unlocked_warning_title: Din konto er ikke låst
   generic:
@@ -220,7 +230,7 @@
       other: Noe er ikke helt riktig ennå. Det er ennå %{count} feil å rette på
   imports:
     preface: Du kan importere data om brukere du følger eller blokkerer til kontoen din på denne instansen med eksportfiler fra andre instanser.
-    success: Din data ble mottatt og vil bli behandlet så fort som mulig.
+    success: Dine data ble mottatt og vil bli behandlet så fort som mulig
     types:
       blocking: Blokkeringsliste
       following: Følgeliste
@@ -243,8 +253,8 @@
         one: "1 ny hendelse siden ditt siste besøk \U0001F418"
         other: "%{count} nye hendelser siden ditt siste besøk \U0001F418"
     favourite:
-      body: Din status ble likt av %{name}
-      subject: "%{name} likte din status."
+      body: 'Statusen din ble likt av %{name}:'
+      subject: "%{name} likte statusen din"
     follow:
       body: "%{name} følger deg!"
       subject: "%{name} følger deg"
@@ -382,11 +392,11 @@
     enable: Skru på
     enabled_success: Aktivering av tofaktorautentisering vellykket
     generate_recovery_codes: Generér gjenopprettingskoder
-    instructions_html: "<strong>Scan denne QR-koden i Google Authenticator eller en lignende app på telefonen din</strong>. Fra nå av vil denne applikasjonen generere koder for deg som skal brukes under innlogging"
+    instructions_html: "<strong>Scan denne QR-koden med Google Authenticator eller en lignende app på telefonen din</strong>. Fra nå av vil denne applikasjonen generere koder for deg som skal brukes under innlogging."
     lost_recovery_codes: Gjenopprettingskoder lar deg gjenoppnå tilgang til din konto hvis du mister din telefon. Hvis du har mistet gjenopprettingskodene, kan du regenerere dem her. Dine gamle gjenopprettingskoder vil bli ugyldige.
     manual_instructions: 'Hvis du ikke får scannet QR-koden må du skrive inn følgende kode manuelt:'
     recovery_codes_regenerated: Generering av gjenopprettingskoder vellykket
-    recovery_instructions_html: Hvis du skulle miste tilgang til telefonen din, kan du bruke en av gjenopprettingskodene nedenfor til å gjenopprette tilgang til din konto. Oppbevar gjenopprettingskodene sikkert, for eksempel ved å skrive dem ut og lagre dem sammen med andre viktige dokumenter.
+    recovery_instructions_html: Hvis du skulle miste tilgang til telefonen din, kan du bruke en av gjenopprettingskodene nedenfor til å gjenopprette tilgang til din konto. <strong>Oppbevar gjenopprettingskodene sikkert</strong>, for eksempel ved å skrive dem ut og gjemme dem på et lurt sted bare du vet om.
     setup: Sett opp
     wrong_code: Den angitte koden var ugyldig! Stemmer instansens tid overalt med enhetens tid?
   users:
diff --git a/config/locales/oc.yml b/config/locales/oc.yml
index a9bad2d34..0167e9271 100644
--- a/config/locales/oc.yml
+++ b/config/locales/oc.yml
@@ -49,6 +49,7 @@ oc:
     reserved_username: Aqueste nom d’utilizaire es reservat
     roles:
       admin: Admin
+      moderator: Mod
     unfollow: Quitar de sègre
   admin:
     account_moderation_notes:
@@ -231,7 +232,12 @@ oc:
       search: Cercar
       title: Instàncias conegudas
     invites:
-      title: Covits
+      filter:
+        all: Totes
+        available: Disponibles
+        expired: Expirats
+        title: Filtre
+      title: Convits
     reports:
       action_taken_by: Mesura menada per
       are_you_sure: Es segur ?
@@ -268,8 +274,11 @@ oc:
           desc_html: Afichat sus las pagina d’acuèlh quand las inscripcions son tampadas.<br>Podètz utilizar de balisas HTML
           title: Messatge de barradura de las inscripcions
         deletion:
-          desc_html: Autorizar al monde a suprimir lor compte
+          desc_html: Autorizar lo monde a suprimir lor compte
           title: Possibilitat de suprimir lo compte
+        min_invite_role:
+          disabled: Degun
+          title: Autorizat amb invitacions
         open:
           desc_html: Autorizar lo monde a se marcar
           title: Inscripcions
@@ -277,7 +286,7 @@ oc:
         desc_html: Mostrar lo badge Personal sus la pagina de perfil
         title: Mostrar lo badge personal
       site_description:
-        desc_html: Afichada jos la forma de paragraf sus la pagina d’acuèlh e utilizada coma balisa meta.<br> Podètz utilizar de balisas HTML, coma <code>&lt;a&gt;</code> e <code>&lt;em&gt;</code>.
+        desc_html: Afichada jos la forma de paragraf sus la pagina d’acuèlh e utilizada coma balisa meta. Podètz utilizar de balisas HTML, en particular <code>&lt;a&gt;</code> e <code>&lt;em&gt;</code>.
         title: Descripcion del site
       site_description_extended:
         desc_html: Afichada sus la pagina d’informacion complementària del site<br>Podètz utilizar de balisas HTML
@@ -287,7 +296,7 @@ oc:
         title: Politica de confidencialitat del site
       site_title: Títol del site
       thumbnail:
-        desc_html: Servís pels apercebuts via OpenGraph e las API. Talha de 1200x630px recomandada.
+        desc_html: Servís pels apercebuts via OpenGraph e las API. Talha de 1200x630px recomandada
         title: Miniatura de l’instància
       timeline_preview:
         desc_html: Mostrar lo flux public sus la pagina d’acuèlh
@@ -306,7 +315,7 @@ oc:
         show: Mostrar mèdia
         title: Mèdia
       no_media: Cap mèdia
-      title: Estatuts del compteAccount statuses
+      title: Estatuts del compte
       with_media: Amb mèdia
     subscriptions:
       callback_url: URL de rapèl
@@ -343,6 +352,8 @@ oc:
     invalid_reset_password_token: Lo geton de reïnicializacion es invalid o acabat. Tornatz demandar un geton se vos plai.
     login: Se connectar
     logout: Se desconnectar
+    migrate_account: Mudar endacòm mai
+    migrate_account_html: Se volètz mandar los visitors d’aqueste compte a un autre, podètz<a href="%{path}"> o configurar aquí</a>.
     register: Se marcar
     resend_confirmation: Tornar mandar las instruccions de confirmacion
     reset_password: Reïnicializar lo senhal
@@ -503,18 +514,48 @@ oc:
       muting: Lista de mond que volètz pas legir
     upload: Importar
   in_memoriam_html: En Memòria.
+  invites:
+    delete: Desactivar
+    expired: Expirat
+    expires_in:
+      '1800': 30 minutas
+      '21600': 6 oras
+      '3600': 1 ora
+      '43200': 12 oras
+      '86400': 1 jorn
+    expires_in_prompt: Jamai
+    generate: Generar
+    max_uses:
+      one: 1 persona
+      other: "%{count} personas"
+    max_uses_prompt: Cap limit
+    prompt: Generatz e partejatz los ligams per donar accès a aquesta instància
+    table:
+      expires_at: Expirats
+      uses: Usatges
+    title: Convidar de monde
   landing_strip_html: "<strong>%{name}</strong> utiliza %{link_to_root_path}. Podètz lo/la sègre o interagir amb el o ela s’avètz un compte ont que siasque sul fediverse."
   landing_strip_signup_html: S’es pas lo cas, podètz <a href="%{sign_up_path}">vos marcar aquí</a>.
+  lists:
+    errors:
+      limit: Avètz atengut lo maximum de listas
   media_attachments:
     validations:
       images_and_video: Se pòt pas ajustar una vidèo a un estatut que ten ja d’imatges
       too_many: Se pòt pas ajustar mai de 4 fichièrs
+  migrations:
+    acct: nomutilizaire@domeni del nòu compte
+    currently_redirecting: 'Vòstre perfil es parametrat per mandar a :'
+    proceed: Enregistrar
+    updated_msg: Vòstre paramètre de migracion es ben estat mes a jorn !
+  moderation:
+    title: Moderation
   notification_mailer:
     digest:
       body: 'Trobatz aquí un resumit de çò qu’avètz mancat dempuèi vòstra darrièra visita lo %{since}:'
       mention: "%{name} vos a mencionat dins :"
       new_followers_summary:
-        one: Avètz un nòu seguidor ! Ouà  
+        one: Avètz un nòu seguidor ! Ouà !
         other: Avètz %{count} nòus seguidors ! Qué crane !
       subject:
         one: "Una nòva notificacion dempuèi vòstra darrièra visita \U0001F418"
@@ -621,6 +662,7 @@ oc:
     export: Export donadas
     followers: Seguidors autorizats
     import: Importar
+    migrate: Migracion de compte
     notifications: Notificacions
     preferences: Preferéncias
     settings: Paramètres
@@ -630,7 +672,7 @@ oc:
     open_in_web: Dobrir sul web
     over_character_limit: limit de %{max} caractèrs passat
     pin_errors:
-      limit: Tròp de tuts penjats
+      limit: Avètz ja lo maximum de tuts penjats
       ownership: Se pòt pas penjar lo tut de qualqu’un mai
       private: Se pòt pas penjar los tuts pas publics
       reblog: Se pòt pas penjar un tut partejat
@@ -739,4 +781,4 @@ oc:
   users:
     invalid_email: L’adreça de corrièl es invalida
     invalid_otp_token: Còdi d’autentificacion en dos temps invalid
-    signed_in_as: Session a
+    signed_in_as: 'Session a :'
diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml
index 760bb69a2..597de9e4a 100644
--- a/config/locales/pt-BR.yml
+++ b/config/locales/pt-BR.yml
@@ -67,7 +67,7 @@ pt-BR:
       demote: Rebaixar
       disable: Desativar
       disable_two_factor_authentication: Desativar 2FA
-      disabled: Desativado
+      disabled: Desativada
       display_name: Nome de exibição
       domain: Domínio
       edit: Editar
@@ -121,14 +121,14 @@ pt-BR:
       search: Pesquisar
       shared_inbox_url: URL da Inbox Compartilhada
       show:
-        created_reports: Relatórios criados por esta conta
+        created_reports: Denúncias criadas por esta conta
         report: relatórios
-        targeted_reports: Relatórios feitos sobre esta conta
+        targeted_reports: Denúncias feitas sobre esta conta
       silence: Silêncio
       statuses: Postagens
       subscribe: Inscrever-se
       title: Contas
-      undo_silenced: Retirar silêncio
+      undo_silenced: Desativar silêncio
       undo_suspension: Retirar suspensão
       unsubscribe: Desinscrever-se
       username: Nome de usuário
@@ -151,9 +151,14 @@ pt-BR:
         memorialize_account: "%{name} transformou a conta de %{target} em um memorial"
         promote_user: "%{name} promoveu o usuário %{target}"
         reset_password_user: "%{name} redefiniu a senha do usuário %{target}"
-        resolve_report: "%{name} dispensou o relatório %{target}"
+        resolve_report: "%{name} dispensou a denúncia %{target}"
         silence_account: "%{name} silenciou a conta de %{target}"
         suspend_account: "%{name} suspendeu a conta de %{target}"
+        unsilence_account: "%{name} desativou o silêncio de %{target}"
+        unsuspend_account: "%{name} desativou a suspensão de  %{target}"
+        update_custom_emoji: "%{name} atualizou o emoji %{target}"
+        update_status: "%{name} atualizou o estado de %{target}"
+      title: Auditar relatório
     custom_emojis:
       copied_msg: Cópia local do emoji criada com sucesso
       copy: Copiar
@@ -204,7 +209,7 @@ pt-BR:
           one: Uma conta no banco de dados foi afetada
           other: "%{count} contas no banco de dados foram afetadas"
         retroactive:
-          silence: Retirar silêncio de todas as contas existentes neste domínio
+          silence: Desativar silêncio de todas as contas existentes desse domínio
           suspend: Retirar suspensão de todas as contas neste domínio
         title: Retirar bloqueio de domínio de %{domain}
         undo: Retirar
@@ -217,7 +222,7 @@ pt-BR:
       destroyed_msg: Bloqueio de domínio de e-mail excluído com sucesso
       domain: Domínio
       new:
-        create: Criar bloqueio
+        create: Adicionar domínio
         title: Novo bloqueio de domínio de e-mail
       title: Bloqueio de Domínio de E-mail
     instances:
@@ -226,6 +231,13 @@ pt-BR:
       reset: Resetar
       search: Buscar
       title: Instâncias conhecidas
+    invites:
+      filter:
+        all: Todos
+        available: Disponíveis
+        expired: Expirados
+        title: Filtro
+      title: Convites
     reports:
       action_taken_by: Ação realizada por
       are_you_sure: Você tem certeza?
@@ -238,10 +250,10 @@ pt-BR:
       nsfw:
         'false': Mostrar mídias anexadas
         'true': Esconder mídias anexadas
-      report: 'Reportar #%{id}'
+      report: 'Denúncia #%{id}'
       report_contents: Conteúdos
-      reported_account: Conta reportada
-      reported_by: Reportada por
+      reported_account: Conta denunciada
+      reported_by: Denunciada por
       resolved: Resolvido
       silence_account: Silenciar conta
       status: Status
@@ -264,6 +276,9 @@ pt-BR:
         deletion:
           desc_html: Permitir que qualquer um delete a sua conta
           title: Exclusão aberta de contas
+        min_invite_role:
+          disabled: Ninguém
+          title: Permitir convites de
         open:
           desc_html: Permitir que qualquer um crie uma conta
           title: Cadastro aberto
@@ -279,7 +294,7 @@ pt-BR:
       site_title: Nome da instância
       thumbnail:
         desc_html: Usada para prévias via OpenGraph e API. Recomenda-se 1200x630px
-        title: Thumbnail da instância
+        title: Miniatura da instância
       timeline_preview:
         desc_html: Exibir a timeline pública na página inicial
         title: Prévia da timeline
@@ -309,7 +324,7 @@ pt-BR:
     title: Administração
   admin_mailer:
     new_report:
-      body: "%{reporter} reportou %{target}"
+      body: "%{reporter} denunciou %{target}"
       subject: Nova denúncia sobre %{instance} (#%{id})
   application_mailer:
     salutation: "%{name},"
@@ -334,9 +349,11 @@ pt-BR:
     invalid_reset_password_token: Token de modificação de senha é inválido ou expirou. Por favor, requisite um novo.
     login: Entrar
     logout: Sair
+    migrate_account: Mudar para uma conta diferente
+    migrate_account_html: Se você quer redirecionar essa conta para uma outra você pode <a href="%{path}">configura isso aqui</a>.
     register: Cadastrar-se
     resend_confirmation: Reenviar instruções de confirmação
-    reset_password: Modificar senha
+    reset_password: Redefinir senha
     set_new_password: Definir uma nova senha
   authorize_follow:
     error: Infelizmente, ocorreu um erro ao buscar a conta remota
@@ -376,7 +393,7 @@ pt-BR:
     '410': A página pela qual você está procurando não existe mais.
     '422':
       content: A verificação de segurança falhou. Você desativou o uso de cookies?
-      title: Falha na verificação de segurança
+      title: Verificação de segurança falhou
     '429': Muitas requisições
     '500':
       content: Desculpe, algo deu errado.
@@ -416,12 +433,39 @@ pt-BR:
       muting: Lista de silêncio
     upload: Enviar
   in_memoriam_html: Em memória.
+  invites:
+    delete: Desativar
+    expired: Expirados
+    expires_in:
+      '1800': 30 minutos
+      '21600': 6 horas
+      '3600': 1 hora
+      '43200': 12 horas
+      '86400': 1 dia
+    expires_in_prompt: Nunca
+    generate: Gerar
+    max_uses:
+      one: 1 uso
+      other: "%{count} usos"
+    max_uses_prompt: Sem limite
+    prompt: Gerar e compartilha links com outras pessoas para permitir acesso a essa instância
+    table:
+      expires_at: Expira em
+      uses: Usos
+    title: Convidar pessoas
   landing_strip_html: "<strong>%{name}</strong> é um usuário no %{link_to_root_path}. Você pode segui-lo ou interagir com ele se você tiver uma conta em qualquer lugar no fediverso."
   landing_strip_signup_html: Se não, você pode <a href="%{sign_up_path}">se cadastrar aqui</a>.
   media_attachments:
     validations:
       images_and_video: Não é possível anexar um vídeo a uma postagem que já contém imagens
       too_many: Não é possível anexar mais de 4 imagens
+  migrations:
+    acct: username@domain da nova conta
+    currently_redirecting: 'Seu perfil está configurado para redirecionar para:'
+    proceed: Salvar
+    updated_msg: As configurações de migração da sua conta foram atualizadas com sucesso!
+  moderation:
+    title: Moderação
   notification_mailer:
     digest:
       body: 'Aqui está um resumo do que você perdeu no %{instance} desde o seu último acesso em %{since}:'
@@ -498,7 +542,7 @@ pt-BR:
       generic: Navegador desconhecido
       ie: Internet Explorer
       micro_messenger: MicroMessenger
-      nokia: Nokia S40 Ovi Browser
+      nokia: Navegador Nokia S40 Ovi
       opera: Opera
       phantom_js: PhantomJS
       qq: QQ Browser
@@ -534,6 +578,7 @@ pt-BR:
     export: Exportar dados
     followers: Seguidores autorizados
     import: Importar
+    migrate: Migração de conta
     notifications: Notificações
     preferences: Preferências
     settings: Configurações
diff --git a/config/locales/simple_form.ar.yml b/config/locales/simple_form.ar.yml
index 932b166d7..e4c6694e9 100644
--- a/config/locales/simple_form.ar.yml
+++ b/config/locales/simple_form.ar.yml
@@ -4,10 +4,14 @@ ar:
     hints:
       defaults:
         avatar: PNG, GIF أو JPG. على الأكثر 2 ميغابيت . سوف يتم تصغيرها إلى 120x120px
-        display_name: <span class="name-counter">%{count}</span> أحرف متبقية
+        digest: يُرسَل بعد مضيّ مدة طويلة من خمول نشاطك يحوي على تلخيص للتبويقات التي ذُكر حسابك فيها أثناء غيابك
+        display_name:
+          one: <span class="name-counter">1</span> حرف متبقي
         header: PNG, GIF or JPG. على الأكثر 2 ميغابيت . سوف يتم تصغيرها إلى 700x335px
         locked: يتطلب منك الموافقة يدويا على كل طلب للإشتراك بحسابك و منشوراتك تعرض لمتابعيك فقط دون غيرهم
         note: <span class="note-counter">%{count}</span> أحرف متبقية
+        setting_noindex: تمس ملفك العمومي الخاص بك وصفحات الحالة
+        setting_theme: تغير المظهر الذي يبدو عليه ماستدون عندما تقوم بتسجيل دخولك على أي جهاز.
       imports:
         data: ملف CSV تم تصديره من خادوم مثيل  آخر لماستدون
       sessions:
@@ -24,6 +28,7 @@ ar:
         header: رأس الصفحة
         locale: اللغة
         locked: إجعل حسابك خاصًا
+        max_uses: العدد الأقصى للإستخدام
         new_password: كلمة مرور جديدة
         note: السيرة الذاتية
         otp_attempt: الرمز الثنائي
@@ -31,6 +36,8 @@ ar:
         setting_auto_play_gif: تشغيل صور جيف المتحركة تلقائي
         setting_boost_modal: إظهار مربع حوار التأكيد قبل القيام بالترقية
         setting_default_privacy: خصوصية المنشور
+        setting_default_sensitive: دائما تحديد الوسائط كحساسة
+        setting_noindex: منع محركات البحث من فهرسة ملفي الشخصي
         severity: الشدة
         type: نوع الإستيراد
         username: اسم المستخدم
diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml
index ab7540621..361a5ada6 100644
--- a/config/locales/simple_form.ja.yml
+++ b/config/locales/simple_form.ja.yml
@@ -3,20 +3,20 @@ ja:
   simple_form:
     hints:
       defaults:
-        avatar: 2MBまでのPNGやGIF、JPGが利用可能です。120x120pxまで縮小されます。
-        digest: 長期間ログインしなかった際、その期間に受け取った返信の要約を受け取ることができます。
+        avatar: 2MBまでのPNGやGIF、JPGが利用可能です。120x120pxまで縮小されます
+        digest: 長期間ログインしなかった際、その期間に受け取った返信の要約を受け取ることができます
         display_name: あと<span class="name-counter">%{count}</span>文字入力できます。
-        header: 2MBまでのPNGやGIF、JPGが利用可能です。 700x335pxまで縮小されます。
+        header: 2MBまでのPNGやGIF、JPGが利用可能です。 700x335pxまで縮小されます
         locked: フォロワーを手動で承認する必要があります
         note: あと<span class="note-counter">%{count}</span>文字入力できます。
         setting_noindex: 公開プロフィールおよび各投稿ページに影響します
         setting_theme: ログインしている全てのデバイスで適用されるデザインです。
       imports:
-        data: 他の Mastodon インスタンスからエクスポートしたCSVファイルを選択して下さい。
+        data: 他の Mastodon インスタンスからエクスポートしたCSVファイルを選択して下さい
       sessions:
         otp: 携帯電話に表示された2段階認証コードを入力するか、生成したリカバリーコードを使用してください。
       user:
-        filtered_languages: 選択した言語があなたの公開タイムラインから取り除かれます。
+        filtered_languages: 選択した言語があなたの公開タイムラインから取り除かれます
     labels:
       defaults:
         avatar: アイコン
diff --git a/config/locales/simple_form.nl.yml b/config/locales/simple_form.nl.yml
index f2847e7ca..17b9647a4 100644
--- a/config/locales/simple_form.nl.yml
+++ b/config/locales/simple_form.nl.yml
@@ -14,7 +14,7 @@ nl:
           one: <span class="note-counter">1</span> teken over
           other: <span class="note-counter">%{count}</span> tekens over
         setting_noindex: Heeft invloed op jouw openbare profiel en toots
-        setting_theme: Heeft invloed op hoe Mastodon eruitziet op elk apparaat waarmee je inlogt.
+        setting_theme: Heeft invloed op hoe de webapp van Mastodon eruitziet (op elk apparaat waarmee je inlogt).
       imports:
         data: CSV-bestand dat op een andere Mastodon-server werd geëxporteerd
       sessions:
@@ -48,22 +48,22 @@ nl:
         setting_noindex: Jouw toots niet door zoekmachines laten indexeren
         setting_reduce_motion: Langzamere animaties
         setting_system_font_ui: Standaardlettertype van jouw systeem gebruiken
-        setting_theme: Site thema
+        setting_theme: Thema website
         setting_unfollow_modal: Vraag voor het ontvolgen van iemand een bevestiging
-        severity: Strengheid
+        severity: Zwaarte
         type: Importtype
         username: gebruikersnaam
       interactions:
-        must_be_follower: Blokkeer meldingen van mensen die jou niet volgen
-        must_be_following: Blokkeer meldingen van mensen die jij niet volgt
-        must_be_following_dm: Blokkeer directe berichten van mensen die jij niet volgt
+        must_be_follower: Meldingen van mensen die jou niet volgen blokkeren
+        must_be_following: Meldingen van mensen die jij niet volgt blokkeren
+        must_be_following_dm: Directe berichten van mensen die jij niet volgt blokkeren
       notification_emails:
-        digest: Verstuur periodiek e-mails met een samenvatting
-        favourite: Verstuur een e-mail wanneer iemand jouw toot als favoriet markeert
-        follow: Verstuur een e-mail wanneer iemand jou volgt
-        follow_request: Verstuur een e-mail wanneer iemand jou wilt volgen
-        mention: Verstuur een e-mail wanneer iemand jou vermeld
-        reblog: Verstuur een e-mail wanneer iemand jouw toot heeft geboost
+        digest: Periodiek e-mails met een samenvatting versturen
+        favourite: Een e-mail versturen wanneer iemand jouw toot als favoriet markeert
+        follow: Een e-mail versturen wanneer iemand jou volgt
+        follow_request: Een e-mail versturen wanneer iemand jou wilt volgen
+        mention: Een e-mail versturen wanneer iemand jou vermeld
+        reblog: Een e-mail versturen wanneer iemand jouw toot heeft geboost
     'no': Nee
     required:
       mark: "*"
diff --git a/config/locales/simple_form.oc.yml b/config/locales/simple_form.oc.yml
index f178d1857..06c23ace2 100644
--- a/config/locales/simple_form.oc.yml
+++ b/config/locales/simple_form.oc.yml
@@ -4,6 +4,7 @@ oc:
     hints:
       defaults:
         avatar: PNG, GIF o JPG. Maximum 2 Mo. Serà retalhat en 120x120px
+        digest: Enviat aprèp un long moment d’inactivitat amb un resumit de las mencions qu’avètz recebudas pendent vòstra abséncia
         display_name:
           one: Demòra encara <span class="name-counter">1</span> caractèr
           other: Demòran encara <span class="name-counter">%{count}</span> caractèrs
@@ -29,10 +30,12 @@ oc:
         data: Data
         display_name: Escais
         email: Corrièl
+        expires_in: Expira aprèp
         filtered_languages: Lengas filtradas
         header: Bandièra
         locale: Lenga
         locked: Far venir lo compte privat
+        max_uses: Limit d’utilizacion
         new_password: Nòu senhal
         note: Bio
         otp_attempt: Còdi Two-factor
@@ -44,7 +47,7 @@ oc:
         setting_delete_modal: Afichar una fenèstra de confirmacion abans de suprimir un estatut
         setting_noindex: Èsser pas indexat pels motors de recèrca
         setting_reduce_motion: Reduire la velocitat de las animacions
-        setting_system_font_ui: Utilizar la policia Font del sisèma
+        setting_system_font_ui: Utilizar la polissa del sisèma
         setting_theme: Tèma del site
         setting_unfollow_modal: Afichar una confirmacion abans de quitar de sègre qualqu’un
         severity: Severitat
@@ -53,6 +56,7 @@ oc:
       interactions:
         must_be_follower: Blocar las notificacions del mond que vos sègon pas
         must_be_following: Blocar las notificacions del mond que seguètz pas
+        must_be_following_dm: Blocar los messatges del monde que seguètz pas
       notification_emails:
         digest: Enviar un corrièl recapitulatiu
         favourite: Enviar un corrièl quand qualqu’un plaça vòstre estatut en favorit
diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml
index cd087c3e8..3ede5c4d5 100644
--- a/config/locales/zh-CN.yml
+++ b/config/locales/zh-CN.yml
@@ -325,13 +325,13 @@ zh-CN:
     title: 管理
   admin_mailer:
     new_report:
-      body: "%{reporter} 举报了 %{target}"
-      subject: 来自 %{instance} 的新举报(#%{id})
+      body: "%{reporter} 举报了用户 %{target}。"
+      subject: 来自 %{instance} 的用户举报(#%{id})
   application_mailer:
-    salutation: "%{name},"
-    settings: 更改电子邮件首选项:%{link}
-    signature: 来自 %{instance} 的 Mastodon 通知
-    view: 查看:
+    salutation: "%{name}:"
+    settings: 使用此链接更改你的电子邮件首选项:%{link}
+    signature: 这是一封来自 %{instance} 的 Mastodon 电子邮件通知。
+    view: 点此链接查看详情:
   applications:
     created: 应用创建成功
     destroyed: 应用删除成功
@@ -399,7 +399,7 @@ zh-CN:
     '500':
       content: 抱歉,我们这里出错了。
       title: 这个页面不正确
-    noscript_html: 请启用 JavaScript 以便使用 Mastodon 网页版应用。你也可以选择适用于你的平台的 <a href="https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md">Mastodon 应用</a>。
+    noscript_html: 使用 Mastodon 网页版应用需要启用 JavaScript。你也可以选择适用于你的平台的 <a href="https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md">Mastodon 应用</a>。
   exports:
     blocks: 屏蔽的用户
     csv: CSV
@@ -445,13 +445,16 @@ zh-CN:
     generate: 生成邀请链接
     max_uses: "%{count} 次"
     max_uses_prompt: 无限制
-    prompt: 生成可供分享的链接以便邀请他人在本实例注册
+    prompt: 生成分享链接,邀请他人在本实例注册
     table:
       expires_at: 失效时间
       uses: 已使用次数
     title: 邀请用户
   landing_strip_html: "<strong>%{name}</strong> 是一位来自 %{link_to_root_path} 的用户。如果你想关注他们或者与他们互动,你需要在任意一个 Mastodon 实例或与其兼容的网站上拥有一个帐户。"
   landing_strip_signup_html: 还没有这种帐户?你可以<a href="%{sign_up_path}">在本站注册一个</a>。
+  lists:
+    errors:
+      limit: 你所建立的列表数量已经达到上限
   media_attachments:
     validations:
       images_and_video: 无法在嘟文中同时插入视频和图片
@@ -478,8 +481,8 @@ zh-CN:
       body: "%{name} 关注了你!"
       subject: "%{name} 关注了你"
     follow_request:
-      body: "%{name} 请求关注你"
-      subject: 待审核的关注者:%{name}
+      body: "%{name} 向你发送了关注请求!"
+      subject: 来自 %{name} 的关注请求
     mention:
       body: "%{name} 在嘟文中提到了你:"
       subject: "%{name} 提到了你"
@@ -583,7 +586,7 @@ zh-CN:
     open_in_web: 在站内打开
     over_character_limit: 超过了 %{max} 字的限制
     pin_errors:
-      limit: 置顶的嘟文条数超出限制
+      limit: 你所置顶的嘟文数量已经达到上限
       ownership: 不能置顶他人的嘟文
       private: 不能置顶非公开的嘟文
       reblog: 不能置顶转嘟
@@ -674,7 +677,7 @@ zh-CN:
     formats:
       default: "%Y年%-m月%d日 %H:%M"
   two_factor_authentication:
-    code_hint: 输入你的认证器生成的代码以确认
+    code_hint: 输入认证器生成的代码以确认操作
     description_html: 启用<strong>双重认证</strong>后,你需要输入手机认证器生成的代码才能登录
     disable: 停用
     enable: 启用
@@ -688,7 +691,7 @@ zh-CN:
     recovery_codes_regenerated: 恢复代码重新生成成功
     recovery_instructions_html: 如果你的手机无法使用,你可以使用下列任意一个恢复代码来重新获得对帐户的访问权。<strong>请妥善保管好你的恢复代码</strong>(例如,你可以将它们打印出来,然后和其他重要的文件放在一起)。
     setup: 设置
-    wrong_code: 输入的认证码无效!请检查你设备上显示的时间是否正确,如果正确,你可能需要联系管理员以检查服务器的时间是否正确。
+    wrong_code: 输入的认证码无效!请核对一下你的设备显示的时间,如果正确,你可能需要联系一下实例的管理员,让他们校准服务器的时间。
   users:
     invalid_email: 输入的电子邮件地址无效
     invalid_otp_token: 输入的双重认证代码无效
diff --git a/config/routes.rb b/config/routes.rb
index 75b9c2d58..5b0ee9324 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -284,6 +284,7 @@ Rails.application.routes.draw do
         resources :statuses, only: :index, controller: 'accounts/statuses'
         resources :followers, only: :index, controller: 'accounts/follower_accounts'
         resources :following, only: :index, controller: 'accounts/following_accounts'
+        resources :lists, only: :index, controller: 'accounts/lists'
 
         member do
           post :follow
diff --git a/db/migrate/20171201000000_change_account_id_nonnullable_in_lists.rb b/db/migrate/20171201000000_change_account_id_nonnullable_in_lists.rb
new file mode 100644
index 000000000..120f74402
--- /dev/null
+++ b/db/migrate/20171201000000_change_account_id_nonnullable_in_lists.rb
@@ -0,0 +1,7 @@
+require Rails.root.join('lib', 'mastodon', 'migration_helpers')
+
+class ChangeAccountIdNonnullableInLists < ActiveRecord::Migration[5.1]
+  def change
+    change_column_null :lists, :account_id, false
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 4cf886a00..16882c743 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: 20171130000000) do
+ActiveRecord::Schema.define(version: 20171201000000) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -218,7 +218,7 @@ ActiveRecord::Schema.define(version: 20171130000000) do
   end
 
   create_table "lists", force: :cascade do |t|
-    t.bigint "account_id"
+    t.bigint "account_id", null: false
     t.string "title", default: "", null: false
     t.datetime "created_at", null: false
     t.datetime "updated_at", null: false
diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb
index ac04913e6..f59ec97cd 100644
--- a/lib/mastodon/version.rb
+++ b/lib/mastodon/version.rb
@@ -17,7 +17,7 @@ module Mastodon
     end
 
     def pre
-      'rc2'
+      'rc3'
     end
 
     def flags
diff --git a/spec/controllers/api/v1/accounts/lists_controller_spec.rb b/spec/controllers/api/v1/accounts/lists_controller_spec.rb
new file mode 100644
index 000000000..0a372f65b
--- /dev/null
+++ b/spec/controllers/api/v1/accounts/lists_controller_spec.rb
@@ -0,0 +1,23 @@
+require 'rails_helper'
+
+describe Api::V1::Accounts::ListsController do
+  render_views
+
+  let(:user)    { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') }
+  let(:account) { Fabricate(:account) }
+  let(:list)    { Fabricate(:list, account: user.account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+    user.account.follow!(account)
+    list.accounts << account
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id }
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/api/web/push_subscriptions_controller_spec.rb b/spec/controllers/api/web/push_subscriptions_controller_spec.rb
index 7e83b801d..bbf94c5c6 100644
--- a/spec/controllers/api/web/push_subscriptions_controller_spec.rb
+++ b/spec/controllers/api/web/push_subscriptions_controller_spec.rb
@@ -48,6 +48,23 @@ describe Api::Web::PushSubscriptionsController do
       expect(push_subscription['key_p256dh']).to eq(create_payload[:subscription][:keys][:p256dh])
       expect(push_subscription['key_auth']).to eq(create_payload[:subscription][:keys][:auth])
     end
+
+    context 'with initial data' do
+      it 'saves alert settings' do
+        sign_in(user)
+
+        stub_request(:post, create_payload[:subscription][:endpoint]).to_return(status: 200)
+
+        post :create, format: :json, params: create_payload.merge(alerts_payload)
+
+        push_subscription = Web::PushSubscription.find_by(endpoint: create_payload[:subscription][:endpoint])
+
+        expect(push_subscription.data['follow']).to eq(alerts_payload[:data][:follow])
+        expect(push_subscription.data['favourite']).to eq(alerts_payload[:data][:favourite])
+        expect(push_subscription.data['reblog']).to eq(alerts_payload[:data][:reblog])
+        expect(push_subscription.data['mention']).to eq(alerts_payload[:data][:mention])
+      end
+    end
   end
 
   describe 'PUT #update' do
diff --git a/spec/controllers/concerns/rate_limit_headers_spec.rb b/spec/controllers/concerns/rate_limit_headers_spec.rb
index 719978dc2..00a9a2080 100644
--- a/spec/controllers/concerns/rate_limit_headers_spec.rb
+++ b/spec/controllers/concerns/rate_limit_headers_spec.rb
@@ -34,7 +34,7 @@ describe ApplicationController do
       let(:start_time) { DateTime.new(2017, 1, 1, 12, 0, 0).utc }
 
       before do
-        request.env['rack.attack.throttle_data'] = { 'api' => { limit: 100, count: 20, period: 10 } }
+        request.env['rack.attack.throttle_data'] = { 'throttle_authenticated_api' => { limit: 100, count: 20, period: 10 } }
         travel_to start_time do
           get 'show'
         end
diff --git a/spec/fabricators/preview_card_fabricator.rb b/spec/fabricators/preview_card_fabricator.rb
deleted file mode 100644
index 15b33815c..000000000
--- a/spec/fabricators/preview_card_fabricator.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-Fabricator(:preview_card) do
-  status
-  url 'http://example.com'
-end