about summary refs log tree commit diff
path: root/config
diff options
context:
space:
mode:
authorSurinna Curtis <ekiru.0@gmail.com>2017-11-16 01:38:26 -0600
committerGitHub <noreply@github.com>2017-11-16 01:38:26 -0600
commitee560abdbe7a2caf0f7ac6137faf248bbaff9a93 (patch)
treefcd9bdb5ba49ab7a6a79590c74db858ae77b4239 /config
parent88627fd7aa2493a6890d60a5965459e4c7fe6fe9 (diff)
parent35fbdc36f92b610e8a73e2acb220e87cf5fc83b0 (diff)
Merge pull request #216 from glitch-soc/merge-upstream-3023725
Merge upstream at commit 3023725
Diffstat (limited to 'config')
-rw-r--r--config/brakeman.ignore56
-rw-r--r--config/deploy.rb2
-rw-r--r--config/i18n-tasks.yml2
-rw-r--r--config/initializers/paperclip.rb90
-rw-r--r--config/initializers/statsd.rb2
-rw-r--r--config/locales/activerecord.zh-CN.yml13
-rw-r--r--config/locales/devise.oc.yml2
-rw-r--r--config/locales/devise.zh-CN.yml58
-rw-r--r--config/locales/doorkeeper.pl.yml6
-rw-r--r--config/locales/doorkeeper.zh-CN.yml63
-rw-r--r--config/locales/en.yml18
-rw-r--r--config/locales/fr.yml2
-rw-r--r--config/locales/ja.yml28
-rw-r--r--config/locales/oc.yml13
-rw-r--r--config/locales/pl.yml45
-rw-r--r--config/locales/pt-BR.yml22
-rw-r--r--config/locales/simple_form.en.yml1
-rw-r--r--config/locales/simple_form.ja.yml1
-rw-r--r--config/locales/simple_form.oc.yml2
-rw-r--r--config/locales/simple_form.pl.yml1
-rw-r--r--config/locales/simple_form.zh-CN.yml59
-rw-r--r--config/locales/zh-CN.yml578
-rw-r--r--config/navigation.rb16
-rw-r--r--config/routes.rb10
-rw-r--r--config/settings.yml1
-rw-r--r--config/webpack/production.js44
-rw-r--r--config/webpack/shared.js38
27 files changed, 830 insertions, 343 deletions
diff --git a/config/brakeman.ignore b/config/brakeman.ignore
index f198eebac..f7cf89dff 100644
--- a/config/brakeman.ignore
+++ b/config/brakeman.ignore
@@ -10,7 +10,7 @@
       "line": 122,
       "link": "http://brakemanscanner.org/docs/warning_types/link_to_href",
       "code": "link_to(Account.find(params[:id]).inbox_url, Account.find(params[:id]).inbox_url)",
-      "render_path": [{"type":"controller","class":"Admin::AccountsController","method":"show","line":13,"file":"app/controllers/admin/accounts_controller.rb"}],
+      "render_path": [{"type":"controller","class":"Admin::AccountsController","method":"show","line":15,"file":"app/controllers/admin/accounts_controller.rb"}],
       "location": {
         "type": "template",
         "template": "admin/accounts/show"
@@ -29,7 +29,7 @@
       "line": 128,
       "link": "http://brakemanscanner.org/docs/warning_types/link_to_href",
       "code": "link_to(Account.find(params[:id]).shared_inbox_url, Account.find(params[:id]).shared_inbox_url)",
-      "render_path": [{"type":"controller","class":"Admin::AccountsController","method":"show","line":13,"file":"app/controllers/admin/accounts_controller.rb"}],
+      "render_path": [{"type":"controller","class":"Admin::AccountsController","method":"show","line":15,"file":"app/controllers/admin/accounts_controller.rb"}],
       "location": {
         "type": "template",
         "template": "admin/accounts/show"
@@ -48,7 +48,7 @@
       "line": 35,
       "link": "http://brakemanscanner.org/docs/warning_types/link_to_href",
       "code": "link_to(Account.find(params[:id]).url, Account.find(params[:id]).url)",
-      "render_path": [{"type":"controller","class":"Admin::AccountsController","method":"show","line":13,"file":"app/controllers/admin/accounts_controller.rb"}],
+      "render_path": [{"type":"controller","class":"Admin::AccountsController","method":"show","line":15,"file":"app/controllers/admin/accounts_controller.rb"}],
       "location": {
         "type": "template",
         "template": "admin/accounts/show"
@@ -60,25 +60,6 @@
     {
       "warning_type": "Dynamic Render Path",
       "warning_code": 15,
-      "fingerprint": "3b0a20b08aef13cf8cf865384fae0cfd3324d8200a83262bf4abbc8091b5fec5",
-      "check_name": "Render",
-      "message": "Render path contains parameter value",
-      "file": "app/views/admin/custom_emojis/index.html.haml",
-      "line": 31,
-      "link": "http://brakemanscanner.org/docs/warning_types/dynamic_render_path/",
-      "code": "render(action => filtered_custom_emojis.page(params[:page]), {})",
-      "render_path": [{"type":"controller","class":"Admin::CustomEmojisController","method":"index","line":9,"file":"app/controllers/admin/custom_emojis_controller.rb"}],
-      "location": {
-        "type": "template",
-        "template": "admin/custom_emojis/index"
-      },
-      "user_input": "params[:page]",
-      "confidence": "Weak",
-      "note": ""
-    },
-    {
-      "warning_type": "Dynamic Render Path",
-      "warning_code": 15,
       "fingerprint": "44d3f14e05d8fbb5b23e13ac02f15aa38b2a2f0f03b9ba76bab7f98e155a4a4e",
       "check_name": "Render",
       "message": "Render path contains parameter value",
@@ -105,7 +86,7 @@
       "line": 131,
       "link": "http://brakemanscanner.org/docs/warning_types/link_to_href",
       "code": "link_to(Account.find(params[:id]).followers_url, Account.find(params[:id]).followers_url)",
-      "render_path": [{"type":"controller","class":"Admin::AccountsController","method":"show","line":13,"file":"app/controllers/admin/accounts_controller.rb"}],
+      "render_path": [{"type":"controller","class":"Admin::AccountsController","method":"show","line":15,"file":"app/controllers/admin/accounts_controller.rb"}],
       "location": {
         "type": "template",
         "template": "admin/accounts/show"
@@ -124,7 +105,7 @@
       "line": 106,
       "link": "http://brakemanscanner.org/docs/warning_types/link_to_href",
       "code": "link_to(Account.find(params[:id]).salmon_url, Account.find(params[:id]).salmon_url)",
-      "render_path": [{"type":"controller","class":"Admin::AccountsController","method":"show","line":13,"file":"app/controllers/admin/accounts_controller.rb"}],
+      "render_path": [{"type":"controller","class":"Admin::AccountsController","method":"show","line":15,"file":"app/controllers/admin/accounts_controller.rb"}],
       "location": {
         "type": "template",
         "template": "admin/accounts/show"
@@ -134,13 +115,32 @@
       "note": ""
     },
     {
+      "warning_type": "Dynamic Render Path",
+      "warning_code": 15,
+      "fingerprint": "8d843713d99e8403f7992f3e72251b633817cf9076ffcbbad5613859d2bbc127",
+      "check_name": "Render",
+      "message": "Render path contains parameter value",
+      "file": "app/views/admin/custom_emojis/index.html.haml",
+      "line": 31,
+      "link": "http://brakemanscanner.org/docs/warning_types/dynamic_render_path/",
+      "code": "render(action => filtered_custom_emojis.eager_load(:local_counterpart).page(params[:page]), {})",
+      "render_path": [{"type":"controller","class":"Admin::CustomEmojisController","method":"index","line":9,"file":"app/controllers/admin/custom_emojis_controller.rb"}],
+      "location": {
+        "type": "template",
+        "template": "admin/custom_emojis/index"
+      },
+      "user_input": "params[:page]",
+      "confidence": "Weak",
+      "note": ""
+    },
+    {
       "warning_type": "SQL Injection",
       "warning_code": 0,
       "fingerprint": "9ccb9ba6a6947400e187d515e0bf719d22993d37cfc123c824d7fafa6caa9ac3",
       "check_name": "SQL",
       "message": "Possible SQL injection",
       "file": "lib/mastodon/snowflake.rb",
-      "line": 86,
+      "line": 87,
       "link": "http://brakemanscanner.org/docs/warning_types/sql_injection/",
       "code": "connection.execute(\"        CREATE OR REPLACE FUNCTION timestamp_id(table_name text)\\n        RETURNS bigint AS\\n        $$\\n          DECLARE\\n            time_part bigint;\\n            sequence_base bigint;\\n            tail bigint;\\n          BEGIN\\n            time_part := (\\n              -- Get the time in milliseconds\\n              ((date_part('epoch', now()) * 1000))::bigint\\n              -- And shift it over two bytes\\n              << 16);\\n\\n            sequence_base := (\\n              'x' ||\\n              -- Take the first two bytes (four hex characters)\\n              substr(\\n                -- Of the MD5 hash of the data we documented\\n                md5(table_name ||\\n                  '#{SecureRandom.hex(16)}' ||\\n                  time_part::text\\n                ),\\n                1, 4\\n              )\\n            -- And turn it into a bigint\\n            )::bit(16)::bigint;\\n\\n            -- Finally, add our sequence number to our base, and chop\\n            -- it to the last two bytes\\n            tail := (\\n              (sequence_base + nextval(table_name || '_id_seq'))\\n              & 65535);\\n\\n            -- Return the time part and the sequence part. OR appears\\n            -- faster here than addition, but they're equivalent:\\n            -- time_part has no trailing two bytes, and tail is only\\n            -- the last two bytes.\\n            RETURN time_part | tail;\\n          END\\n        $$ LANGUAGE plpgsql VOLATILE;\\n\")",
       "render_path": null,
@@ -182,7 +182,7 @@
       "line": 95,
       "link": "http://brakemanscanner.org/docs/warning_types/link_to_href",
       "code": "link_to(Account.find(params[:id]).remote_url, Account.find(params[:id]).remote_url)",
-      "render_path": [{"type":"controller","class":"Admin::AccountsController","method":"show","line":13,"file":"app/controllers/admin/accounts_controller.rb"}],
+      "render_path": [{"type":"controller","class":"Admin::AccountsController","method":"show","line":15,"file":"app/controllers/admin/accounts_controller.rb"}],
       "location": {
         "type": "template",
         "template": "admin/accounts/show"
@@ -240,7 +240,7 @@
       "line": 125,
       "link": "http://brakemanscanner.org/docs/warning_types/link_to_href",
       "code": "link_to(Account.find(params[:id]).outbox_url, Account.find(params[:id]).outbox_url)",
-      "render_path": [{"type":"controller","class":"Admin::AccountsController","method":"show","line":13,"file":"app/controllers/admin/accounts_controller.rb"}],
+      "render_path": [{"type":"controller","class":"Admin::AccountsController","method":"show","line":15,"file":"app/controllers/admin/accounts_controller.rb"}],
       "location": {
         "type": "template",
         "template": "admin/accounts/show"
@@ -269,6 +269,6 @@
       "note": ""
     }
   ],
-  "updated": "2017-10-07 19:24:02 +0200",
+  "updated": "2017-10-20 00:00:54 +0900",
   "brakeman_version": "4.0.1"
 }
diff --git a/config/deploy.rb b/config/deploy.rb
index 33b88b109..3fd149f21 100644
--- a/config/deploy.rb
+++ b/config/deploy.rb
@@ -1,6 +1,6 @@
 # frozen_string_literal: true
 
-lock '3.8.2'
+lock '3.10.0'
 
 set :repo_url, ENV.fetch('REPO', 'https://github.com/tootsuite/mastodon.git')
 set :branch, ENV.fetch('BRANCH', 'master')
diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml
index b35e5c09a..08a96f727 100644
--- a/config/i18n-tasks.yml
+++ b/config/i18n-tasks.yml
@@ -46,6 +46,7 @@ ignore_missing:
   - 'terms.body_html'
   - 'application_mailer.salutation'
   - 'errors.500'
+
 ignore_unused:
   - 'activemodel.errors.*'
   - 'activerecord.attributes.*'
@@ -58,3 +59,4 @@ ignore_unused:
   - 'errors.messages.*'
   - 'activerecord.errors.models.doorkeeper/*'
   - 'errors.429'
+  - 'admin.accounts.roles.*'
diff --git a/config/initializers/paperclip.rb b/config/initializers/paperclip.rb
index 2c82a91db..14bd034e6 100644
--- a/config/initializers/paperclip.rb
+++ b/config/initializers/paperclip.rb
@@ -7,60 +7,76 @@ Paperclip.interpolates :filename do |attachment, style|
   [basename(attachment, style), extension(attachment, style)].delete_if(&:blank?).join('.')
 end
 
-Paperclip::Attachment.default_options[:use_timestamp]  = false
+Paperclip::Attachment.default_options.merge!(
+  use_timestamp: false,
+  path: ':class/:attachment/:id_partition/:style/:filename',
+  storage: :fog
+)
 
 if ENV['S3_ENABLED'] == 'true'
-  Aws.eager_autoload!(services: %w(S3))
+  require 'fog/aws'
 
-  Paperclip::Attachment.default_options[:storage]        = :s3
-  Paperclip::Attachment.default_options[:s3_protocol]    = ENV.fetch('S3_PROTOCOL') { 'https' }
-  Paperclip::Attachment.default_options[:url]            = ':s3_domain_url'
-  Paperclip::Attachment.default_options[:s3_host_name]   = ENV.fetch('S3_HOSTNAME') { "s3-#{ENV.fetch('S3_REGION')}.amazonaws.com" }
-  Paperclip::Attachment.default_options[:path]           = '/:class/:attachment/:id_partition/:style/:filename'
-  Paperclip::Attachment.default_options[:s3_headers]     = { 'Cache-Control' => 'max-age=315576000' }
-  Paperclip::Attachment.default_options[:s3_permissions] = ENV.fetch('S3_PERMISSION') { 'public-read' }
-  Paperclip::Attachment.default_options[:s3_region]      = ENV.fetch('S3_REGION') { 'us-east-1' }
+  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?
 
-  Paperclip::Attachment.default_options[:s3_credentials] = {
-    bucket: ENV.fetch('S3_BUCKET'),
-    access_key_id: ENV.fetch('AWS_ACCESS_KEY_ID'),
-    secret_access_key: ENV.fetch('AWS_SECRET_ACCESS_KEY'),
-  }
-
-  unless ENV['S3_ENDPOINT'].blank?
-    Paperclip::Attachment.default_options[:s3_options] = {
-      endpoint: ENV['S3_ENDPOINT'],
-      signature_version: ENV['S3_SIGNATURE_VERSION'] || 'v4',
-      force_path_style: true,
+  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
+    },
+    fog_directory: ENV['S3_BUCKET'],
+    fog_options: {
+      acl: ENV.fetch('S3_PERMISSION') { 'public-read' },
+      cache_control: 'max-age=315576000',
     }
+  )
 
-    Paperclip::Attachment.default_options[:url] = ':s3_path_url'
+  if ENV.has_key?('S3_ENDPOINT')
+    Paperclip::Attachment.default_options[:fog_credentials].merge!(
+      endpoint: ENV['S3_ENDPOINT'],
+      path_style: true
+    )
+    Paperclip::Attachment.default_options[:fog_host] = "#{s3_protocol}://#{s3_hostname}/#{ENV['S3_BUCKET']}"
   end
 
-  unless ENV['S3_CLOUDFRONT_HOST'].blank?
-    Paperclip::Attachment.default_options[:url]           = ':s3_alias_url'
-    Paperclip::Attachment.default_options[:s3_host_alias] = ENV['S3_CLOUDFRONT_HOST']
+  if ENV.has_key?('S3_CLOUDFRONT_HOST')
+    Paperclip::Attachment.default_options[:fog_host] = "#{s3_protocol}://#{ENV['S3_CLOUDFRONT_HOST']}"
   end
 elsif ENV['SWIFT_ENABLED'] == 'true'
+  require 'fog/openstack'
+
   Paperclip::Attachment.default_options.merge!(
-    path: ':class/:attachment/:id_partition/:style/:filename',
-    storage: :fog,
     fog_credentials: {
       provider: 'OpenStack',
-      openstack_username: ENV.fetch('SWIFT_USERNAME'),
-      openstack_project_name: ENV.fetch('SWIFT_TENANT'),
-      openstack_tenant: ENV.fetch('SWIFT_TENANT'), # Some OpenStack-v2 ignores project_name but needs tenant
-      openstack_api_key: ENV.fetch('SWIFT_PASSWORD'),
-      openstack_auth_url: ENV.fetch('SWIFT_AUTH_URL'),
-      openstack_domain_name: ENV['SWIFT_DOMAIN_NAME'] || 'default',
+      openstack_username: ENV['SWIFT_USERNAME'],
+      openstack_project_name: ENV['SWIFT_TENANT'],
+      openstack_tenant: ENV['SWIFT_TENANT'], # Some OpenStack-v2 ignores project_name but needs tenant
+      openstack_api_key: ENV['SWIFT_PASSWORD'],
+      openstack_auth_url: ENV['SWIFT_AUTH_URL'],
+      openstack_domain_name: ENV.fetch('SWIFT_DOMAIN_NAME') { 'default' },
       openstack_region: ENV['SWIFT_REGION'],
-      openstack_cache_ttl: ENV['SWIFT_CACHE_TTL'] || 60,
+      openstack_cache_ttl: ENV.fetch('SWIFT_CACHE_TTL') { 60 },
     },
-    fog_directory: ENV.fetch('SWIFT_CONTAINER'),
+    fog_directory: ENV['SWIFT_CONTAINER'],
     fog_host: ENV['SWIFT_OBJECT_URL'],
     fog_public: true
   )
 else
-  Paperclip::Attachment.default_options[:path] = (ENV['PAPERCLIP_ROOT_PATH'] || ':rails_root/public/system') + '/:class/:attachment/:id_partition/:style/:filename'
-  Paperclip::Attachment.default_options[:url]  = (ENV['PAPERCLIP_ROOT_URL'] || '/system') + '/:class/:attachment/:id_partition/:style/:filename'
+  require 'fog/local'
+
+  Paperclip::Attachment.default_options.merge!(
+    fog_credentials: {
+      provider: 'Local',
+      local_root: ENV.fetch('PAPERCLIP_ROOT_PATH') { Rails.root.join('public', 'system') },
+    },
+    fog_directory: '',
+    fog_host: ENV.fetch('PAPERCLIP_ROOT_URL') { '/system' }
+  )
 end
diff --git a/config/initializers/statsd.rb b/config/initializers/statsd.rb
index 17a176174..45702ac94 100644
--- a/config/initializers/statsd.rb
+++ b/config/initializers/statsd.rb
@@ -4,7 +4,7 @@ if ENV['STATSD_ADDR'].present?
   host, port = ENV['STATSD_ADDR'].split(':')
 
   statsd = ::Statsd.new(host, port)
-  statsd.namespace = ['Mastodon', Rails.env].join('.')
+  statsd.namespace = ENV.fetch('STATSD_NAMESPACE') { ['Mastodon', Rails.env].join('.') }
 
   ::NSA.inform_statsd(statsd) do |informant|
     informant.collect(:action_controller, :web)
diff --git a/config/locales/activerecord.zh-CN.yml b/config/locales/activerecord.zh-CN.yml
new file mode 100644
index 000000000..8628d6677
--- /dev/null
+++ b/config/locales/activerecord.zh-CN.yml
@@ -0,0 +1,13 @@
+---
+zh-CN:
+  activerecord:
+    errors:
+      models:
+        account:
+          attributes:
+            username:
+              invalid: 只能使用字母、数字和下划线
+        status:
+          attributes:
+            reblog:
+              taken: 已经被转嘟过
diff --git a/config/locales/devise.oc.yml b/config/locales/devise.oc.yml
index 5cccb48ff..266f87373 100644
--- a/config/locales/devise.oc.yml
+++ b/config/locales/devise.oc.yml
@@ -9,7 +9,7 @@ oc:
       already_authenticated: Sètz ja connectat.
       inactive: Vòstre compte es pas encara activat.
       invalid: Corrièl o senhal invalid.
-      last_attempt: Vos demòra un ensag abans que vòstre compte siasgue blocat.
+      last_attempt: Vos demòra un ensag abans que vòstre compte siasque blocat.
       locked: Vòstre compte es blocat.
       not_found_in_database: Corrièl o senhal invalid.
       timeout: Vòstra session s’a acabat. Mercés de vos tornar connectar per contunhar.
diff --git a/config/locales/devise.zh-CN.yml b/config/locales/devise.zh-CN.yml
index 8cc814224..0e40fcc90 100644
--- a/config/locales/devise.zh-CN.yml
+++ b/config/locales/devise.zh-CN.yml
@@ -2,24 +2,24 @@
 zh-CN:
   devise:
     confirmations:
-      confirmed: 成功验证您的邮箱地址。
-      send_instructions: 您的电子邮箱将在几分钟后收到一封邮箱确认邮件。
-      send_paranoid_instructions: 如果您的邮箱存在于我们的数据库中,您将收到一封确认帐号的邮件。
+      confirmed: 成功验证你的邮箱地址。
+      send_instructions: 你的电子邮箱将在几分钟后收到一封确认邮件。如果没有,请检查你的垃圾邮箱。
+      send_paranoid_instructions: 如果你的邮箱存在于我们的数据库中,你将收到一封确认注册的邮件。如果没有,请检查你的垃圾邮箱。
     failure:
-      already_authenticated: 您已经登录。
-      inactive: 您还没有激活帐户。
+      already_authenticated: 你已经登录。
+      inactive: 你还没有激活帐户。
       invalid: " %{authentication_keys} 或密码错误。"
-      last_attempt: 您还有最后一次尝试机会,再次失败您的帐号将被锁定。
-      locked: 您的帐号已被锁定。
+      last_attempt: 你还有最后一次尝试机会,再次失败你的帐户将被锁定。
+      locked: 你的帐户已被锁定。
       not_found_in_database: "%{authentication_keys}或密码错误。"
-      timeout: 您已登录超时,请重新登录。
+      timeout: 你已登录超时,请重新登录。
       unauthenticated: 继续操作前请注册或者登录。
-      unconfirmed: 继续操作前请先确认您的帐号。
+      unconfirmed: 继续操作前请先确认你的帐户。
     mailer:
       confirmation_instructions:
         subject: Mastodon 帐户确认信息
       email_changed:
-        subject: Mastodon 电邮已被修改
+        subject: Mastodon 电子邮件地址已被修改
       password_change:
         subject: Mastodon 密码已被重置
       reset_password_instructions:
@@ -30,33 +30,33 @@ zh-CN:
       failure: 由于%{reason},无法从%{kind}获得授权。
       success: 成功地从%{kind}获得授权。
     passwords:
-      no_token: 无重置邮件不可访问密码重置页面。如果您是从重置邮件来到了这个页面,请确保您输入的URL完整的。
-      send_instructions: 几分钟后,您将收到重置密码的电子邮件。
-      send_paranoid_instructions: 如果您的邮箱存在于我们的数据库中,您将收到一封找回密码的邮件。
-      updated: 您的密码已修改成功,您现在已登录。
-      updated_not_active: 您的密码已修改成功。
+      no_token: 你必须通过密码重置邮件才能访问这个页面。如果你确实如此,请确保你输入的 URL 是完整的。
+      send_instructions: 几分钟后,你将收到重置密码的电子邮件。如果没有,请检查你的垃圾邮箱。
+      send_paranoid_instructions: 如果你的邮箱存在于我们的数据库中,你将收到一封找回密码的邮件。如果没有,请检查你的垃圾邮箱。
+      updated: 你的密码已修改成功,你现在已登录。
+      updated_not_active: 你的密码已修改成功。
     registrations:
-      destroyed: 再见!您的帐户已成功注销。我们希望很快可以再见到您。
-      signed_up: 欢迎!您已注册成功。
-      signed_up_but_inactive: 您已注册,但尚未激活帐号。
-      signed_up_but_locked: 您已注册,但帐号被锁定了。
-      signed_up_but_unconfirmed: 一封带有确认链接的邮件已经发送至您的邮箱,请检查邮箱(包括垃圾邮箱),并点击该链接激活您的帐号。
-      update_needs_confirmation: 信息更新成功,但我们需要验证您的新电子邮件地址,请检查邮箱(包括垃圾邮箱),并点击该链接激活您的帐号。
-      updated: 帐号资料更新成功。
+      destroyed: 再见!你的帐户已成功注销。我们希望很快可以再见到你。
+      signed_up: 欢迎!你已注册成功。
+      signed_up_but_inactive: 你已注册,但尚未激活帐户。
+      signed_up_but_locked: 你已注册,但帐户被锁定了。
+      signed_up_but_unconfirmed: 一封带有确认链接的邮件已经发送至你的邮箱,请点击邮件中的链接以激活你的帐户。如果没有,请检查你的垃圾邮箱。
+      update_needs_confirmation: 信息更新成功,但我们需要验证你的新电子邮件地址,请点击邮件中的链接以确认。如果没有,请检查你的垃圾邮箱。
+      updated: 帐户资料更新成功。
     sessions:
-      already_signed_out: 已经退出成功。
+      already_signed_out: 已成功登出。
       signed_in: 登录成功。
-      signed_out: 退出成功。
+      signed_out: 登出成功。
     unlocks:
-      send_instructions: 几分钟后,您将收到一封解锁帐号的邮件。
-      send_paranoid_instructions: 如果您的邮箱存在于我们的数据库中,您将收到一封解锁帐号的邮件。
-      unlocked: 您的帐号已成功解锁,您现在已登录。
+      send_instructions: 几分钟后,你将收到一封解锁帐户的邮件。如果没有,请检查你的垃圾邮箱。
+      send_paranoid_instructions: 如果你的邮箱存在于我们的数据库中,你将收到一封解锁帐户的邮件。如果没有,请检查你的垃圾邮箱。
+      unlocked: 你的帐户已成功解锁。登录以继续。
   errors:
     messages:
       already_confirmed: 已经确认,请重新登录。
-      confirmation_period_expired: 注册帐号后须在%{period}以内确认。请重新注册。
+      confirmation_period_expired: 注册帐户后须在 %{period}以内确认。请重新注册。
       expired: 邮件确认已过期,请重新注册。
       not_found: 找不到。
       not_locked: 未锁定。
       not_saved:
-        other: 发生%{count}个错误,导致%{resource}保存失败:
+        other: 发生 %{count} 个错误,导致%{resource}保存失败:
diff --git a/config/locales/doorkeeper.pl.yml b/config/locales/doorkeeper.pl.yml
index fa4324e4d..33f133c06 100644
--- a/config/locales/doorkeeper.pl.yml
+++ b/config/locales/doorkeeper.pl.yml
@@ -59,7 +59,7 @@ pl:
       error:
         title: Wystapił błąd
       new:
-        able_to: Będzie w stanie
+        able_to: Uzyska
         prompt: Aplikacja %{client_name} prosi o dostęp do Twojego konta
         title: Wymagana jest autoryzacja
       show:
@@ -114,6 +114,6 @@ pl:
       application:
         title: Uwierzytelnienie OAuth jest wymagane
     scopes:
-      follow: śledzenie, blokowanie, usuwanie blokady, anulowanie śledzenia kont
+      follow: możliwość śledzenia, blokowania, usuwania blokad, anulowania śledzenia kont
       read: dostęp do odczytu danych konta
-      write: publikowanie wpisów w Twoim imieniu
+      write: możliwość publikowania wpisów w Twoim imieniu
diff --git a/config/locales/doorkeeper.zh-CN.yml b/config/locales/doorkeeper.zh-CN.yml
index 12b38b81f..c7e8f368d 100644
--- a/config/locales/doorkeeper.zh-CN.yml
+++ b/config/locales/doorkeeper.zh-CN.yml
@@ -3,15 +3,16 @@ zh-CN:
   activerecord:
     attributes:
       doorkeeper/application:
-        name: 名称
-        redirect_uri: 登录回调地址
+        name: 应用名称
+        redirect_uri: 重定向 URI
         scopes: 权限范围
+        website: 应用网站
     errors:
       models:
         doorkeeper/application:
           attributes:
             redirect_uri:
-              fragment_present: 不能包含片段(#)
+              fragment_present: 不能包含网址片段(#)
               invalid_uri: 必须是有效的 URL 格式
               relative_uri: 必须是绝对的 URL 地址
               secured_uri: 必须是 HTTPS/SSL 的 URL 地址
@@ -30,60 +31,65 @@ zh-CN:
       form:
         error: 抱歉! 提交信息的时候遇到了下面的错误
       help:
-        native_redirect_uri: 使用 %{native_redirect_uri} 作为本地测试
+        native_redirect_uri: 本地测试请使用 %{native_redirect_uri}
         redirect_uri: 每行只能有一个 URL
-        scopes: 用空格隔开权限范围,留空则使用默认设置
+        scopes: 用空格分割权限范围,留空则使用默认设置
       index:
-        callback_url: 登录回调地址
+        application: 应用
+        callback_url: 回调 URL
+        delete: 删除
         name: 名称
         new: 创建新应用
+        scopes: 权限范围
+        show: 显示
         title: 你的应用
       new:
         title: 创建新应用
       show:
         actions: 操作
         application_id: 应用 ID
-        callback_urls: 登录回调地址
+        callback_urls: 回调 URL
         scopes: 权限范围
-        secret: 私钥
+        secret: 应用密钥
         title: 应用:%{name}
     authorizations:
       buttons:
-        authorize: 授权
-        deny: 拒绝
+        authorize: 同意授权
+        deny: 拒绝授权
       error:
-        title: 存在错误
+        title: 发生错误
       new:
-        able_to: 此应用将会
-        prompt: 授权 %{client_name} 使用你的帐号?
-        title: 需要你授权
+        able_to: 此应用将能够
+        prompt: 授权 %{client_name} 使用你的帐户?
+        title: 需要授权
       show:
-        title: Copy this authorization code and paste it to the application.
+        title: 接下来请复制此处的授权代码并粘贴到应用中。
     authorized_applications:
       buttons:
-        revoke: 注销
+        revoke: 撤销授权
       confirmations:
-        revoke: 确定要注销此应用的认证信息吗?
+        revoke: 确定要撤销对此应用的授权吗?
       index:
         application: 应用
         created_at: 授权时间
         date_format: "%Y-%m-%d %H:%M:%S"
-        title: 你授权的应用列表
+        scopes: 权限范围
+        title: 已授权的应用列表
     errors:
       messages:
         access_denied: 用户或服务器拒绝了请求
-        credential_flow_not_configured: Resource Owner Password Credentials flow failed,原因是 Doorkeeper.configure.resource_owner_from_credentials 尚未设置。
-        invalid_client: 由于未知、不支持或没有客户端,认证失败
+        credential_flow_not_configured: 由于 Doorkeeper.configure.resource_owner_from_credentials 尚未配置,应用验证授权流程失败。
+        invalid_client: 由于应用信息未知、未提交认证信息或使用了不支持的认证方式,认证失败
         invalid_grant: 授权方式无效,或者登录回调地址无效、过期或已被撤销
         invalid_redirect_uri: 无效的登录回调地址
-        invalid_request: 这个请求缺少必要的参数,或者参数值、格式不正确
-        invalid_resource_owner: 资源所有者认证无效或没有所有者
-        invalid_scope: 请求范围无效、未知或格式不正确
+        invalid_request: 请求缺少必要的参数,或者参数值、格式不正确
+        invalid_resource_owner: 资源所有者认证无效,或找不到所有者
+        invalid_scope: 请求的权限范围无效、未知或格式不正确
         invalid_token:
           expired: 访问令牌已过期
           revoked: 访问令牌已被吊销
           unknown: 访问令牌无效
-        resource_owner_authenticator_not_configured: Resource Owner find failed,原因是 Doorkeeper.configure.resource_owner_authenticator 尚未设置。
+        resource_owner_authenticator_not_configured: 由于 Doorkeeper.configure.resource_owner_authenticator 尚未配置,查找资源所有者失败。
         server_error: 服务器异常,无法处理请求
         temporarily_unavailable: 服务器维护中或负载过高,暂时无法处理请求
         unauthorized_client: 未授权的应用,请求无法执行
@@ -99,16 +105,15 @@ zh-CN:
           notice: 应用修改成功
       authorized_applications:
         destroy:
-          notice: 已成功注销了应用的认证信息
+          notice: 已成功撤销对此应用的授权
     layouts:
       admin:
         nav:
           applications: 应用
-          home: 首页
           oauth2_provider: OAuth2 提供商
       application:
-        title: OAuth 认证
+        title: 需要 OAuth 认证
     scopes:
-      follow: 关注(或取消关注),屏蔽(或取消屏蔽)用户
-      read: 读取你的账户数据
+      follow: 关注(或取消关注)、屏蔽(或取消屏蔽)用户
+      read: 读取你的帐户数据
       write: 为你发表嘟文
diff --git a/config/locales/en.yml b/config/locales/en.yml
index d5c46470c..a590b189f 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -43,7 +43,7 @@ en:
     people_followed_by: People whom %{name} follows
     people_who_follow: People who follow %{name}
     posts: Toots
-    posts_with_replies: Toots with replies
+    posts_with_replies: Toots and replies
     remote_follow: Remote follow
     reserved_username: The username is reserved
     roles:
@@ -59,13 +59,19 @@ en:
       destroyed_msg: Moderation note successfully destroyed!
     accounts:
       are_you_sure: Are you sure?
+      by_domain: Domain
       confirm: Confirm
       confirmed: Confirmed
+      demote: Demote
+      disable: Disable
       disable_two_factor_authentication: Disable 2FA
+      disabled: Disabled
       display_name: Display name
       domain: Domain
       edit: Edit
       email: E-mail
+      enable: Enable
+      enabled: Enabled
       feed_url: Feed URL
       followers: Followers
       followers_url: Followers URL
@@ -77,7 +83,9 @@ en:
         local: Local
         remote: Remote
         title: Location
+      login_status: Login status
       media_attachments: Media attachments
+      memorialize: Turn into memoriam
       moderation:
         all: All
         silenced: Silenced
@@ -94,6 +102,7 @@ en:
       outbox_url: Outbox URL
       perform_full_suspension: Perform full suspension
       profile_url: Profile URL
+      promote: Promote
       protocol: Protocol
       public: Public
       push_subscription_expires: PuSH subscription expires
@@ -101,6 +110,11 @@ en:
       reset: Reset
       reset_password: Reset password
       resubscribe: Resubscribe
+      role: Permissions
+      roles:
+        admin: Administrator
+        moderator: Moderator
+        user: User
       salmon_url: Salmon URL
       search: Search
       shared_inbox_url: Shared Inbox URL
@@ -133,6 +147,7 @@ en:
       listed: Listed
       new:
         title: Add new custom emoji
+      overwrite: Overwrite
       shortcode: Shortcode
       shortcode_hint: At least 2 characters, only alphanumeric characters and underscores
       title: Custom emojis
@@ -377,6 +392,7 @@ en:
       following: Following list
       muting: Muting list
     upload: Upload
+  in_memoriam_html: In Memoriam.
   keyword_mutes:
     add_keyword: Add keyword
     edit: Edit
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index 0d9c227f3..55588d111 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -437,7 +437,7 @@ fr:
       action_favourite: Ajouter aux favoris
       title: "%{name} vous a mentionné·e"
     reblog:
-      title: "%{name} a partagé⋅e votre statut"
+      title: "%{name} a partagé votre statut"
   remote_follow:
     acct: Entrez votre pseudo@instance depuis lequel vous voulez suivre ce⋅tte utilisateur⋅rice
     missing_resource: L’URL de redirection n’a pas pu être trouvée
diff --git a/config/locales/ja.yml b/config/locales/ja.yml
index 391556040..82b642b5b 100644
--- a/config/locales/ja.yml
+++ b/config/locales/ja.yml
@@ -59,13 +59,19 @@ ja:
       destroyed_msg: モデレーションメモを削除しました
     accounts:
       are_you_sure: 本当に実行しますか?
+      by_domain: ドメイン
       confirm: 確認
       confirmed: 確認済み
+      demote: 降格
+      disable: 無効化
       disable_two_factor_authentication: 二段階認証を無効にする
+      disabled: 無効
       display_name: 表示名
       domain: ドメイン
       edit: 編集
       email: E-mail
+      enable: 有効化
+      enabled: 有効
       feed_url: フィードURL
       followers: フォロワー数
       followers_url: Followers URL
@@ -77,7 +83,9 @@ ja:
         local: ローカル
         remote: リモート
         title: ロケーション
+      login_status: ログイン
       media_attachments: 添付されたメディア
+      memorialize: 追悼アカウント化
       moderation:
         all: すべて
         silenced: サイレンス中
@@ -94,6 +102,7 @@ ja:
       outbox_url: Outbox URL
       perform_full_suspension: 完全に活動停止させる
       profile_url: プロフィールURL
+      promote: 昇格
       protocol: プロトコル
       public: パブリック
       push_subscription_expires: PuSH購読期限
@@ -101,6 +110,11 @@ ja:
       reset: リセット
       reset_password: パスワード再設定
       resubscribe: 再講読
+      role: 役割
+      roles:
+        admin: 管理者
+        moderator: モデレーター
+        user: ユーザー
       salmon_url: Salmon URL
       search: 検索
       shared_inbox_url: Shared Inbox URL
@@ -130,11 +144,16 @@ ja:
       enable: 有効化
       enabled_msg: 絵文字を有効化しました
       image_hint: 50KBまでのPNG画像を利用できます。
+      listed: 収載
       new:
         title: 新規カスタム絵文字の追加
+      overwrite: 上書き
       shortcode: ショートコード
       shortcode_hint: 2文字以上の半角英数字とアンダーバーのみ利用できます。
       title: カスタム絵文字
+      unlisted: 未収載
+      update_failed_msg: 絵文字を更新できませんでした
+      updated_msg: 絵文字の更新に成功しました
       upload: アップロード
     domain_blocks:
       add_new: 新規追加
@@ -373,6 +392,7 @@ ja:
       following: フォロー中のアカウントリスト
       muting: ミュートしたアカウントリスト
     upload: アップロード
+  in_memoriam_html: 故人を偲んで
   landing_strip_html: "<strong>%{name}</strong> さんはインスタンス %{link_to_root_path} のユーザーです。アカウントさえ持っていればフォローしたり会話したりできます。"
   landing_strip_signup_html: もしお持ちでないなら <a href="%{sign_up_path}">こちら</a> からサインアップできます。
   media_attachments:
@@ -390,8 +410,8 @@ ja:
         one: "新しい1件の通知 \U0001F418"
         other: "新しい%{count}件の通知 \U0001F418"
     favourite:
-      body: 'あなたのトゥートが %{name} さんにお気に入り登録されました:'
-      subject: "%{name} さんがあなたのトゥートをお気に入りに登録しました"
+      body: "%{name} さんにお気に入り登録された、あなたのトゥートがあります:"
+      subject: "%{name} さんにお気に入りに登録されました"
     follow:
       body: "%{name} さんにフォローされています"
       subject: "%{name} さんにフォローされています"
@@ -402,8 +422,8 @@ ja:
       body: "%{name} さんから返信がありました:"
       subject: "%{name} さんに返信されました"
     reblog:
-      body: 'あなたのトゥートが %{name} さんにブーストされました:'
-      subject: あなたのトゥートが %{name} さんにブーストされました
+      body: "%{name} さんにブーストされた、あなたのトゥートがあります:"
+      subject: "%{name} さんにブーストされました"
   number:
     human:
       decimal_units:
diff --git a/config/locales/oc.yml b/config/locales/oc.yml
index 0d2a8c2f6..914cc7e9d 100644
--- a/config/locales/oc.yml
+++ b/config/locales/oc.yml
@@ -59,13 +59,18 @@ oc:
       destroyed_msg: Nòta de moderacion ben suprimida !
     accounts:
       are_you_sure: Sètz segur ?
+      by_domain: Domeni
       confirm: Confirmar
       confirmed: Confirmat
+      disable: Desactivar
       disable_two_factor_authentication: Desactivar 2FA
+      disabled: Desactivat
       display_name: Escais-nom
       domain: Domeni
       edit: Modificar
       email: Corrièl
+      enable: Activar
+      enabled: Activat
       feed_url: Flux URL
       followers: Seguidors
       followers_url: URL dels seguidors
@@ -77,7 +82,9 @@ oc:
         local: Locals
         remote: Alonhats
         title: Emplaçament
+      login_status: Estat formulari de connexion
       media_attachments: Mèdias ajustats
+      memorialize: Passar en memorial
       moderation:
         all: Tot
         silenced: Rescondut
@@ -130,11 +137,16 @@ oc:
       enable: Activar
       enabled_msg: Aqueste emoji es ben activat
       image_hint: PNG cap a 50Ko
+      listed: Listat
       new:
         title: Ajustar un nòu emoji personal
+      overwrite: Remplaçar
       shortcode: Acorchi
       shortcode_hint: Almens 2 caractèrs, solament alfanumerics e jonhent bas
       title: Emojis personals
+      unlisted: Pas listat
+      update_failed_msg: Mesa a jorn de l’emoji fracasada
+      updated_msg: Emoji ben mes a jorn !
       upload: Enviar
     domain_blocks:
       add_new: N’ajustar un nòu
@@ -451,6 +463,7 @@ oc:
       following: Lista de mond que seguètz
       muting: Lista de mond que volètz pas legir
     upload: Importar
+  in_memoriam_html: En Memòria.
   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>.
   media_attachments:
diff --git a/config/locales/pl.yml b/config/locales/pl.yml
index c58c1c2f8..49dace354 100644
--- a/config/locales/pl.yml
+++ b/config/locales/pl.yml
@@ -9,7 +9,7 @@ pl:
     contact_missing: Nie ustawiono
     contact_unavailable: Nie dotyczy
     description_headline: Czym jest %{domain}?
-    domain_count_after: instancji
+    domain_count_after: instancjami
     domain_count_before: Serwer połączony z
     extended_description_html: |
       <h3>Dobre miejsce na zasady użytkowania</h3>
@@ -59,13 +59,19 @@ pl:
       destroyed_msg: Pomyślnie usunięto notatkę moderacyjną!
     accounts:
       are_you_sure: Jesteś tego pewien?
+      by_domain: Domena
       confirm: Potwierdź
       confirmed: Potwierdzono
+      demote: Degraduj
+      disable: Dezaktywuj
       disable_two_factor_authentication: Wyłącz uwierzytelnianie dwuetapowe
+      disabled: Dezaktywowano
       display_name: Wyświetlana nazwa
       domain: Domena
       edit: Edytuj
       email: Adres e-mail
+      enable: Aktywuj
+      enabled: Aktywowano
       feed_url: Adres kanału
       followers: Śledzący
       followers_url: Adres śledzących
@@ -77,7 +83,9 @@ pl:
         local: Lokalne
         remote: Zdalne
         title: Położenie
+      login_status: Stan logowania
       media_attachments: Załączniki multimedialne
+      memorialize: Przełącz na „In Memoriam”
       moderation:
         all: Wszystkie
         silenced: Wyciszone
@@ -94,6 +102,7 @@ pl:
       outbox_url: Adres skrzynki nadawczej
       perform_full_suspension: Całkowicie zawieś
       profile_url: Adres profilu
+      promote: Podnieś uprawnienia
       protocol: Protokół
       public: Publiczne
       push_subscription_expires: Subskrypcja PuSH wygasa
@@ -101,6 +110,11 @@ pl:
       reset: Resetuj
       reset_password: Resetuj hasło
       resubscribe: Ponów subskrypcję
+      role: Uprawnienia
+      roles:
+        admin: Administrator
+        moderator: Moderator
+        user: Użytkownik
       salmon_url: Adres Salmon
       search: Szukaj
       shared_inbox_url: Adres udostępnianej skrzynki
@@ -109,7 +123,7 @@ pl:
         report: zgłoszeń
         targeted_reports: Zgłoszenia dotyczące tego użytkownika
       silence: Wycisz
-      statuses: Statusy
+      statuses: Wpisy
       subscribe: Subskrybuj
       title: Konta
       undo_silenced: Cofnij wyciszenie
@@ -130,12 +144,16 @@ pl:
       enable: Włącz
       enabled_msg: Pomyślnie przywrócono emoji
       image_hint: Plik PNG ważący do 50KB
+      listed: Widoczne
       new:
         title: Dodaj nowe niestandardowe emoji
       shortcode: Shortcode
       shortcode_hint: Co najmniej 2 znaki, tylko znaki alfanumeryczne i podkreślniki
       title: Niestandardowe emoji
-      upload: Wyślij
+      unlisted: Niewidoczne
+      update_failed_msg: Nie udało się zaktualizować emoji
+      updated_msg: Pomyślnie zaktualizowano emoji
+      upload: Dodaj
     domain_blocks:
       add_new: Dodaj nową
       created_msg: Blokada domen jest przetwarzana
@@ -203,7 +221,7 @@ pl:
       reported_by: Zgłaszający
       resolved: Rozwiązane
       silence_account: Wycisz konto
-      status: Status
+      status: Stan
       suspend_account: Zawieś konto
       target: Cel
       title: Zgłoszenia
@@ -212,7 +230,7 @@ pl:
     settings:
       bootstrap_timeline_accounts:
         desc_html: Oddzielaj nazwy użytkowników przecinkami. Działa tylko dla niezablokowanych kont w obrębie instancji. Jeżeli puste, zostaną użyte konta administratorów instancji.
-        title: Domyślne obserwacje nowych użytkowników
+        title: Domyślnie obserwowani użytkownicy
       contact_information:
         email: Służbowy adres e-mail
         username: Nazwa użytkownika do kontaktu
@@ -256,7 +274,7 @@ pl:
         show: Pokaż zawartość multimedialną
         title: Media
       no_media: Bez zawartości multimedialnej
-      title: Statusy konta
+      title: Wpisy konta
       with_media: Z zawartością multimedialną
     subscriptions:
       callback_url: URL zwrotny
@@ -332,7 +350,7 @@ pl:
   errors:
     '403': Nie masz uprawnień, aby wyświetlić tę stronę.
     '404': Strona, którą próbujesz odwiedzić, nie istnieje.
-    '410': Strona, którą próbujesz odwiedzić, już nie istnieje.
+    '410': Strona, którą próbujesz odwiedzić, przestała istnieć.
     '422':
       content: Sprawdzanie bezpieczeństwa nie powiodło się. Czy blokujesz pliki cookie?
       title: Sprawdzanie bezpieczeństwa nie powiodło się
@@ -349,7 +367,7 @@ pl:
     storage: Urządzenie przechowujące dane
   followers:
     domain: Domena
-    explanation_html: Jeżeli chcesz mieć pewność, kto może przeczytać Twoje statusy, musisz kontrolować, kto śledzi Twój profil. <strong>Twoje prywatne statusy są dostarczane na te instancje, na których jesteś śledzony</strong>. Możesz sprawdzać, kto Cię śledzi i blokować ich, jeśli nie ufasz właścicielom lub oprogramowaniu danej instancji.
+    explanation_html: Jeżeli chcesz mieć pewność, kto może przeczytać Twoje wpisy, musisz kontrolować, kto śledzi Twój profil. <strong>Twoje prywatne wpisy są dostarczane na te instancje, na których jesteś śledzony</strong>. Możesz sprawdzać, kto Cię śledzi i blokować ich, jeśli nie ufasz właścicielom lub oprogramowaniu danej instancji.
     followers_count: Liczba śledzących
     lock_link: Zablokuj swoje konto
     purge: Przestań śledzić
@@ -357,7 +375,7 @@ pl:
       one: W trakcie usuwania śledzących z jednej domeny…
       other: W trakcie usuwania śledzących z %{count} domen…
     true_privacy_html: Pamiętaj, że <strong>rzeczywista prywatność może zostać uzyskana wyłącznie dzięki szyfrowaniu end-to-end</strong>.
-    unlocked_warning_html: Każdy może Cię śledzić, aby natychmiastowo zobaczyć twoje statusy. %{lock_link} aby móc kontrolować, kto Cię śledzi.
+    unlocked_warning_html: Każdy może Cię śledzić, aby natychmiastowo zobaczyć twoje wpisy. %{lock_link} aby móc kontrolować, kto Cię śledzi.
     unlocked_warning_title: Twoje konto nie jest zablokowane
   generic:
     changes_saved_msg: Ustawienia zapisane!
@@ -374,11 +392,12 @@ pl:
       following: Lista śledzonych
       muting: Lista wyciszonych
     upload: Załaduj
+  in_memoriam_html: Ku pamięci.
   landing_strip_html: "<strong>%{name}</strong> ma konto na %{link_to_root_path}. Możesz je śledzić i wejść z nim w interakcję jeśli masz konto gdziekolwiek w Fediwersum."
   landing_strip_signup_html: Jeśli jeszcze go nie masz, możesz <a href="%{sign_up_path}">stworzyć konto</a>.
   media_attachments:
     validations:
-      images_and_video: Nie możesz załączyć pliku wideo do statusu, który zawiera już zdjęcia
+      images_and_video: Nie możesz załączyć pliku wideo do wpisu, który zawiera już zdjęcia
       too_many: Nie możesz załączyć więcej niż 4 plików
   notification_mailer:
     digest:
@@ -431,7 +450,7 @@ pl:
     web: Sieć
   push_notifications:
     favourite:
-      title: "%{name} dodał Twój status do ulubionych"
+      title: "%{name} dodał Twój wpis do ulubionych"
     follow:
       title: "%{name} zaczął Cię śledzić"
     group:
@@ -442,7 +461,7 @@ pl:
       action_favourite: Dodaj do ulubionych
       title: "%{name} wspomniał o Tobie"
     reblog:
-      title: "%{name} podbił Twój status"
+      title: "%{name} podbił Twój wpis"
   remote_follow:
     acct: Podaj swój adres (nazwa@domena), z którego chcesz śledzić
     missing_resource: Nie udało się znaleźć adresu przekierowania z Twojej domeny
@@ -610,7 +629,7 @@ pl:
     manual_instructions: 'Jeżeli nie możesz zeskanować kodu QR, musisz wprowadzić ten kod ręcznie:'
     recovery_codes: Przywróć kody zapasowe
     recovery_codes_regenerated: Pomyślnie wygenerowano ponownie kody zapasowe
-    recovery_instructions_html: Jeżeli kiedykolwiek utracisz dostęp do telefonu, możesz wykorzystać jeden z kodów zapasowych, aby odzyskać dostęp do konta. <strong>Trzymaj je w bezpiecznym miejscu</strong>. Na przykład, wydrukuj je i przechowuj z ważnymu dokumentami.
+    recovery_instructions_html: Jeżeli kiedykolwiek utracisz dostęp do telefonu, możesz wykorzystać jeden z kodów zapasowych, aby odzyskać dostęp do konta. <strong>Trzymaj je w bezpiecznym miejscu</strong>. Na przykład, wydrukuj je i przechowuj z ważnymi dokumentami.
     setup: Skonfiguruj
     wrong_code: Wprowadzony kod jest niepoprawny! Czy czas serwera i urządzenia jest poprawny?
   users:
diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml
index 4b9ea152f..f5c61c01c 100644
--- a/config/locales/pt-BR.yml
+++ b/config/locales/pt-BR.yml
@@ -43,7 +43,7 @@ pt-BR:
     people_followed_by: Pessoas que %{name} segue
     people_who_follow: Pessoas que seguem %{name}
     posts: Toots
-    posts_with_replies: Toots com respostas
+    posts_with_replies: Toots e respostas
     remote_follow: Siga remotamente
     reserved_username: Este usuário está reservado
     roles:
@@ -59,13 +59,19 @@ pt-BR:
       destroyed_msg: Nota de moderação excluída com sucesso!
     accounts:
       are_you_sure: Você tem certeza?
+      by_domain: Domínio
       confirm: Confirmar
       confirmed: Confirmado
+      demote: Rebaixar
+      disable: Desativar
       disable_two_factor_authentication: Desativar 2FA
+      disabled: Desativado
       display_name: Nome de exibição
       domain: Domínio
       edit: Editar
       email: E-mail
+      enable: Ativar
+      enabled: Ativado
       feed_url: URL do feed
       followers: Seguidores
       followers_url: URL de seguidores
@@ -77,7 +83,9 @@ pt-BR:
         local: Local
         remote: Remoto
         title: Localização
+      login_status: Status de login
       media_attachments: Mídia(s) anexada(s)
+      memorialize: Tornar um memorial
       moderation:
         all: Todos
         silenced: Silenciados
@@ -94,6 +102,7 @@ pt-BR:
       outbox_url: URL da Outbox
       perform_full_suspension: Efetue suspensão total
       profile_url: URL do perfil
+      promote: Promover
       protocol: Protocolo
       public: Público
       push_subscription_expires: Inscrição PuSH expira
@@ -101,6 +110,11 @@ pt-BR:
       reset: Anular
       reset_password: Modificar senha
       resubscribe: Reinscrever-se
+      role: Permissões
+      roles:
+        admin: Administrador
+        moderator: Moderador
+        user: Usuário
       salmon_url: Salmon URL
       search: Pesquisar
       shared_inbox_url: URL da Inbox Compartilhada
@@ -130,11 +144,16 @@ pt-BR:
       enable: Habilitar
       enabled_msg: Emoji habilitado com sucesso!
       image_hint: PNG de até 50KB
+      listed: Listado
       new:
         title: Adicionar novo emoji customizado
+      overwrite: Sobrescrever
       shortcode: Atalho
       shortcode_hint: Pelo menos 2 caracteres, apenas caracteres alfanuméricos e underscores
       title: Emojis customizados
+      unlisted: Não listado
+      update_failed_msg: Não foi possível atualizar esse emoji
+      updated_msg: Emoji atualizado com sucesso!
       upload: Enviar
     domain_blocks:
       add_new: Adicionar novo
@@ -373,6 +392,7 @@ pt-BR:
       following: Pessoas que você segue
       muting: Lista de silêncio
     upload: Enviar
+  in_memoriam_html: Em memória de
   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:
diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml
index aafae48ce..faf41f316 100644
--- a/config/locales/simple_form.en.yml
+++ b/config/locales/simple_form.en.yml
@@ -54,6 +54,7 @@ en:
       interactions:
         must_be_follower: Block notifications from non-followers
         must_be_following: Block notifications from people you don't follow
+        must_be_following_dm: Block direct messages from people you don't follow
       notification_emails:
         digest: Send digest e-mails
         favourite: Send e-mail when someone favourites your status
diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml
index 993eae706..48eaf79b2 100644
--- a/config/locales/simple_form.ja.yml
+++ b/config/locales/simple_form.ja.yml
@@ -50,6 +50,7 @@ ja:
       interactions:
         must_be_follower: フォロワー以外からの通知をブロック
         must_be_following: フォローしていないユーザーからの通知をブロック
+        must_be_following_dm: フォローしていないユーザーからのダイレクトメッセージをブロック
       notification_emails:
         digest: タイムラインからピックアップしてメールで通知する
         favourite: お気に入りに登録された時にメールで通知する
diff --git a/config/locales/simple_form.oc.yml b/config/locales/simple_form.oc.yml
index d43c0d7eb..f178d1857 100644
--- a/config/locales/simple_form.oc.yml
+++ b/config/locales/simple_form.oc.yml
@@ -13,7 +13,7 @@ oc:
           one: Demòra encara <span class="name-counter">1</span> caractèr
           other: Demòran encara <span class="name-counter">%{count}</span> caractèrs
         setting_noindex: Aquò es destinat a vòstre perfil public e vòstra pagina d’estatuts
-        setting_theme: Aquò càmbia lo tèma grafic de Mastodon quand sètz connectat qualque siaque lo periferic.
+        setting_theme: Aquò càmbia lo tèma grafic de Mastodon quand sètz connectat qual que siasque lo periferic.
       imports:
         data: Fichièr CSV exportat d’una autra instància Mastodon
       sessions:
diff --git a/config/locales/simple_form.pl.yml b/config/locales/simple_form.pl.yml
index 68f84d109..8b539662c 100644
--- a/config/locales/simple_form.pl.yml
+++ b/config/locales/simple_form.pl.yml
@@ -58,6 +58,7 @@ pl:
       interactions:
         must_be_follower: Nie wyświetlaj powiadomień od osób, które Cię nie śledzą
         must_be_following: Nie wyświetlaj powiadomień od osób, których nie śledzisz
+        must_be_following_dm: Nie wyświetlaj wiadomości bezpośrednich od osób, których nie śledzisz
       notification_emails:
         digest: Wysyłaj podsumowania e-mailem
         favourite: Powiadamiaj mnie e-mailem, gdy ktoś polubi mój wpis
diff --git a/config/locales/simple_form.zh-CN.yml b/config/locales/simple_form.zh-CN.yml
index eafaa972e..f8c9461ad 100644
--- a/config/locales/simple_form.zh-CN.yml
+++ b/config/locales/simple_form.zh-CN.yml
@@ -3,49 +3,60 @@ zh-CN:
   simple_form:
     hints:
       defaults:
-        avatar: 最大 2MB,限 PNG, GIF 或 JPG 格式,将缩到 120x120px
-        display_name: 不起过 30 个字符
-        header: 最大 2MB,限 PNG, GIF 或 JPG 格式,将缩到 700x335px
-        locked: 默认仅向粉丝公开嘟文,需要手工批准粉丝关注请求。
-        note: 最多 160 个字符
+        avatar: 文件大小限制 2MB,只支持 PNG、GIF 或 JPG 格式。图片分辨率将会压缩至 120×120px
+        digest: 在你长时间未登录的情况下,我们会向你发送一份含有提及你的嘟文的摘要邮件
+        display_name: 还能输入 <span class="name-counter">%{count}</span> 个字符
+        header: 文件大小限制 2MB,只支持 PNG、GIF 或 JPG 格式。图片分辨率将会压缩至 700×335px
+        locked: 你需要手动审核所有关注请求
+        note: 还能输入 <span class="note-counter">%{count}</span> 个字符
+        setting_noindex: 此设置会影响到你的公开个人资料以及嘟文页面
+        setting_theme: 此设置会影响到你从任意设备登录时 Mastodon 的显示样式
       imports:
-        data: 从其他服务器节点导出的 CSV 文件
+        data: 请上传从其他 Mastodon 实例导出的 CSV 文件
       sessions:
-        otp: 输入你手机生成的两步验证码,或者恢复代码。
+        otp: 输入你手机上生成的双重认证码,或者任意一个恢复代码。
       user:
-        filtered_languages: 下列被选择的语言的嘟文将不会出现在你的公共时间轴上。
+        filtered_languages: 勾选语言的嘟文将不会出现在你的公共时间轴上
     labels:
       defaults:
         avatar: 头像
         confirm_new_password: 确认新密码
         confirm_password: 确认密码
         current_password: 当前密码
-        data: 数据
-        display_name: 显示名
-        email: 邮箱
-        filtered_languages: 屏蔽下列语言的嘟文
-        header: 个人页面顶部
+        data: 数据文件
+        display_name: 昵称
+        email: 电子邮件地址
+        filtered_languages: 语言过滤
+        header: 个人资料页横幅图片
         locale: 语言
-        locked: 隐私模式(锁嘟)
+        locked: 保护你的帐户(锁嘟)
         new_password: 新密码
         note: 简介
-        otp_attempt: 两步认证码
+        otp_attempt: 双重认证代码
         password: 密码
+        setting_auto_play_gif: 自动播放 GIF 动画
         setting_boost_modal: 在转嘟前询问我
-        setting_default_privacy: 嘟文默认隐私度
-        severity: 等级
+        setting_default_privacy: 嘟文默认可见范围
+        setting_default_sensitive: 总是将我发送的媒体文件标记为敏感内容
+        setting_delete_modal: 在删除嘟文前询问我
+        setting_noindex: 禁止搜索引擎建立索引
+        setting_reduce_motion: 降低过渡动画效果
+        setting_system_font_ui: 使用系统默认字体
+        setting_theme: 站点主题
+        setting_unfollow_modal: 在取消关注前询问我
+        severity: 级别
         type: 导入数据类型
         username: 用户名
       interactions:
-        must_be_follower: 隐藏没有关注你的用户的通知
-        must_be_following: 隐藏你不关注的用户的通知
+        must_be_follower: 屏蔽来自未关注你的用户的通知
+        must_be_following: 屏蔽来自你未关注的用户的通知
       notification_emails:
         digest: 发送摘要邮件
-        favourite: 当有用户赞了你的嘟文时,发电邮通知
-        follow: 当有用户关注你时,发电邮通知
-        follow_request: 当有用户要求关注你时,发电邮通知
-        mention: 当有用户在嘟文中提及你时,发电邮通知
-        reblog: 当有用户转嘟了你的嘟文时,发电邮通知
+        favourite: 当有用户收藏了你的嘟文时,发送电子邮件提醒我
+        follow: 当有用户关注你时,发送电子邮件提醒我
+        follow_request: 当有用户向你发送关注请求时,发送电子邮件提醒我
+        mention: 当有用户在嘟文中提及你时,发送电子邮件提醒我
+        reblog: 当有用户转嘟了你的嘟文时,发送电子邮件提醒我
     'no': 否
     required:
       mark: "*"
diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml
index 44d0f3803..b50c34fd0 100644
--- a/config/locales/zh-CN.yml
+++ b/config/locales/zh-CN.yml
@@ -1,196 +1,350 @@
 ---
 zh-CN:
   about:
-    about_mastodon_html: Mastodon(长毛象)是一个<em>自由、开放源码</em>的社交网站。它是一个分布式的服务,避免你的通信被单一商业机构垄断操控。请你选择一家你信任的 Mastodon 实例,在上面创建帐号,然后你就可以和任一 Mastodon 实例上的用户互通,享受无缝的<em>社交</em>交流。
+    about_hashtag_html: 这里展示的是带有话题标签 <strong>#%{hashtag}</strong> 的公开嘟文。如果你在象毛世界中拥有一个帐户,就可以加入讨论。
+    about_mastodon_html: Mastodon(长毛象)是一个基于开放式网络协议和自由、开源软件建立的社交网络,有着类似于电子邮件的分布式设计。
     about_this: 关于本实例
-    closed_registrations: 这个实例目前不开放注册 _(:3」∠)_
+    closed_registrations: 这个实例目前没有开放注册。不过,你可以前往其他实例注册一个帐户,同样可以加入到这个网络中哦!
     contact: 联络
+    contact_missing: 未设定
+    contact_unavailable: 未公开
     description_headline: 关于 %{domain}
     domain_count_after: 个其它实例
     domain_count_before: 现已接入
-    other_instances: 其它实例
+    extended_description_html: |
+      <h3>这里可以写一些规定</h3>
+      <p>本站尚未设置详细介绍。</p>
+    features:
+      humane_approach_body: Mastodon 从其他网络的失败经验中汲取了教训,致力于在与错误的社交媒体使用方式的斗争中做出符合伦理化设计的选择。
+      humane_approach_title: 更加以人为本
+      not_a_product_body: Mastodon 绝非一个商业网络。这里既没有广告,也没有数据挖掘,更没有围墙花园。中心机构在这里不复存在。
+      not_a_product_title: 作为用户,你并非一件商品
+      real_conversation_body: Mastodon 有着高达 500 字的字数限制,以及对内容的细化控制和媒体警告提示的支持,只为让你能够畅所欲言。
+      real_conversation_title: 为真正的交流而生
+      within_reach_body: 通过一个面向开发者友好的 API 生态系统,Mastodon 让你可以随时随地通过众多 iOS、Android 以及其他平台的应用与朋友们保持联系。
+      within_reach_title: 始终触手可及
+    find_another_instance: 寻找另一个实例
+    generic_description: "%{domain} 是这个庞大网络中的一台服务器"
+    hosted_on: 一个在 %{domain} 上运行的 Mastodon 实例
+    learn_more: 详细了解
+    other_instances: 其他实例
     source_code: 源码
     status_count_after: 条嘟文
     status_count_before: 他们共嘟出了
     user_count_after: 位用户
     user_count_before: 这里共注册有
+    what_is_mastodon: Mastodon 是什么?
   accounts:
     follow: 关注
-    followers: 粉丝
-    following: 关注
-    nothing_here: 神马都没有!
+    followers: 关注者
+    following: 正在关注
+    media: 媒体
+    nothing_here: 这里神马都没有!
     people_followed_by: 正关注
     people_who_follow: 粉丝
     posts: 嘟文
+    posts_with_replies: 嘟文和回复
     remote_follow: 跨站关注
+    reserved_username: 此用户名已保留
+    roles:
+      admin: 管理员
     unfollow: 取消关注
   admin:
+    account_moderation_notes:
+      account: 管理员
+      create: 新建
+      created_at: 日期
+      created_msg: 管理记录建立成功!
+      delete: 删除
+      destroyed_msg: 管理记录删除成功!
     accounts:
       are_you_sure: 你确定吗?
+      by_domain: 域名
       confirm: 确认
       confirmed: 已确认
-      disable_two_factor_authentication: 两步认证无效
-      display_name: 显示名称
+      demote: 降任
+      disable: 停用
+      disable_two_factor_authentication: 停用双重认证
+      disabled: 已停用
+      display_name: 昵称
       domain: 域名
       edit: 编辑
-      email: 电邮地址
+      email: 电子邮件地址
+      enable: 启用
+      enabled: 已启用
       feed_url: 订阅 URL
       followers: 关注者
+      followers_url: 关注者(Followers)URL
       follows: 正在关注
-      ip: IP地址
+      inbox_url: 收件箱(Inbox)URL
+      ip: IP 地址
       location:
         all: 全部
         local: 本地
         remote: 远程
-        title: 地点
+        title: 位置
+      login_status: 登录状态
       media_attachments: 媒体文件
+      memorialize: 设置为追悼帐户
       moderation:
         all: 全部
-        silenced: 被静音的
-        suspended: 被停权的
-        title: 管理操作
-      most_recent_activity: 最新活动
-      most_recent_ip: 最新 IP 地址
+        silenced: 已静音
+        suspended: 已封禁
+        title: 帐户状态
+      moderation_notes: 管理记录
+      most_recent_activity: 最后一次活跃的时间
+      most_recent_ip: 最后一次活跃的 IP 地址
       not_subscribed: 未订阅
       order:
         alphabetic: 按字母
         most_recent: 按时间
         title: 排序
-      perform_full_suspension: 实行完全暂停
-      profile_url: 个人文件 URL
-      public: 公共
-      push_subscription_expires: 推送订阅过期
+      outbox_url: 发件箱(Outbox)URL
+      perform_full_suspension: 永久封禁
+      profile_url: 个人资料页面 URL
+      promote: 升任
+      protocol: 协议
+      public: 公开页面
+      push_subscription_expires: PuSH 订阅过期时间
+      redownload: 刷新头像
       reset: 重置
       reset_password: 重置密码
-      salmon_url: Salmon 反馈 URL
+      resubscribe: 重新订阅
+      role: 用户组
+      roles:
+        admin: 管理员
+        moderator: 协管
+        user: 用户
+      salmon_url: Salmon URL
       search: 搜索
+      shared_inbox_url: 公用收件箱(Shared Inbox)URL
       show:
-        created_reports: 这个帐户创建的报告
-        report: 报告
-        targeted_reports: 关于这个帐户的报告
+        created_reports: 这个帐户提交的举报
+        report: 个举报
+        targeted_reports: 针对这个帐户的举报
       silence: 静音
       statuses: 嘟文
+      subscribe: 订阅
       title: 用户
       undo_silenced: 解除静音
-      undo_suspension: 解除停权
-      username: 用户名称
-      web: 用户页面
+      undo_suspension: 解除封禁
+      unsubscribe: 取消订阅
+      username: 用户名
+      web: 站内页面
+    custom_emojis:
+      copied_msg: 成功将表情复制到本地
+      copy: 复制
+      copy_failed_msg: 无法将表情复制到本地
+      created_msg: 表情添加成功!
+      delete: 删除
+      destroyed_msg: 表情删除成功!
+      disable: 停用
+      disabled_msg: 表情停用成功
+      emoji: 表情
+      enable: 启用
+      enabled_msg: 表情启用成功
+      image_hint: PNG 格式,最大 50KB
+      listed: 已显示
+      new:
+        title: 添加新的自定义表情
+      overwrite: 覆盖
+      shortcode: 短代码
+      shortcode_hint: 至少 2 个字符,只能使用字母、数字和下划线
+      title: 自定义表情
+      unlisted: 已隐藏
+      update_failed_msg: 表情更新失败!
+      updated_msg: 表情更新成功!
+      upload: 上传
     domain_blocks:
-      add_new: 添加
-      created_msg: 正处理域名阻隔
-      destroyed_msg: 已撤销域名阻隔
-      domain: 域名阻隔
+      add_new: 添加新条目
+      created_msg: 正在进行域名屏蔽
+      destroyed_msg: 域名屏蔽已撤销
+      domain: 域名
       new:
-        create: 添加域名阻隔
-        hint: "「域名阻隔」不会隔绝该域名用户的嘟帐户入本站数据库,但会嘟文抵达后,自动套用特定的审批操作。"
+        create: 添加域名屏蔽
+        hint: 域名屏蔽不会阻止该域名下的帐户进入本站的数据库,但是会对来自这个域名的帐户自动进行预先设置的管理操作。
         severity:
-          desc_html: "「<strong>自动静音</strong>」令该域名用户的嘟文,设为只对关注者显示,没有关注的人会看不到。 「<strong>自动除名</strong>」会自动将该域名用户的嘟文、媒体文件、个人资料从本服务器实例删除。"
+          desc_html: 选择<strong>自动静音</strong>会将该域名下帐户发送的嘟文设置为仅关注者可见;选择<strong>自动封禁</strong>会将该域名下帐户发送的嘟文、媒体文件以及个人资料数据从本实例上删除;如果你只是想拒绝接收来自该域名的任何媒体文件,请选择<strong>无</strong>。
+          noop: 无
           silence: 自动静音
-          suspend: 自动除名
-        title: 添加域名阻隔
-      reject_media: 拒绝媒体文件
-      reject_media_hint: 删除本地缓存的媒体文件,再也不在未来下载这个站点的文件。和自动除名无关。
+          suspend: 自动封禁
+        title: 添加域名屏蔽
+      reject_media: 拒绝接收媒体文件
+      reject_media_hint: 删除本地已缓存的媒体文件,并且不再接收来自该域名的任何媒体文件。此选项不影响封禁
       severities:
+        noop: 无
         silence: 自动静音
-        suspend: 自动除名
-      severity: 阻隔程度
+        suspend: 自动封禁
+      severity: 屏蔽级别
       show:
-        affected_accounts:
-          one: 数据库中有1个帐户受影响
-          other: 数据库中有%{count}个帐户受影响
+        affected_accounts: 将会影响到数据库中的 %{count} 个帐户
         retroactive:
           silence: 对此域名的所有帐户取消静音
-          suspend: 对此域名的所有帐户取消除名
-        title: 撤销 %{domain} 的域名阻隔
+          suspend: 对此域名的所有帐户取消封禁
+        title: 撤销对 %{domain} 的域名屏蔽
         undo: 撤销
-      title: 域名阻隔
+      title: 域名屏蔽
       undo: 撤销
+    email_domain_blocks:
+      add_new: 添加新条目
+      created_msg: 电子邮件域名屏蔽添加成功
+      delete: 删除
+      destroyed_msg: 电子邮件域名屏蔽删除成功
+      domain: 域名
+      new:
+        create: 添加屏蔽
+        title: 添加电子邮件域名屏蔽
+      title: 电子邮件域名屏蔽
     instances:
-      account_count: 已知帐号
+      account_count: 已知帐户
       domain_name: 域名
+      reset: 重置
+      search: 搜索
       title: 已知实例
     reports:
-      are_you_sure: 你确定吗?
+      action_taken_by: 操作执行者
+      are_you_sure: 你确定吗?
       comment:
         label: 备注
         none: 没有
       delete: 删除
       id: ID
-      mark_as_resolved: 标示为「已处理」
+      mark_as_resolved: 标记为“已处理”
       nsfw:
-        'false': NSFW无效
-        'true': NSFW有效
+        'false': 取消 NSFW 标记
+        'true': 添加 NSFW 标记
       report: '举报 #%{id}'
+      report_contents: 内容
       reported_account: 举报用户
-      reported_by: 举报者
+      reported_by: 举报人
       resolved: 已处理
-      silence_account: 将用户静音
+      silence_account: 静音用户
       status: 状态
-      suspend_account: 将用户停权
-      target: 对象
+      suspend_account: 封禁用户
+      target: 被举报人
       title: 举报
       unresolved: 未处理
       view: 查看
     settings:
+      bootstrap_timeline_accounts:
+        desc_html: 用半角逗号分隔多个用户名。只能添加来自本站且未开启保护的帐户。如果留空,则默认关注本站所有的管理员。
+        title: 新用户默认关注
       contact_information:
-        email: 输入一个公开的电邮地址
-        username: 输入用户名称
+        email: 输入一个公开的电子邮件地址
+        username: 输入用户名
       registrations:
         closed_message:
-          desc_html: 当本站暂停接受注册时,会显示这个消息。<br/> 可使用 HTML
-          title: 暂停注册消息
+          desc_html: 本站关闭注册期间的提示信息。可以使用 HTML 标签
+          title: 关闭注册时的提示消息
+        deletion:
+          desc_html: 允许所有人删除自己的帐户
+          title: 开放删除帐户权限
         open:
+          desc_html: 允许任何人建立一个帐户
           title: 开放注册
       site_description:
-        desc_html: 在首页显示,及在 meta 标签中用作网站介绍。<br>你可以在此使用 HTML 标签,尤其是<code>&lt;a&gt;</code> 和 <code>&lt;em&gt;</code>。
-        title: 本站介绍
+        desc_html: 展示在首页以及 meta 标签中的网站简介。可以使用 HTML 标签,包括 <code>&lt;a&gt;</code> 和 <code>&lt;em&gt;</code>。
+        title: 本站简介
       site_description_extended:
-        desc_html: 本站详细信息页的內容<br/>你可在此使用 HTML
-        title: 本站详细信息
+        desc_html: 可以填写行为守则、规定、指南或其他本站特有的内容。可以使用 HTML 标签
+        title: 本站详细介绍
+      site_terms:
+        desc_html: 可以填写自己的隐私权政策、使用条款或其他法律文本。可以使用 HTML 标签
+        title: 自定义使用条款
       site_title: 本站名称
+      thumbnail:
+        desc_html: 用于在 OpenGraph 和 API 中显示预览图。推荐分辨率 1200×630px
+        title: 本站缩略图
+      timeline_preview:
+        desc_html: 在主页显示公开时间线
+        title: 时间线预览
       title: 网站设置
+    statuses:
+      back_to_account: 返回帐户信息页
+      batch:
+        delete: 删除
+        nsfw_off: 取消 NSFW 标记
+        nsfw_on: 添加 NSFW 标记
+      execute: 执行
+      failed_to_execute: 执行失败
+      media:
+        hide: 隐藏媒体文件
+        show: 显示媒体文件
+        title: 媒体文件
+      no_media: 不含媒体文件
+      title: 帐户嘟文
+      with_media: 含有媒体文件
     subscriptions:
       callback_url: 回调 URL
-      confirmed: 确定
-      expires_in: 期限
-      last_delivery: 数据最后送抵时间
-      title: WebSub 订阅
-      topic: 所订阅资源
+      confirmed: 已确认
+      expires_in: 失效时间
+      last_delivery: 最后一次接收数据的时间
+      title: WebSub
+      topic: Topic
     title: 管理
+  admin_mailer:
+    new_report:
+      body: "%{reporter} 举报了 %{target}"
+      subject: 来自 %{instance} 的新举报(#%{id})
   application_mailer:
-    settings: 更改电邮设置︰%{link}
+    salutation: "%{name},"
+    settings: 更改电子邮件首选项:%{link}
     signature: 来自 %{instance} 的 Mastodon 通知
     view: 查看:
   applications:
+    created: 应用创建成功
+    destroyed: 应用删除成功
     invalid_url: URL 无效
+    regenerate_token: 重置访问令牌
+    token_regenerated: 访问令牌重置成功
+    warning: 一定小心,千万不要把它分享给任何人!
+    your_token: 你的访问令牌
   auth:
-    change_password: 登录凭据
+    agreement_html: 注册即表示你同意<a href="%{rules_path}">我们的使用条款</a>和<a href="%{terms_path}">隐私权政策</a>。
+    change_password: 帐户安全
+    delete_account: 删除帐户
+    delete_account_html: 如果你想删除你的帐户,请<a href="%{path}">点击这里继续</a>。你需要确认你的操作。
     didnt_get_confirmation: 没有收到确认邮件?
     forgot_password: 忘记密码?
+    invalid_reset_password_token: 密码重置令牌无效或已过期。请重新发起重置密码请求。
     login: 登录
     logout: 登出
     register: 注册
-    resend_confirmation: 重发确认邮件
+    resend_confirmation: 重新发送确认邮件
     reset_password: 重置密码
     set_new_password: 设置新密码
   authorize_follow:
     error: 对不起,寻找这个跨站用户时出错
     follow: 关注
+    follow_request: 关注请求已发送给:
+    following: 成功!你正在关注:
+    post_follow:
+      close: 你也可以直接关闭这个窗口。
+      return: 返回至个人资料页
+      web: 返回本站
     title: 关注 %{acct}
   datetime:
     distance_in_words:
-      about_x_hours: "%{count} 小时"
+      about_x_hours: "%{count} 时"
       about_x_months: "%{count} 个月"
       about_x_years: "%{count} 年"
-      almost_x_years: 接近 %{count} 年
+      almost_x_years: "%{count} 年"
       half_a_minute: 刚刚
-      less_than_x_minutes: "%{count} 分不到"
+      less_than_x_minutes: "%{count} 分"
       less_than_x_seconds: 刚刚
-      over_x_years: 超过 %{count} 年
+      over_x_years: "%{count} 年"
       x_days: "%{count} 天"
       x_minutes: "%{count} 分"
       x_months: "%{count} 个月"
       x_seconds: "%{count} 秒"
+  deletes:
+    bad_password_msg: 想得美,黑客!密码输入错误
+    confirm_password: 输入你当前的密码来验证身份
+    description_html: 继续操作将会<strong>永久地、不可撤销地</strong>删除你帐户中的内容,并冻结你的帐户。你的用户名将会被保留,以防有人冒用你的身份。
+    proceed: 删除帐户
+    success_msg: 你的帐户已经成功删除
+    warning_html: 我们只能保证本实例上的内容已经被彻底删除。对于已经被广泛传播的内容,它们在本实例以外的某些地方可能仍然可见。此外,失去连接的服务器以及停止接收订阅的服务器上的数据亦无法删除。
+    warning_title: 关于已传播的内容的警告
   errors:
     '403': 无权查看
     '404': 找不到页面
@@ -199,68 +353,71 @@ zh-CN:
       content: 无法确认登录信息。你是不是屏蔽了 Cookie?
       title: 无法确认登录信息
     '429': 被限制
+    '500':
+      content: 抱歉,我们这里出错了。
+      title: 这个页面不正确
+    noscript_html: 请启用 JavaScript 以便使用 Mastodon 网页版应用。你也可以选择适用于你的平台的 <a href="https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md">Mastodon 应用</a>。
   exports:
-    blocks: 被你封锁的用户
+    blocks: 屏蔽的用户
     csv: CSV
-    follows: 你所关注的用户
-    mutes: 你所静音的用户
-    storage: 媒体容量大小
+    follows: 关注的用户
+    mutes: 静音的用户
+    storage: 媒体文件存储
   followers:
     domain: 域名
-    explanation_html: 想要保护你的嘟文的话,请慎重考虑关注你的人。<strong>你的受保护的嘟文会发送到有你的关注者的所有实例上</strong>。你也许想要复查一下关注者列表来移除那些你无法信任的关注者。
+    explanation_html: 为保证你的嘟文的隐私安全,你应当时刻留意你的关注者列表。<strong>受保护的嘟文将会发送到所有关注者所在的实例上</strong>。有些实例使用的软件代码或其管理员可能不会尊重你的隐私设置,因此你应当复查一下关注者列表,并移除那些你无法信任的关注者。
     followers_count: 关注者数量
-    lock_link: 保护你的帐户
+    lock_link: 为你的帐户开启保护
     purge: 从关注者中移除
-    success: 从 %{count} 个域名中移除了关注者。
-    true_privacy_html: "<strong>真正的隐私只能靠端到端加密来实现</strong>!"
-    unlocked_warning_html: 任何人都可以关注你然后查看被保护的嘟文, %{lock_link} 可以复核和拒绝关注请求。
-    unlocked_warning_title: 你的帐户没被保护
+    success: 正在从 %{count} 个域名中移除关注者……
+    true_privacy_html: 请始终铭记:<strong>真正的隐私只能靠端到端加密来实现</strong>!
+    unlocked_warning_html: 任何人都可以通过关注你来立即查看被保护的嘟文。%{lock_link},即可审核并拒绝关注请求。
+    unlocked_warning_title: 你的帐户未受到保护
   generic:
-    changes_saved_msg: 更改已被保存。
+    changes_saved_msg: 更改保存成功!
     powered_by: 基于 %{link} 构建
-    save_changes: 保存
+    save_changes: 保存更改
     validation_errors:
-      one: 出错啦!请确认以下出错的地方,修改之后再来一次:
-      other: 出错啦!请确认以下 %{count} 处出错的地方,修改之后再来一次:
+      one: 出错啦!检查一下下面出错的地方吧
+      other: 出错啦!检查一下下面 %{count} 处出错的地方吧
   imports:
-    preface: 你可以在此导入你在其他服务器实例所导出的数据文件,包括︰你所关注、封锁的用户。
-    success: 你已成功上载数据文件,我们正将数据导入,请稍候
+    preface: 你可以在此导入你在其他实例导出的数据,比如你所关注或屏蔽的用户列表。
+    success: 数据上传成功,正在处理中
     types:
-      blocking: 封锁名单
-      following: 关注名单
-      muting: 静音名单
-    upload: 上载
-  landing_strip_html: "<strong>%{name}</strong> 是一个在 %{link_to_root_path} 的用户。只要你是象毛世界里(Mastodon、GNU social)任一服务器实例的用户,便可以跨站关注此站用户并与其沟通。"
-  landing_strip_signup_html: 如果你没有这类帐户,欢迎在<a href="%{sign_up_path}">此处登记</a>。
+      blocking: 屏蔽列表
+      following: 关注列表
+      muting: 静音列表
+    upload: 上传
+  in_memoriam_html: 谨此悼念。
+  landing_strip_html: "<strong>%{name}</strong> 是一位来自 %{link_to_root_path} 的用户。如果你想关注这个人或者与这个人互动,你需要在任意一个 Mastodon 实例或与其兼容的网站上拥有一个帐户。"
+  landing_strip_signup_html: 还没有这种帐户?你可以<a href="%{sign_up_path}">在本站注册一个</a>。
   media_attachments:
     validations:
-      images_and_video: 无法添加视频到一个已经包含图片的嘟文中
-      too_many: 最多只能添加4张图片
+      images_and_video: 无法在嘟文中同时插入视频和图片
+      too_many: 最多只能添加 4 张图片
   notification_mailer:
     digest:
-      body: 自从你在%{since}使用%{instance}以后,错过了这些嘟嘟滴滴:
-      mention: "%{name} 在此提及了你︰"
+      body: 自从你最后一次(时间是%{since})登录 %{instance} 以来,你错过了这些嘟嘟滴滴:
+      mention: "%{name} 在嘟文中提到了你:"
       new_followers_summary:
-        one: 有人关注你了!耶!
-        other: 有 %{count} 个人关注了你!别激动!
-      subject:
-        one: "你有一个新通知 \U0001F418"
-        other: "%{count} 个通知太多,赶快去看看 \U0001F418"
+        one: 有个人关注了你!耶!
+        other: 有 %{count} 个人关注了你!好棒!
+      subject: "自从你最后一次登录以来,你错过了 %{count} 条新通知 \U0001F418"
     favourite:
-      body: "%{name} 收藏了你"
-      subject: "%{name} 给你点了收藏"
+      body: 你的嘟文被 %{name} 收藏了:
+      subject: "%{name} 收藏了你的嘟文"
     follow:
-      body: "%{name} 关注了你"
+      body: "%{name} 关注了你!"
       subject: "%{name} 关注了你"
     follow_request:
-      body: "%{name} 要求关注你"
-      subject: 等候关注你的用户︰ %{name}
+      body: "%{name} 请求关注你"
+      subject: 待审核的关注者:%{name}
     mention:
-      body: "%{name} 在文章中提及你︰"
-      subject: "%{name} 在文章中提及你"
+      body: "%{name} 在嘟文中提到了你:"
+      subject: "%{name} 提到了你"
     reblog:
-      body: 你的嘟文得到 %{name} 的转嘟
-      subject: "%{name} 转嘟(嘟嘟滴)了你的嘟文"
+      body: 你的嘟文被 %{name} 转嘟了:
+      subject: "%{name} 转嘟了你的嘟文"
   number:
     human:
       decimal_units:
@@ -275,54 +432,197 @@ zh-CN:
   pagination:
     next: 下一页
     prev: 上一页
-    truncate: "……"
+    truncate: "…"
+  preferences:
+    languages: 语言
+    other: 其他
+    publishing: 发布
+    web: 站内
+  push_notifications:
+    favourite:
+      title: "%{name} 收藏了你的嘟文"
+    follow:
+      title: "%{name} 关注了你"
+    group:
+      title: "%{count} 条新通知"
+    mention:
+      action_boost: 转嘟
+      action_expand: 显示更多
+      action_favourite: 收藏
+      title: "%{name} 提到了你"
+    reblog:
+      title: "%{name} 转嘟了你的嘟文"
   remote_follow:
-    acct: 请输入你的︰用户名称@实例域名
-    missing_resource: 无法找到您的帐户转接网址
-    proceed: 下一步
-    prompt: 你正准备关注︰
+    acct: 请输入你的“用户名@实例域名”
+    missing_resource: 无法确定你的帐户的跳转 URL
+    proceed: 确认关注
+    prompt: 你正准备关注:
+  sessions:
+    activity: 最后一次活跃的时间
+    browser: 浏览器
+    browsers:
+      alipay: 支付宝
+      blackberry: Blackberry
+      chrome: Chrome
+      edge: Microsoft Edge
+      firefox: Firefox
+      generic: 未知浏览器
+      ie: Internet Explorer
+      micro_messenger: 微信
+      nokia: Nokia S40 Ovi 浏览器
+      opera: Opera
+      phantom_js: PhantomJS
+      qq: QQ浏览器
+      safari: Safari
+      uc_browser: UC浏览器
+      weibo: 新浪微博
+    current_session: 当前会话
+    description: "%{platform} 上的 %{browser}"
+    explanation: 你的 Mastodon 帐户目前已在这些浏览器上登录。
+    ip: IP 地址
+    platforms:
+      adobe_air: Adobe Air
+      android: Android
+      blackberry: Blackberry
+      chrome_os: ChromeOS
+      firefox_os: Firefox OS
+      ios: iOS
+      linux: Linux
+      mac: Mac
+      other: 未知平台
+      windows: Windows
+      windows_mobile: Windows Mobile
+      windows_phone: Windows Phone
+    revoke: 注销
+    revoke_success: 会话注销成功
+    title: 会话
   settings:
     authorized_apps: 已授权的应用
     back: 回到 Mastodon
+    delete: 删除帐户
+    development: 开发
     edit_profile: 更改个人信息
     export: 导出
     followers: 授权的关注者
     import: 导入
+    notifications: 通知
     preferences: 首选项
     settings: 设置
-    two_factor_authentication: 两步认证
+    two_factor_authentication: 双重认证
+    your_apps: 你的应用
   statuses:
-    open_in_web: 打开网页
+    open_in_web: 在站内打开
     over_character_limit: 超过了 %{max} 字的限制
+    pin_errors:
+      limit: 置顶的嘟文条数超出限制
+      ownership: 不能置顶他人的嘟文
+      private: 不能置顶非公开的嘟文
+      reblog: 不能置顶转嘟
     show_more: 显示更多
     visibilities:
-      private: 限关注者
-      private_long: 仅向关注者公开
+      private: 仅关注者
+      private_long: 只有关注你的用户能看到
       public: 公开
-      public_long: 向所有人公开
-      unlisted: 于公共时间线中隐藏
-      unlisted_long: 公开,但不显示在公共时间线中
+      public_long: 所有人可见,并会出现在公共时间轴上
+      unlisted: 不公开
+      unlisted_long: 所有人可见,但不会出现在公共时间轴上
   stream_entries:
-    click_to_show: 显示
+    click_to_show: 点击显示
+    pinned: 置顶嘟文
     reblogged: 转嘟
     sensitive_content: 敏感内容
+  terms:
+    body_html: |
+      <h2>隐私权政策</h2>
+
+      <h3 id="collect">我们收集什么信息?</h3>
+
+      <p>我们从你在我们站点注册开始从你那开始收集信息,并收集关于你在论坛的阅读和写作的数据,并评估分享的内容。</p>
+
+      <p>当在我们站点注册时,你可能被要求输入你的名字和邮件地址。然而你可以在不用注册的情况下访问站点。你的邮件地将通过一个独一无二的链接验证。如果链接被访问了,我们就知道你控制了该邮件地址。</p>
+
+      <p>当已注册和发帖时,我们记录发布帖子时的 IP 地址。我们也可能保留服务器日志,其中包括了每一个向我们服务器的请求。</p>
+
+      <h3 id="use">我们如何使用你的信息?</h3>
+
+      <p>从你那收集的任何数据将以以下方式使用:</p>
+
+      <ul>
+        <li>改进你的个人体验 &mdash; 你的信息帮助我们更好地满足你的个人需求。</li>
+        <li>改进我们的站点 &mdash; 我们基于信息和我们从你那收到的反馈不断地试图改进我们的站点。</li>
+        <li>改善我们的客户服务 &mdash; 你的信息帮助我们更有效地回应用户服务请求和支持。</li>
+        <li>用于发送阶段性的邮件 &mdash; 你提供的邮件地址可能用于接受信息、你想看到的通知或与你用户名有关的回复和询问,或是其他的请求和问题。</li>
+      </ul>
+
+      <h3 id="protect">我们如何保护你的信息?</h3>
+
+      <p>我们实现了一系列的安全措施保证你输入、提交或者访问你个人信息的数据安全。</p>
+
+      <h3 id="data-retention">数据保存政策是什么?</h3>
+
+      <p>我们将善意地:</p>
+
+      <ul>
+        <li>保存 90 天内的所有向服务器的包含 IP 地址的请求。</li>
+        <li>保存 5 年内已注册用户和与他们的帖子有关的 IP 地址。</li>
+      </ul>
+
+      <h3 id="cookies">我们使用 Cookie 吗?</h3>
+
+      <p>是的。Cookie 是网站或它的服务商通过网页浏览器存储在你电脑硬盘上的小文件(如果你同意)。这些 Cookie 使站点能分辨你的浏览器,并且,如果你注册了一个账户,与你的注册账户关联。</p>
+
+      <p>我们使用 Cookie 为之后的访问和编译一小段关于站点流量和交互的数据来判断并保存你的个人设置,这样我们可以在之后提供更好的站点体验和工具。我们可能使用第三方服务商来帮助我们更好地理解我们的站点访客。这些服务商是不允许代表我们使用收集的信息,除非是在帮助我们执行和改进我们的站点。</p>
+
+      <h3 id="disclose">我们会在站外提供任何信息吗?</h3>
+
+      <p>我们绝不销售、交易或任何向外转移你个人信息的行为。这不包括帮助我们管理站点、改进站点或给你提供服务的第三方团体,这些团体需要保证对这些信息保密。当我们认为提交你的信息符合法律、我们的站点政策或保护我们或其他人的权利、知识产权或安全时,我们也可能提交你的信息。然而,非个人访问信息可能供其他团体使用,用于市场、广告或其他用途。</p>
+
+      <h3 id="third-party">第三方链接</h3>
+
+      <p>偶尔地,根据我们的判断,我们可能在我们的站点上包括或提供第三方团体的产品或服务。这些第三方站点用于独立和不同的隐私政策。因此我们对链接到的站点或活动没有责任和权利。尽管如此,我们寻求保护我们的整个站点并且欢迎你给这些站点反馈。</p>
+
+      <h3 id="coppa">儿童在线隐私保护法案合规</h3>
+
+      <p>我们的站点、产品和服务提供给 13 岁以上的人们。如果服务器位于美国,并且你小于 13 岁,根据<a href="https://en.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act">儿童在线隐私保护法案合规</a>,不要使用这个站点。</p>
+
+      <h3 id="online">仅用于在线隐私政策</h3>
+
+      <p>这个线上隐私政策只适用于通过我们站点收集到的信息,并不包括线下收集的信息。</p>
+
+      <h3 id="consent">你的同意</h3>
+
+      <p>你使用站点的同时,代表你同意了我们网站的隐私政策。</p>
+
+      <h3 id="changes">隐私政策的更改</h3>
+
+      <p>如果我们决定更改我们的隐私政策,我们将在此页更新这些改变。</p>
+
+      <p>文档以 CC-BY-SA 发布。最后更新时间为2013年5月31日。</p>
+
+      <p>原文出自 <a href="https://github.com/discourse/discourse">Discourse 隐私权政策</a>。</p>
+    title: "%{instance} 使用条款和隐私权政策"
+  themes:
+    default: Mastodon
   time:
     formats:
       default: "%Y年%-m月%d日 %H:%M"
   two_factor_authentication:
-    code_hint: 请输入你认证器产生的代码,以确认设置
-    description_html: 当你启用<strong>两步认证</strong>后,你登录时将额外需要使用手机或其他认证器生成的代码。
+    code_hint: 输入你的认证器生成的代码以确认
+    description_html: 启用<strong>双重认证</strong>后,你需要输入手机认证器生成的代码才能登录
     disable: 停用
     enable: 启用
-    enabled_success: 已成功启用两步认证
+    enabled: 双重认证已启用
+    enabled_success: 双重认证启用成功
     generate_recovery_codes: 生成恢复代码
-    instructions_html: "<strong>请用你手机的认证器应用(如 Google Authenticator、Authy),扫描这里的 QR 二维码</strong>。在两步认证启用后,你登录时将需要使用此应用程序产生的认证码。"
-    lost_recovery_codes: 如果你丢了手机,你可以用恢复代码重新访问你的帐户。如果你丢了恢复代码,也可以在这里重新生成一个,不过以前的恢复代码就失效了。<del>(废话)</del>
-    manual_instructions: 如果你无法扫描 QR 二维码,请手动输入这个文本密码︰
-    recovery_codes_regenerated: 已成功重新生成恢复代码
-    recovery_instructions_html: 如果你的手机无法使用,你可以使用下面的任何恢复代码来恢复你的帐号。请保管好你的恢复代码以防泄漏(例如你可以打印好它们并和重要文档一起保存)。
+    instructions_html: "<strong>请使用 Google 身份验证器或其他 TOTP 双重认证手机应用扫描此处的二维码</strong>。启用双重认证后,你需要输入该应用生成的代码来登录你的帐户。"
+    lost_recovery_codes: 如果你的手机不慎丢失,你可以使用恢复代码来重新获得对帐户的访问权。如果你遗失了恢复代码,可以在此处重新生成。之前使用的恢复代码将会失效。
+    manual_instructions: 如果你无法扫描二维码,请手动输入下列文本:
+    recovery_codes: 备份恢复代码
+    recovery_codes_regenerated: 恢复代码重新生成成功
+    recovery_instructions_html: 如果你的手机无法使用,你可以使用下列任意一个恢复代码来重新获得对帐户的访问权。<strong>请妥善保管好你的恢复代码</strong>(例如,你可以将它们打印出来,然后和其他重要的文件放在一起)。
     setup: 设置
-    wrong_code: 你输入的认证码并不正确!可能服务器时间和你手机不一致,请检查你手机的时钟,或与本站管理员联系。
+    wrong_code: 输入的认证码无效!请检查你设备上显示的时间是否正确,如果正确,你可能需要联系管理员以检查服务器的时间是否正确。
   users:
-    invalid_email: 邮箱格式有误
-    invalid_otp_token: 两步认证码有误
+    invalid_email: 输入的电子邮件地址无效
+    invalid_otp_token: 输入的双重认证代码无效
+    signed_in_as: 当前登录的帐户:
diff --git a/config/navigation.rb b/config/navigation.rb
index 9fa029b72..16c48849b 100644
--- a/config/navigation.rb
+++ b/config/navigation.rb
@@ -21,16 +21,16 @@ SimpleNavigation::Configuration.run do |navigation|
       development.item :your_apps, safe_join([fa_icon('list fw'), t('settings.your_apps')]), settings_applications_url, highlights_on: %r{/settings/applications}
     end
 
-    primary.item :admin, safe_join([fa_icon('cogs fw'), t('admin.title')]), admin_reports_url, if: proc { current_user.admin? } do |admin|
+    primary.item :admin, safe_join([fa_icon('cogs fw'), t('admin.title')]), admin_reports_url, if: proc { current_user.staff? } do |admin|
       admin.item :reports, safe_join([fa_icon('flag fw'), t('admin.reports.title')]), admin_reports_url, highlights_on: %r{/admin/reports}
       admin.item :accounts, safe_join([fa_icon('users fw'), t('admin.accounts.title')]), admin_accounts_url, highlights_on: %r{/admin/accounts}
-      admin.item :instances, safe_join([fa_icon('cloud fw'), t('admin.instances.title')]), admin_instances_url, highlights_on: %r{/admin/instances}
-      admin.item :subscriptions, safe_join([fa_icon('paper-plane-o fw'), t('admin.subscriptions.title')]), admin_subscriptions_url
-      admin.item :domain_blocks, safe_join([fa_icon('lock fw'), t('admin.domain_blocks.title')]), admin_domain_blocks_url, highlights_on: %r{/admin/domain_blocks}
-      admin.item :email_domain_blocks, safe_join([fa_icon('envelope fw'), t('admin.email_domain_blocks.title')]), admin_email_domain_blocks_url, highlights_on: %r{/admin/email_domain_blocks}
-      admin.item :sidekiq, safe_join([fa_icon('diamond fw'), 'Sidekiq']), sidekiq_url, link_html: { target: 'sidekiq' }
-      admin.item :pghero, safe_join([fa_icon('database fw'), 'PgHero']), pghero_url, link_html: { target: 'pghero' }
-      admin.item :settings, safe_join([fa_icon('cogs fw'), t('admin.settings.title')]), edit_admin_settings_url
+      admin.item :instances, safe_join([fa_icon('cloud fw'), t('admin.instances.title')]), admin_instances_url, highlights_on: %r{/admin/instances}, if: -> { current_user.admin? }
+      admin.item :subscriptions, safe_join([fa_icon('paper-plane-o fw'), t('admin.subscriptions.title')]), admin_subscriptions_url, if: -> { current_user.admin? }
+      admin.item :domain_blocks, safe_join([fa_icon('lock fw'), t('admin.domain_blocks.title')]), admin_domain_blocks_url, highlights_on: %r{/admin/domain_blocks}, if: -> { current_user.admin? }
+      admin.item :email_domain_blocks, safe_join([fa_icon('envelope fw'), t('admin.email_domain_blocks.title')]), admin_email_domain_blocks_url, highlights_on: %r{/admin/email_domain_blocks}, if: -> { current_user.admin? }
+      admin.item :sidekiq, safe_join([fa_icon('diamond fw'), 'Sidekiq']), sidekiq_url, link_html: { target: 'sidekiq' }, if: -> { current_user.admin? }
+      admin.item :pghero, safe_join([fa_icon('database fw'), 'PgHero']), pghero_url, link_html: { target: 'pghero' }, if: -> { current_user.admin? }
+      admin.item :settings, safe_join([fa_icon('cogs fw'), t('admin.settings.title')]), edit_admin_settings_url, if: -> { current_user.admin? }
       admin.item :custom_emojis, safe_join([fa_icon('smile-o fw'), t('admin.custom_emojis.title')]), admin_custom_emojis_url, highlights_on: %r{/admin/custom_emojis}
     end
 
diff --git a/config/routes.rb b/config/routes.rb
index aca613ed2..78b96b0e4 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -133,7 +133,10 @@ Rails.application.routes.draw do
       member do
         post :subscribe
         post :unsubscribe
+        post :enable
+        post :disable
         post :redownload
+        post :memorialize
       end
 
       resource :reset, only: [:create]
@@ -141,6 +144,13 @@ Rails.application.routes.draw do
       resource :suspension, only: [:create, :destroy]
       resource :confirmation, only: [:create]
       resources :statuses, only: [:index, :create, :update, :destroy]
+
+      resource :role do
+        member do
+          post :promote
+          post :demote
+        end
+      end
     end
 
     resources :users, only: [] do
diff --git a/config/settings.yml b/config/settings.yml
index c03d0b766..1b8a6673e 100644
--- a/config/settings.yml
+++ b/config/settings.yml
@@ -36,6 +36,7 @@ defaults: &defaults
   interactions:
     must_be_follower: false
     must_be_following: false
+    must_be_following_dm: false
   reserved_usernames:
     - admin
     - support
diff --git a/config/webpack/production.js b/config/webpack/production.js
index cd1dd91dc..e2d7f11dc 100644
--- a/config/webpack/production.js
+++ b/config/webpack/production.js
@@ -9,6 +9,16 @@ const OfflinePlugin = require('offline-plugin');
 const { publicPath } = require('./configuration.js');
 const path = require('path');
 
+let compressionAlgorithm;
+try {
+  const zopfli = require('node-zopfli');
+  compressionAlgorithm = (content, options, fn) => {
+    zopfli.gzip(content, options, fn);
+  };
+} catch (error) {
+  compressionAlgorithm = 'gzip';
+}
+
 module.exports = merge(sharedConfig, {
   output: {
     filename: '[name]-[chunkhash].js',
@@ -33,7 +43,7 @@ module.exports = merge(sharedConfig, {
     }),
     new CompressionPlugin({
       asset: '[path].gz[query]',
-      algorithm: 'gzip',
+      algorithm: compressionAlgorithm,
       test: /\.(js|css|html|json|ico|svg|eot|otf|ttf)$/,
     }),
     new BundleAnalyzerPlugin({ // generates report.html and stats.json
@@ -48,7 +58,37 @@ module.exports = merge(sharedConfig, {
     }),
     new OfflinePlugin({
       publicPath: publicPath, // sw.js must be served from the root to avoid scope issues
-      caches: { }, // do not cache things, we only use it for push notifications for now
+      caches: {
+        main: [':rest:'],
+        additional: [':externals:'],
+        optional: [
+          '**/locale_*.js', // don't fetch every locale; the user only needs one
+          '**/*_polyfills-*.js', // the user may not need polyfills
+          '**/*.woff2', // the user may have system-fonts enabled
+          // images/audio can be cached on-demand
+          '**/*.png',
+          '**/*.jpg',
+          '**/*.jpeg',
+          '**/*.svg',
+          '**/*.mp3',
+          '**/*.ogg',
+        ],
+      },
+      externals: [
+        '/emoji/1f602.svg', // used for emoji picker dropdown
+        '/emoji/sheet.png', // used in emoji-mart
+      ],
+      excludes: [
+        '**/*.gz',
+        '**/*.map',
+        'stats.json',
+        'report.html',
+        // any browser that supports ServiceWorker will support woff2
+        '**/*.eot',
+        '**/*.ttf',
+        '**/*-webfont-*.svg',
+        '**/*.woff',
+      ],
       ServiceWorker: {
         entry: path.join(__dirname, '../../app/javascript/mastodon/service_worker/entry.js'),
         cacheName: 'mastodon',
diff --git a/config/webpack/shared.js b/config/webpack/shared.js
index e3a1fc379..5d176db4e 100644
--- a/config/webpack/shared.js
+++ b/config/webpack/shared.js
@@ -12,29 +12,27 @@ const localePackPaths = require('./generateLocalePacks');
 const extensionGlob = `**/*{${settings.extensions.join(',')}}*`;
 const entryPath = join(settings.source_path, settings.source_entry_path);
 const packPaths = sync(join(entryPath, extensionGlob));
-const entryPacks = [...packPaths, ...localePackPaths].filter(path => path !== join(entryPath, 'custom.js'));
-
-const themePaths = Object.keys(themes).reduce(
-  (themePaths, name) => {
-    const themeData = themes[name];
-    themePaths[`themes/${name}`] = resolve(themeData.pack_directory, themeData.pack);
-    return themePaths;
-  }, {}
-);
 
 module.exports = {
   entry: Object.assign(
-    entryPacks.reduce(
-      (map, entry) => {
-        const localMap = map;
-        let namespace = relative(join(entryPath), dirname(entry));
-        if (namespace === join('..', '..', '..', 'tmp', 'packs')) {
-          namespace = ''; // generated by generateLocalePacks.js
-        }
-        localMap[join(namespace, basename(entry, extname(entry)))] = resolve(entry);
-        return localMap;
+    packPaths.reduce((map, entry) => {
+      const localMap = map;
+      const namespace = relative(join(entryPath), dirname(entry));
+      localMap[join(namespace, basename(entry, extname(entry)))] = resolve(entry);
+      return localMap;
+    }, {}),
+    localePackPaths.reduce((map, entry) => {
+      const localMap = map;
+      localMap[basename(entry, extname(entry, extname(entry)))] = resolve(entry);
+      return localMap;
+    }, {}),
+    Object.keys(themes).reduce(
+      (themePaths, name) => {
+        const themeData = themes[name];
+        themePaths[`themes/${name}`] = resolve(themeData.pack_directory, themeData.pack);
+        return themePaths;
       }, {}
-    ), themePaths
+    )
   ),
 
   output: {
@@ -58,7 +56,7 @@ module.exports = {
       }
     ),
     new ExtractTextPlugin({
-      filename: env.NODE_ENV === 'production' ? '[name]-[hash].css' : '[name].css',
+      filename: env.NODE_ENV === 'production' ? '[name]-[contenthash].css' : '[name].css',
       allChunks: true,
     }),
     new ManifestPlugin({