about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
authorEugen Rochko <eugen@zeonfederated.com>2017-05-03 02:04:16 +0200
committerGitHub <noreply@github.com>2017-05-03 02:04:16 +0200
commitf5bf5ebb82e3af420dcd23d602b1be6cc86838e1 (patch)
tree92eef08642a038cf44ccbc6d16a884293e7a0814 /app
parent26bc5915727e0a0173c03cb49f5193dd612fb888 (diff)
Replace sprockets/browserify with Webpack (#2617)
* Replace browserify with webpack

* Add react-intl-translations-manager

* Do not minify in development, add offline-plugin for ServiceWorker background cache updates

* Adjust tests and dependencies

* Fix production deployments

* Fix tests

* More optimizations

* Improve travis cache for npm stuff

* Re-run travis

* Add back support for custom.scss as before

* Remove offline-plugin and babili

* Fix issue with Immutable.List().unshift(...values) not working as expected

* Make travis load schema instead of running all migrations in sequence

* Fix missing React import in WarningContainer. Optimize rendering performance by using ImmutablePureComponent instead of
React.PureComponent. ImmutablePureComponent uses Immutable.is() to compare props. Replace dynamic callback bindings in
<UI />

* Add react definitions to places that use JSX

* Add Procfile.dev for running rails, webpack and streaming API at the same time
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/application.js15
-rw-r--r--app/assets/javascripts/application_public.js9
-rw-r--r--app/assets/javascripts/components.js15
-rw-r--r--app/assets/javascripts/components/features/compose/components/autosuggest_account.jsx16
-rw-r--r--app/assets/javascripts/components/features/compose/components/autosuggest_status.jsx15
-rw-r--r--app/assets/javascripts/components/features/follow_requests/components/account_authorize.jsx44
-rw-r--r--app/assets/javascripts/components/features/getting_started/index.jsx66
-rw-r--r--app/assets/javascripts/components/locales/bg.jsx68
-rw-r--r--app/assets/javascripts/components/locales/eo.jsx68
-rw-r--r--app/assets/javascripts/components/locales/es.jsx93
-rw-r--r--app/assets/javascripts/components/locales/fi.jsx68
-rw-r--r--app/assets/javascripts/components/locales/hu.jsx57
-rw-r--r--app/assets/javascripts/components/locales/index.jsx57
-rw-r--r--app/assets/javascripts/components/locales/uk.jsx57
-rw-r--r--app/assets/stylesheets/.gitkeep (renamed from app/assets/javascripts/components/.gitkeep)0
-rw-r--r--app/assets/stylesheets/fonts/montserrat.scss11
-rw-r--r--app/assets/stylesheets/fonts/roboto-mono.scss12
-rw-r--r--app/assets/stylesheets/fonts/roboto.scss52
-rw-r--r--app/helpers/application_helper.rb6
-rw-r--r--app/javascript/fonts/montserrat/Montserrat-Regular.eot (renamed from app/assets/fonts/montserrat/Montserrat-Regular.eot)bin75826 -> 75826 bytes
-rw-r--r--app/javascript/fonts/montserrat/Montserrat-Regular.ttf (renamed from app/assets/fonts/montserrat/Montserrat-Regular.ttf)bin191860 -> 191860 bytes
-rw-r--r--app/javascript/fonts/montserrat/Montserrat-Regular.woff (renamed from app/assets/fonts/montserrat/Montserrat-Regular.woff)bin81244 -> 81244 bytes
-rw-r--r--app/javascript/fonts/montserrat/Montserrat-Regular.woff2 (renamed from app/assets/fonts/montserrat/Montserrat-Regular.woff2)bin61840 -> 61840 bytes
-rw-r--r--app/javascript/fonts/roboto-mono/robotomono-regular-webfont.eot (renamed from app/assets/fonts/roboto-mono/robotomono-regular-webfont.eot)bin57291 -> 57291 bytes
-rw-r--r--app/javascript/fonts/roboto-mono/robotomono-regular-webfont.svg (renamed from app/assets/fonts/roboto-mono/robotomono-regular-webfont.svg)0
-rw-r--r--app/javascript/fonts/roboto-mono/robotomono-regular-webfont.ttf (renamed from app/assets/fonts/roboto-mono/robotomono-regular-webfont.ttf)bin113716 -> 113716 bytes
-rw-r--r--app/javascript/fonts/roboto-mono/robotomono-regular-webfont.woff (renamed from app/assets/fonts/roboto-mono/robotomono-regular-webfont.woff)bin66384 -> 66384 bytes
-rw-r--r--app/javascript/fonts/roboto-mono/robotomono-regular-webfont.woff2 (renamed from app/assets/fonts/roboto-mono/robotomono-regular-webfont.woff2)bin51156 -> 51156 bytes
-rw-r--r--app/javascript/fonts/roboto/roboto-bold-webfont.eot (renamed from app/assets/fonts/roboto/roboto-bold-webfont.eot)bin229925 -> 229925 bytes
-rw-r--r--app/javascript/fonts/roboto/roboto-bold-webfont.svg (renamed from app/assets/fonts/roboto/roboto-bold-webfont.svg)0
-rw-r--r--app/javascript/fonts/roboto/roboto-bold-webfont.ttf (renamed from app/assets/fonts/roboto/roboto-bold-webfont.ttf)bin571400 -> 571400 bytes
-rw-r--r--app/javascript/fonts/roboto/roboto-bold-webfont.woff (renamed from app/assets/fonts/roboto/roboto-bold-webfont.woff)bin282780 -> 282780 bytes
-rw-r--r--app/javascript/fonts/roboto/roboto-bold-webfont.woff2 (renamed from app/assets/fonts/roboto/roboto-bold-webfont.woff2)bin192436 -> 192436 bytes
-rw-r--r--app/javascript/fonts/roboto/roboto-italic-webfont.eot (renamed from app/assets/fonts/roboto/roboto-italic-webfont.eot)bin255774 -> 255774 bytes
-rw-r--r--app/javascript/fonts/roboto/roboto-italic-webfont.svg (renamed from app/assets/fonts/roboto/roboto-italic-webfont.svg)0
-rw-r--r--app/javascript/fonts/roboto/roboto-italic-webfont.ttf (renamed from app/assets/fonts/roboto/roboto-italic-webfont.ttf)bin588464 -> 588464 bytes
-rw-r--r--app/javascript/fonts/roboto/roboto-italic-webfont.woff (renamed from app/assets/fonts/roboto/roboto-italic-webfont.woff)bin306528 -> 306528 bytes
-rw-r--r--app/javascript/fonts/roboto/roboto-italic-webfont.woff2 (renamed from app/assets/fonts/roboto/roboto-italic-webfont.woff2)bin215192 -> 215192 bytes
-rw-r--r--app/javascript/fonts/roboto/roboto-medium-webfont.eot (renamed from app/assets/fonts/roboto/roboto-medium-webfont.eot)bin228395 -> 228395 bytes
-rw-r--r--app/javascript/fonts/roboto/roboto-medium-webfont.svg (renamed from app/assets/fonts/roboto/roboto-medium-webfont.svg)0
-rw-r--r--app/javascript/fonts/roboto/roboto-medium-webfont.ttf (renamed from app/assets/fonts/roboto/roboto-medium-webfont.ttf)bin568816 -> 568816 bytes
-rw-r--r--app/javascript/fonts/roboto/roboto-medium-webfont.woff (renamed from app/assets/fonts/roboto/roboto-medium-webfont.woff)bin279900 -> 279900 bytes
-rw-r--r--app/javascript/fonts/roboto/roboto-medium-webfont.woff2 (renamed from app/assets/fonts/roboto/roboto-medium-webfont.woff2)bin190880 -> 190880 bytes
-rw-r--r--app/javascript/fonts/roboto/roboto-regular-webfont.eot (renamed from app/assets/fonts/roboto/roboto-regular-webfont.eot)bin228773 -> 228773 bytes
-rw-r--r--app/javascript/fonts/roboto/roboto-regular-webfont.svg (renamed from app/assets/fonts/roboto/roboto-regular-webfont.svg)0
-rw-r--r--app/javascript/fonts/roboto/roboto-regular-webfont.ttf (renamed from app/assets/fonts/roboto/roboto-regular-webfont.ttf)bin570352 -> 570352 bytes
-rw-r--r--app/javascript/fonts/roboto/roboto-regular-webfont.woff (renamed from app/assets/fonts/roboto/roboto-regular-webfont.woff)bin280372 -> 280372 bytes
-rw-r--r--app/javascript/fonts/roboto/roboto-regular-webfont.woff2 (renamed from app/assets/fonts/roboto/roboto-regular-webfont.woff2)bin191468 -> 191468 bytes
-rw-r--r--app/javascript/images/.keep (renamed from app/assets/images/.keep)0
-rw-r--r--app/javascript/images/background-photo.jpg (renamed from app/assets/images/background-photo.jpg)bin264344 -> 264344 bytes
-rw-r--r--app/javascript/images/boost_sprite.png (renamed from app/assets/images/boost_sprite.png)bin1326 -> 1326 bytes
-rw-r--r--app/javascript/images/elephant-friend.png (renamed from app/assets/images/elephant-friend.png)bin24466 -> 24466 bytes
-rw-r--r--app/javascript/images/fluffy-elephant-friend.png (renamed from app/assets/images/fluffy-elephant-friend.png)bin60667 -> 60667 bytes
-rw-r--r--app/javascript/images/logo.png (renamed from app/assets/images/logo.png)bin7752 -> 7752 bytes
-rw-r--r--app/javascript/images/logo.svg (renamed from app/assets/images/logo.svg)0
-rw-r--r--app/javascript/images/mastodon-getting-started.png (renamed from app/assets/images/mastodon-getting-started.png)bin34539 -> 34539 bytes
-rw-r--r--app/javascript/images/mastodon-not-found.png (renamed from app/assets/images/mastodon-not-found.png)bin19560 -> 19560 bytes
-rw-r--r--app/javascript/images/mastodon.jpg (renamed from app/assets/images/mastodon.jpg)bin133743 -> 133743 bytes
-rw-r--r--app/javascript/images/mastodon_small.jpg (renamed from app/assets/images/mastodon_small.jpg)bin25199 -> 25199 bytes
-rw-r--r--app/javascript/images/screenshot.png (renamed from app/assets/images/screenshot.png)bin249637 -> 249637 bytes
-rw-r--r--app/javascript/images/void.png (renamed from app/assets/images/void.png)bin174 -> 174 bytes
-rw-r--r--app/javascript/mastodon/.gitkeep0
-rw-r--r--app/javascript/mastodon/actions/accounts.js (renamed from app/assets/javascripts/components/actions/accounts.jsx)0
-rw-r--r--app/javascript/mastodon/actions/alerts.js (renamed from app/assets/javascripts/components/actions/alerts.jsx)0
-rw-r--r--app/javascript/mastodon/actions/blocks.js (renamed from app/assets/javascripts/components/actions/blocks.jsx)0
-rw-r--r--app/javascript/mastodon/actions/cards.js (renamed from app/assets/javascripts/components/actions/cards.jsx)0
-rw-r--r--app/javascript/mastodon/actions/compose.js (renamed from app/assets/javascripts/components/actions/compose.jsx)0
-rw-r--r--app/javascript/mastodon/actions/favourites.js (renamed from app/assets/javascripts/components/actions/favourites.jsx)0
-rw-r--r--app/javascript/mastodon/actions/interactions.js (renamed from app/assets/javascripts/components/actions/interactions.jsx)0
-rw-r--r--app/javascript/mastodon/actions/modal.js (renamed from app/assets/javascripts/components/actions/modal.jsx)0
-rw-r--r--app/javascript/mastodon/actions/mutes.js (renamed from app/assets/javascripts/components/actions/mutes.jsx)0
-rw-r--r--app/javascript/mastodon/actions/notifications.js (renamed from app/assets/javascripts/components/actions/notifications.jsx)0
-rw-r--r--app/javascript/mastodon/actions/onboarding.js (renamed from app/assets/javascripts/components/actions/onboarding.jsx)0
-rw-r--r--app/javascript/mastodon/actions/reports.js (renamed from app/assets/javascripts/components/actions/reports.jsx)0
-rw-r--r--app/javascript/mastodon/actions/search.js (renamed from app/assets/javascripts/components/actions/search.jsx)0
-rw-r--r--app/javascript/mastodon/actions/settings.js (renamed from app/assets/javascripts/components/actions/settings.jsx)0
-rw-r--r--app/javascript/mastodon/actions/statuses.js (renamed from app/assets/javascripts/components/actions/statuses.jsx)0
-rw-r--r--app/javascript/mastodon/actions/store.js (renamed from app/assets/javascripts/components/actions/store.jsx)0
-rw-r--r--app/javascript/mastodon/actions/timelines.js (renamed from app/assets/javascripts/components/actions/timelines.jsx)0
-rw-r--r--app/javascript/mastodon/api.js (renamed from app/assets/javascripts/components/api.jsx)0
-rw-r--r--app/javascript/mastodon/components/account.js (renamed from app/assets/javascripts/components/components/account.jsx)4
-rw-r--r--app/javascript/mastodon/components/attachment_list.js (renamed from app/assets/javascripts/components/components/attachment_list.jsx)1
-rw-r--r--app/javascript/mastodon/components/autosuggest_textarea.js (renamed from app/assets/javascripts/components/components/autosuggest_textarea.jsx)4
-rw-r--r--app/javascript/mastodon/components/avatar.js (renamed from app/assets/javascripts/components/components/avatar.jsx)5
-rw-r--r--app/javascript/mastodon/components/button.js (renamed from app/assets/javascripts/components/components/button.jsx)1
-rw-r--r--app/javascript/mastodon/components/collapsable.js (renamed from app/assets/javascripts/components/components/collapsable.jsx)1
-rw-r--r--app/javascript/mastodon/components/column_back_button.js (renamed from app/assets/javascripts/components/components/column_back_button.jsx)1
-rw-r--r--app/javascript/mastodon/components/column_back_button_slim.js (renamed from app/assets/javascripts/components/components/column_back_button_slim.jsx)1
-rw-r--r--app/javascript/mastodon/components/column_collapsable.js (renamed from app/assets/javascripts/components/components/column_collapsable.jsx)1
-rw-r--r--app/javascript/mastodon/components/display_name.js (renamed from app/assets/javascripts/components/components/display_name.jsx)1
-rw-r--r--app/javascript/mastodon/components/dropdown_menu.js (renamed from app/assets/javascripts/components/components/dropdown_menu.jsx)1
-rw-r--r--app/javascript/mastodon/components/extended_video_player.js (renamed from app/assets/javascripts/components/components/extended_video_player.jsx)1
-rw-r--r--app/javascript/mastodon/components/icon_button.js (renamed from app/assets/javascripts/components/components/icon_button.jsx)1
-rw-r--r--app/javascript/mastodon/components/load_more.js (renamed from app/assets/javascripts/components/components/load_more.jsx)1
-rw-r--r--app/javascript/mastodon/components/loading_indicator.js (renamed from app/assets/javascripts/components/components/loading_indicator.jsx)1
-rw-r--r--app/javascript/mastodon/components/media_gallery.js (renamed from app/assets/javascripts/components/components/media_gallery.jsx)1
-rw-r--r--app/javascript/mastodon/components/missing_indicator.js (renamed from app/assets/javascripts/components/components/missing_indicator.jsx)5
-rw-r--r--app/javascript/mastodon/components/permalink.js (renamed from app/assets/javascripts/components/components/permalink.jsx)9
-rw-r--r--app/javascript/mastodon/components/relative_timestamp.js (renamed from app/assets/javascripts/components/components/relative_timestamp.jsx)1
-rw-r--r--app/javascript/mastodon/components/status.js (renamed from app/assets/javascripts/components/components/status.jsx)4
-rw-r--r--app/javascript/mastodon/components/status_action_bar.js (renamed from app/assets/javascripts/components/components/status_action_bar.jsx)1
-rw-r--r--app/javascript/mastodon/components/status_content.js (renamed from app/assets/javascripts/components/components/status_content.jsx)12
-rw-r--r--app/javascript/mastodon/components/status_list.js (renamed from app/assets/javascripts/components/components/status_list.jsx)4
-rw-r--r--app/javascript/mastodon/components/video_player.js (renamed from app/assets/javascripts/components/components/video_player.jsx)16
-rw-r--r--app/javascript/mastodon/containers/account_container.js (renamed from app/assets/javascripts/components/containers/account_container.jsx)0
-rw-r--r--app/javascript/mastodon/containers/mastodon.js (renamed from app/assets/javascripts/components/containers/mastodon.jsx)10
-rw-r--r--app/javascript/mastodon/containers/status_container.js (renamed from app/assets/javascripts/components/containers/status_container.jsx)1
-rw-r--r--app/javascript/mastodon/emoji.js (renamed from app/assets/javascripts/components/emoji.jsx)0
-rw-r--r--app/javascript/mastodon/features/account/components/action_bar.js (renamed from app/assets/javascripts/components/features/account/components/action_bar.jsx)1
-rw-r--r--app/javascript/mastodon/features/account/components/header.js (renamed from app/assets/javascripts/components/features/account/components/header.jsx)6
-rw-r--r--app/javascript/mastodon/features/account_timeline/components/header.js (renamed from app/assets/javascripts/components/features/account_timeline/components/header.jsx)4
-rw-r--r--app/javascript/mastodon/features/account_timeline/containers/header_container.js (renamed from app/assets/javascripts/components/features/account_timeline/containers/header_container.jsx)1
-rw-r--r--app/javascript/mastodon/features/account_timeline/index.js (renamed from app/assets/javascripts/components/features/account_timeline/index.jsx)4
-rw-r--r--app/javascript/mastodon/features/blocks/index.js (renamed from app/assets/javascripts/components/features/blocks/index.jsx)4
-rw-r--r--app/javascript/mastodon/features/community_timeline/index.js (renamed from app/assets/javascripts/components/features/community_timeline/index.jsx)1
-rw-r--r--app/javascript/mastodon/features/compose/components/autosuggest_account.js26
-rw-r--r--app/javascript/mastodon/features/compose/components/character_counter.js (renamed from app/assets/javascripts/components/features/compose/components/character_counter.jsx)1
-rw-r--r--app/javascript/mastodon/features/compose/components/compose_form.js (renamed from app/assets/javascripts/components/features/compose/components/compose_form.jsx)4
-rw-r--r--app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js (renamed from app/assets/javascripts/components/features/compose/components/emoji_picker_dropdown.jsx)1
-rw-r--r--app/javascript/mastodon/features/compose/components/navigation_bar.js (renamed from app/assets/javascripts/components/features/compose/components/navigation_bar.jsx)9
-rw-r--r--app/javascript/mastodon/features/compose/components/privacy_dropdown.js (renamed from app/assets/javascripts/components/features/compose/components/privacy_dropdown.jsx)1
-rw-r--r--app/javascript/mastodon/features/compose/components/reply_indicator.js (renamed from app/assets/javascripts/components/features/compose/components/reply_indicator.jsx)4
-rw-r--r--app/javascript/mastodon/features/compose/components/search.js (renamed from app/assets/javascripts/components/features/compose/components/search.jsx)2
-rw-r--r--app/javascript/mastodon/features/compose/components/search_results.js (renamed from app/assets/javascripts/components/features/compose/components/search_results.jsx)4
-rw-r--r--app/javascript/mastodon/features/compose/components/text_icon_button.js (renamed from app/assets/javascripts/components/features/compose/components/text_icon_button.jsx)1
-rw-r--r--app/javascript/mastodon/features/compose/components/upload_button.js (renamed from app/assets/javascripts/components/features/compose/components/upload_button.jsx)1
-rw-r--r--app/javascript/mastodon/features/compose/components/upload_form.js (renamed from app/assets/javascripts/components/features/compose/components/upload_form.jsx)1
-rw-r--r--app/javascript/mastodon/features/compose/components/upload_progress.js (renamed from app/assets/javascripts/components/features/compose/components/upload_progress.jsx)1
-rw-r--r--app/javascript/mastodon/features/compose/components/warning.js (renamed from app/assets/javascripts/components/features/compose/components/warning.jsx)1
-rw-r--r--app/javascript/mastodon/features/compose/containers/autosuggest_account_container.js (renamed from app/assets/javascripts/components/features/compose/containers/autosuggest_account_container.jsx)0
-rw-r--r--app/javascript/mastodon/features/compose/containers/autosuggest_status_container.js (renamed from app/assets/javascripts/components/features/compose/containers/autosuggest_status_container.jsx)0
-rw-r--r--app/javascript/mastodon/features/compose/containers/compose_form_container.js (renamed from app/assets/javascripts/components/features/compose/containers/compose_form_container.jsx)0
-rw-r--r--app/javascript/mastodon/features/compose/containers/navigation_container.js (renamed from app/assets/javascripts/components/features/compose/containers/navigation_container.jsx)0
-rw-r--r--app/javascript/mastodon/features/compose/containers/privacy_dropdown_container.js (renamed from app/assets/javascripts/components/features/compose/containers/privacy_dropdown_container.jsx)0
-rw-r--r--app/javascript/mastodon/features/compose/containers/reply_indicator_container.js (renamed from app/assets/javascripts/components/features/compose/containers/reply_indicator_container.jsx)0
-rw-r--r--app/javascript/mastodon/features/compose/containers/search_container.js (renamed from app/assets/javascripts/components/features/compose/containers/search_container.jsx)0
-rw-r--r--app/javascript/mastodon/features/compose/containers/search_results_container.js (renamed from app/assets/javascripts/components/features/compose/containers/search_results_container.jsx)0
-rw-r--r--app/javascript/mastodon/features/compose/containers/sensitive_button_container.js (renamed from app/assets/javascripts/components/features/compose/containers/sensitive_button_container.jsx)1
-rw-r--r--app/javascript/mastodon/features/compose/containers/spoiler_button_container.js (renamed from app/assets/javascripts/components/features/compose/containers/spoiler_button_container.jsx)0
-rw-r--r--app/javascript/mastodon/features/compose/containers/upload_button_container.js (renamed from app/assets/javascripts/components/features/compose/containers/upload_button_container.jsx)0
-rw-r--r--app/javascript/mastodon/features/compose/containers/upload_form_container.js (renamed from app/assets/javascripts/components/features/compose/containers/upload_form_container.jsx)0
-rw-r--r--app/javascript/mastodon/features/compose/containers/upload_progress_container.js (renamed from app/assets/javascripts/components/features/compose/containers/upload_progress_container.jsx)0
-rw-r--r--app/javascript/mastodon/features/compose/containers/warning_container.js (renamed from app/assets/javascripts/components/features/compose/containers/warning_container.jsx)1
-rw-r--r--app/javascript/mastodon/features/compose/index.js (renamed from app/assets/javascripts/components/features/compose/index.jsx)1
-rw-r--r--app/javascript/mastodon/features/favourited_statuses/index.js (renamed from app/assets/javascripts/components/features/favourited_statuses/index.jsx)7
-rw-r--r--app/javascript/mastodon/features/favourites/index.js (renamed from app/assets/javascripts/components/features/favourites/index.jsx)4
-rw-r--r--app/javascript/mastodon/features/follow_requests/components/account_authorize.js51
-rw-r--r--app/javascript/mastodon/features/follow_requests/containers/account_authorize_container.js (renamed from app/assets/javascripts/components/features/follow_requests/containers/account_authorize_container.jsx)0
-rw-r--r--app/javascript/mastodon/features/follow_requests/index.js (renamed from app/assets/javascripts/components/features/follow_requests/index.jsx)4
-rw-r--r--app/javascript/mastodon/features/followers/index.js (renamed from app/assets/javascripts/components/features/followers/index.jsx)4
-rw-r--r--app/javascript/mastodon/features/following/index.js (renamed from app/assets/javascripts/components/features/following/index.jsx)4
-rw-r--r--app/javascript/mastodon/features/generic_not_found/index.js (renamed from app/assets/javascripts/components/features/generic_not_found/index.jsx)1
-rw-r--r--app/javascript/mastodon/features/getting_started/index.js73
-rw-r--r--app/javascript/mastodon/features/hashtag_timeline/index.js (renamed from app/assets/javascripts/components/features/hashtag_timeline/index.jsx)1
-rw-r--r--app/javascript/mastodon/features/home_timeline/components/column_settings.js (renamed from app/assets/javascripts/components/features/home_timeline/components/column_settings.jsx)1
-rw-r--r--app/javascript/mastodon/features/home_timeline/components/setting_text.js (renamed from app/assets/javascripts/components/features/home_timeline/components/setting_text.jsx)1
-rw-r--r--app/javascript/mastodon/features/home_timeline/containers/column_settings_container.js (renamed from app/assets/javascripts/components/features/home_timeline/containers/column_settings_container.jsx)0
-rw-r--r--app/javascript/mastodon/features/home_timeline/index.js (renamed from app/assets/javascripts/components/features/home_timeline/index.jsx)1
-rw-r--r--app/javascript/mastodon/features/mutes/index.js (renamed from app/assets/javascripts/components/features/mutes/index.jsx)4
-rw-r--r--app/javascript/mastodon/features/notifications/components/clear_column_button.js (renamed from app/assets/javascripts/components/features/notifications/components/clear_column_button.jsx)1
-rw-r--r--app/javascript/mastodon/features/notifications/components/column_settings.js (renamed from app/assets/javascripts/components/features/notifications/components/column_settings.jsx)1
-rw-r--r--app/javascript/mastodon/features/notifications/components/notification.js (renamed from app/assets/javascripts/components/features/notifications/components/notification.jsx)4
-rw-r--r--app/javascript/mastodon/features/notifications/components/setting_toggle.js (renamed from app/assets/javascripts/components/features/notifications/components/setting_toggle.jsx)1
-rw-r--r--app/javascript/mastodon/features/notifications/containers/column_settings_container.js (renamed from app/assets/javascripts/components/features/notifications/containers/column_settings_container.jsx)0
-rw-r--r--app/javascript/mastodon/features/notifications/containers/notification_container.js (renamed from app/assets/javascripts/components/features/notifications/containers/notification_container.jsx)0
-rw-r--r--app/javascript/mastodon/features/notifications/index.js (renamed from app/assets/javascripts/components/features/notifications/index.jsx)1
-rw-r--r--app/javascript/mastodon/features/public_timeline/index.js (renamed from app/assets/javascripts/components/features/public_timeline/index.jsx)1
-rw-r--r--app/javascript/mastodon/features/reblogs/index.js (renamed from app/assets/javascripts/components/features/reblogs/index.jsx)4
-rw-r--r--app/javascript/mastodon/features/report/components/status_check_box.js (renamed from app/assets/javascripts/components/features/report/components/status_check_box.jsx)1
-rw-r--r--app/javascript/mastodon/features/report/containers/status_check_box_container.js (renamed from app/assets/javascripts/components/features/report/containers/status_check_box_container.jsx)0
-rw-r--r--app/javascript/mastodon/features/report/index.js (renamed from app/assets/javascripts/components/features/report/index.jsx)1
-rw-r--r--app/javascript/mastodon/features/status/components/action_bar.js (renamed from app/assets/javascripts/components/features/status/components/action_bar.jsx)1
-rw-r--r--app/javascript/mastodon/features/status/components/card.js (renamed from app/assets/javascripts/components/features/status/components/card.jsx)1
-rw-r--r--app/javascript/mastodon/features/status/components/detailed_status.js (renamed from app/assets/javascripts/components/features/status/components/detailed_status.jsx)4
-rw-r--r--app/javascript/mastodon/features/status/containers/card_container.js (renamed from app/assets/javascripts/components/features/status/containers/card_container.jsx)0
-rw-r--r--app/javascript/mastodon/features/status/index.js (renamed from app/assets/javascripts/components/features/status/index.jsx)4
-rw-r--r--app/javascript/mastodon/features/ui/components/boost_modal.js (renamed from app/assets/javascripts/components/features/ui/components/boost_modal.jsx)4
-rw-r--r--app/javascript/mastodon/features/ui/components/column.js (renamed from app/assets/javascripts/components/features/ui/components/column.jsx)15
-rw-r--r--app/javascript/mastodon/features/ui/components/column_header.js (renamed from app/assets/javascripts/components/features/ui/components/column_header.jsx)1
-rw-r--r--app/javascript/mastodon/features/ui/components/column_link.js (renamed from app/assets/javascripts/components/features/ui/components/column_link.jsx)1
-rw-r--r--app/javascript/mastodon/features/ui/components/column_subheading.js (renamed from app/assets/javascripts/components/features/ui/components/column_subheading.jsx)13
-rw-r--r--app/javascript/mastodon/features/ui/components/columns_area.js (renamed from app/assets/javascripts/components/features/ui/components/columns_area.jsx)1
-rw-r--r--app/javascript/mastodon/features/ui/components/confirmation_modal.js (renamed from app/assets/javascripts/components/features/ui/components/confirmation_modal.jsx)1
-rw-r--r--app/javascript/mastodon/features/ui/components/media_modal.js (renamed from app/assets/javascripts/components/features/ui/components/media_modal.jsx)4
-rw-r--r--app/javascript/mastodon/features/ui/components/modal_root.js (renamed from app/assets/javascripts/components/features/ui/components/modal_root.jsx)1
-rw-r--r--app/javascript/mastodon/features/ui/components/onboarding_modal.js (renamed from app/assets/javascripts/components/features/ui/components/onboarding_modal.jsx)1
-rw-r--r--app/javascript/mastodon/features/ui/components/tabs_bar.js (renamed from app/assets/javascripts/components/features/ui/components/tabs_bar.jsx)1
-rw-r--r--app/javascript/mastodon/features/ui/components/upload_area.js (renamed from app/assets/javascripts/components/features/ui/components/upload_area.jsx)1
-rw-r--r--app/javascript/mastodon/features/ui/components/video_modal.js (renamed from app/assets/javascripts/components/features/ui/components/video_modal.jsx)4
-rw-r--r--app/javascript/mastodon/features/ui/containers/loading_bar_container.js (renamed from app/assets/javascripts/components/features/ui/containers/loading_bar_container.jsx)0
-rw-r--r--app/javascript/mastodon/features/ui/containers/modal_container.js (renamed from app/assets/javascripts/components/features/ui/containers/modal_container.jsx)0
-rw-r--r--app/javascript/mastodon/features/ui/containers/notifications_container.js (renamed from app/assets/javascripts/components/features/ui/containers/notifications_container.jsx)0
-rw-r--r--app/javascript/mastodon/features/ui/containers/status_list_container.js (renamed from app/assets/javascripts/components/features/ui/containers/status_list_container.jsx)0
-rw-r--r--app/javascript/mastodon/features/ui/index.js (renamed from app/assets/javascripts/components/features/ui/index.jsx)7
-rw-r--r--app/javascript/mastodon/is_mobile.js (renamed from app/assets/javascripts/components/is_mobile.jsx)0
-rw-r--r--app/javascript/mastodon/link_header.js (renamed from app/assets/javascripts/components/link_header.jsx)0
-rw-r--r--app/javascript/mastodon/locales/ar.json (renamed from app/assets/javascripts/components/locales/ar.jsx)95
-rw-r--r--app/javascript/mastodon/locales/bg.json163
-rw-r--r--app/javascript/mastodon/locales/de.json (renamed from app/assets/javascripts/components/locales/de.jsx)63
-rw-r--r--app/javascript/mastodon/locales/defaultMessages.json1068
-rw-r--r--app/javascript/mastodon/locales/en.json (renamed from app/assets/javascripts/components/locales/en.jsx)22
-rw-r--r--app/javascript/mastodon/locales/eo.json163
-rw-r--r--app/javascript/mastodon/locales/es.json163
-rw-r--r--app/javascript/mastodon/locales/fa.json (renamed from app/assets/javascripts/components/locales/fa.jsx)69
-rw-r--r--app/javascript/mastodon/locales/fi.json163
-rw-r--r--app/javascript/mastodon/locales/fr.json (renamed from app/assets/javascripts/components/locales/fr.jsx)60
-rw-r--r--app/javascript/mastodon/locales/he.json (renamed from app/assets/javascripts/components/locales/he.jsx)50
-rw-r--r--app/javascript/mastodon/locales/hr.json (renamed from app/assets/javascripts/components/locales/hr.jsx)66
-rw-r--r--app/javascript/mastodon/locales/hu.json163
-rw-r--r--app/javascript/mastodon/locales/id.json (renamed from app/assets/javascripts/components/locales/id.jsx)16
-rw-r--r--app/javascript/mastodon/locales/index.js57
-rw-r--r--app/javascript/mastodon/locales/io.json (renamed from app/assets/javascripts/components/locales/io.jsx)65
-rw-r--r--app/javascript/mastodon/locales/it.json (renamed from app/assets/javascripts/components/locales/it.jsx)66
-rw-r--r--app/javascript/mastodon/locales/ja.json (renamed from app/assets/javascripts/components/locales/ja.jsx)46
-rw-r--r--app/javascript/mastodon/locales/nl.json (renamed from app/assets/javascripts/components/locales/nl.jsx)69
-rw-r--r--app/javascript/mastodon/locales/no.json (renamed from app/assets/javascripts/components/locales/no.jsx)73
-rw-r--r--app/javascript/mastodon/locales/oc.json (renamed from app/assets/javascripts/components/locales/oc.jsx)231
-rw-r--r--app/javascript/mastodon/locales/pt-BR.json (renamed from app/assets/javascripts/components/locales/pt-br.jsx)66
-rw-r--r--app/javascript/mastodon/locales/pt.json (renamed from app/assets/javascripts/components/locales/pt.jsx)66
-rw-r--r--app/javascript/mastodon/locales/ru.json (renamed from app/assets/javascripts/components/locales/ru.jsx)39
-rw-r--r--app/javascript/mastodon/locales/uk.json163
-rw-r--r--app/javascript/mastodon/locales/whitelist_ar.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_bg.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_de.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_en.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_eo.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_es.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_fa.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_fi.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_fr.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_hr.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_hu.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_id.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_io.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_it.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_ja.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_nl.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_no.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_oc.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_pt-BR.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_pt.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_ru.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_uk.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_zh-CN.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_zh-HK.json2
-rw-r--r--app/javascript/mastodon/locales/zh-CN.json (renamed from app/assets/javascripts/components/locales/zh-cn.jsx)98
-rw-r--r--app/javascript/mastodon/locales/zh-HK.json (renamed from app/assets/javascripts/components/locales/zh-hk.jsx)91
-rw-r--r--app/javascript/mastodon/middleware/errors.js (renamed from app/assets/javascripts/components/middleware/errors.jsx)0
-rw-r--r--app/javascript/mastodon/middleware/loading_bar.js (renamed from app/assets/javascripts/components/middleware/loading_bar.jsx)0
-rw-r--r--app/javascript/mastodon/middleware/sounds.js (renamed from app/assets/javascripts/components/middleware/sounds.jsx)0
-rw-r--r--app/javascript/mastodon/reducers/accounts.js133
-rw-r--r--app/javascript/mastodon/reducers/accounts_counters.js (renamed from app/assets/javascripts/components/reducers/accounts.jsx)10
-rw-r--r--app/javascript/mastodon/reducers/alerts.js (renamed from app/assets/javascripts/components/reducers/alerts.jsx)0
-rw-r--r--app/javascript/mastodon/reducers/cards.js (renamed from app/assets/javascripts/components/reducers/cards.jsx)0
-rw-r--r--app/javascript/mastodon/reducers/compose.js (renamed from app/assets/javascripts/components/reducers/compose.jsx)0
-rw-r--r--app/javascript/mastodon/reducers/index.js (renamed from app/assets/javascripts/components/reducers/index.jsx)2
-rw-r--r--app/javascript/mastodon/reducers/meta.js (renamed from app/assets/javascripts/components/reducers/meta.jsx)0
-rw-r--r--app/javascript/mastodon/reducers/modal.js (renamed from app/assets/javascripts/components/reducers/modal.jsx)0
-rw-r--r--app/javascript/mastodon/reducers/notifications.js (renamed from app/assets/javascripts/components/reducers/notifications.jsx)4
-rw-r--r--app/javascript/mastodon/reducers/relationships.js (renamed from app/assets/javascripts/components/reducers/relationships.jsx)0
-rw-r--r--app/javascript/mastodon/reducers/reports.js (renamed from app/assets/javascripts/components/reducers/reports.jsx)0
-rw-r--r--app/javascript/mastodon/reducers/search.js (renamed from app/assets/javascripts/components/reducers/search.jsx)0
-rw-r--r--app/javascript/mastodon/reducers/settings.js (renamed from app/assets/javascripts/components/reducers/settings.jsx)4
-rw-r--r--app/javascript/mastodon/reducers/status_lists.js (renamed from app/assets/javascripts/components/reducers/status_lists.jsx)2
-rw-r--r--app/javascript/mastodon/reducers/statuses.js (renamed from app/assets/javascripts/components/reducers/statuses.jsx)0
-rw-r--r--app/javascript/mastodon/reducers/timelines.js (renamed from app/assets/javascripts/components/reducers/timelines.jsx)8
-rw-r--r--app/javascript/mastodon/reducers/user_lists.js (renamed from app/assets/javascripts/components/reducers/user_lists.jsx)8
-rw-r--r--app/javascript/mastodon/rtl.js (renamed from app/assets/javascripts/components/rtl.jsx)0
-rw-r--r--app/javascript/mastodon/selectors/index.js (renamed from app/assets/javascripts/components/selectors/index.jsx)5
-rw-r--r--app/javascript/mastodon/store/configureStore.js (renamed from app/assets/javascripts/components/store/configureStore.jsx)0
-rw-r--r--app/javascript/mastodon/stream.js (renamed from app/assets/javascripts/components/stream.jsx)0
-rw-r--r--app/javascript/mastodon/uuid.js (renamed from app/assets/javascripts/components/uuid.jsx)0
-rw-r--r--app/javascript/packs/application.js29
-rw-r--r--app/javascript/packs/public.js (renamed from app/assets/javascripts/extras.jsx)8
-rw-r--r--app/javascript/styles/about.scss (renamed from app/assets/stylesheets/about.scss)0
-rw-r--r--app/javascript/styles/accounts.scss (renamed from app/assets/stylesheets/accounts.scss)0
-rw-r--r--app/javascript/styles/admin.scss (renamed from app/assets/stylesheets/admin.scss)0
-rw-r--r--app/javascript/styles/application.scss (renamed from app/assets/stylesheets/application.scss)1
-rw-r--r--app/javascript/styles/basics.scss (renamed from app/assets/stylesheets/basics.scss)2
-rw-r--r--app/javascript/styles/boost.scss (renamed from app/assets/stylesheets/boost.scss)0
-rw-r--r--app/javascript/styles/compact_header.scss (renamed from app/assets/stylesheets/compact_header.scss)0
-rw-r--r--app/javascript/styles/components.scss (renamed from app/assets/stylesheets/components.scss)19
-rw-r--r--app/javascript/styles/containers.scss (renamed from app/assets/stylesheets/containers.scss)0
-rw-r--r--app/javascript/styles/fonts/montserrat.scss11
-rw-r--r--app/javascript/styles/fonts/roboto-mono.scss12
-rw-r--r--app/javascript/styles/fonts/roboto.scss52
-rw-r--r--app/javascript/styles/footer.scss (renamed from app/assets/stylesheets/footer.scss)0
-rw-r--r--app/javascript/styles/forms.scss (renamed from app/assets/stylesheets/forms.scss)0
-rw-r--r--app/javascript/styles/landing_strip.scss (renamed from app/assets/stylesheets/landing_strip.scss)0
-rw-r--r--app/javascript/styles/lists.scss (renamed from app/assets/stylesheets/lists.scss)0
-rw-r--r--app/javascript/styles/reset.scss (renamed from app/assets/stylesheets/reset.scss)0
-rw-r--r--app/javascript/styles/rtl.scss (renamed from app/assets/stylesheets/rtl.scss)0
-rw-r--r--app/javascript/styles/stream_entries.scss (renamed from app/assets/stylesheets/stream_entries.scss)0
-rw-r--r--app/javascript/styles/tables.scss (renamed from app/assets/stylesheets/tables.scss)0
-rwxr-xr-xapp/javascript/styles/variables.scss (renamed from app/assets/stylesheets/variables.scss)0
-rw-r--r--app/views/about/show.html.haml8
-rw-r--r--app/views/home/index.html.haml4
-rw-r--r--app/views/layouts/admin.html.haml4
-rwxr-xr-xapp/views/layouts/application.html.haml4
-rw-r--r--app/views/layouts/auth.html.haml4
-rw-r--r--app/views/layouts/embedded.html.haml2
-rw-r--r--app/views/layouts/public.html.haml2
302 files changed, 3693 insertions, 1282 deletions
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
deleted file mode 100644
index 441282825..000000000
--- a/app/assets/javascripts/application.js
+++ /dev/null
@@ -1,15 +0,0 @@
-// This is a manifest file that'll be compiled into application.js, which will include all the files
-// listed below.
-//
-// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
-// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
-//
-// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
-// compiled file.
-//
-// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
-// about supported directives.
-//
-//= require jquery2
-//= require jquery_ujs
-//= require components
diff --git a/app/assets/javascripts/application_public.js b/app/assets/javascripts/application_public.js
deleted file mode 100644
index 7df4891e7..000000000
--- a/app/assets/javascripts/application_public.js
+++ /dev/null
@@ -1,9 +0,0 @@
-//= require jquery2
-//= require jquery_ujs
-//= require extras
-//= require best_in_place
-//= require local_time
-
-$(function () {
-  $(".best_in_place").best_in_place();
-});
diff --git a/app/assets/javascripts/components.js b/app/assets/javascripts/components.js
deleted file mode 100644
index 1604d5198..000000000
--- a/app/assets/javascripts/components.js
+++ /dev/null
@@ -1,15 +0,0 @@
-//= require_self
-//= require react_ujs
-
-window.React    = require('react');
-window.ReactDOM = require('react-dom');
-window.Perf     = require('react-addons-perf');
-
-if (!window.Intl) {
-  require('intl');
-  require('intl/locale-data/jsonp/en.js');
-}
-
-//= require_tree ./components
-
-window.Mastodon = require('./components/containers/mastodon');
diff --git a/app/assets/javascripts/components/features/compose/components/autosuggest_account.jsx b/app/assets/javascripts/components/features/compose/components/autosuggest_account.jsx
deleted file mode 100644
index bf6a15e5d..000000000
--- a/app/assets/javascripts/components/features/compose/components/autosuggest_account.jsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import Avatar from '../../../components/avatar';
-import DisplayName from '../../../components/display_name';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-
-const AutosuggestAccount = ({ account }) => (
-  <div className='autosuggest-account'>
-    <div className='autosuggest-account-icon'><Avatar src={account.get('avatar')} staticSrc={account.get('avatar_static')} size={18} /></div>
-    <DisplayName account={account} />
-  </div>
-);
-
-AutosuggestAccount.propTypes = {
-  account: ImmutablePropTypes.map.isRequired
-};
-
-export default AutosuggestAccount;
diff --git a/app/assets/javascripts/components/features/compose/components/autosuggest_status.jsx b/app/assets/javascripts/components/features/compose/components/autosuggest_status.jsx
deleted file mode 100644
index 275b3d5a6..000000000
--- a/app/assets/javascripts/components/features/compose/components/autosuggest_status.jsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import { FormattedMessage } from 'react-intl';
-import DisplayName from '../../../components/display_name';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-
-const AutosuggestStatus = ({ status }) => (
-  <div className='autosuggest-status'>
-    <FormattedMessage id='search.status_by' defaultMessage='Status by {name}' values={{ name: <strong>@{status.getIn(['account', 'acct'])}</strong> }} />
-  </div>
-);
-
-AutosuggestStatus.propTypes = {
-  status: ImmutablePropTypes.map.isRequired
-};
-
-export default AutosuggestStatus;
diff --git a/app/assets/javascripts/components/features/follow_requests/components/account_authorize.jsx b/app/assets/javascripts/components/features/follow_requests/components/account_authorize.jsx
deleted file mode 100644
index d35a54c12..000000000
--- a/app/assets/javascripts/components/features/follow_requests/components/account_authorize.jsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import Permalink from '../../../components/permalink';
-import Avatar from '../../../components/avatar';
-import DisplayName from '../../../components/display_name';
-import emojify from '../../../emoji';
-import IconButton from '../../../components/icon_button';
-import { defineMessages, injectIntl } from 'react-intl';
-
-const messages = defineMessages({
-  authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' },
-  reject: { id: 'follow_request.reject', defaultMessage: 'Reject' }
-});
-
-const AccountAuthorize = ({ intl, account, onAuthorize, onReject }) => {
-  const content = { __html: emojify(account.get('note')) };
-
-  return (
-    <div className='account-authorize__wrapper'>
-      <div className='account-authorize'>
-        <Permalink href={account.get('url')} to={`/accounts/${account.get('id')}`} className='detailed-status__display-name'>
-          <div className='account-authorize__avatar'><Avatar src={account.get('avatar')} staticSrc={account.get('avatar_static')} size={48} /></div>
-          <DisplayName account={account} />
-        </Permalink>
-
-        <div className='account__header__content' dangerouslySetInnerHTML={content} />
-      </div>
-
-      <div className='account--panel'>
-        <div className='account--panel__button'><IconButton title={intl.formatMessage(messages.authorize)} icon='check' onClick={onAuthorize} /></div>
-        <div className='account--panel__button'><IconButton title={intl.formatMessage(messages.reject)} icon='times' onClick={onReject} /></div>
-      </div>
-    </div>
-  )
-};
-
-AccountAuthorize.propTypes = {
-  account: ImmutablePropTypes.map.isRequired,
-  onAuthorize: PropTypes.func.isRequired,
-  onReject: PropTypes.func.isRequired,
-  intl: PropTypes.object.isRequired
-};
-
-export default injectIntl(AccountAuthorize);
diff --git a/app/assets/javascripts/components/features/getting_started/index.jsx b/app/assets/javascripts/components/features/getting_started/index.jsx
deleted file mode 100644
index bd4920c94..000000000
--- a/app/assets/javascripts/components/features/getting_started/index.jsx
+++ /dev/null
@@ -1,66 +0,0 @@
-import Column from '../ui/components/column';
-import ColumnLink from '../ui/components/column_link';
-import ColumnSubheading from '../ui/components/column_subheading';
-import { Link } from 'react-router';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-
-const messages = defineMessages({
-  heading: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
-  public_timeline: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' },
-  navigation_subheading: { id: 'column_subheading.navigation', defaultMessage: 'Navigation'},
-  settings_subheading: { id: 'column_subheading.settings', defaultMessage: 'Settings'},
-  community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
-  preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
-  follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
-  sign_out: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
-  favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
-  blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
-  mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
-  info: { id: 'navigation_bar.info', defaultMessage: 'Extended information' }
-});
-
-const mapStateToProps = state => ({
-  me: state.getIn(['accounts', state.getIn(['meta', 'me'])])
-});
-
-const GettingStarted = ({ intl, me }) => {
-  let followRequests = '';
-
-  if (me.get('locked')) {
-    followRequests = <ColumnLink icon='users' text={intl.formatMessage(messages.follow_requests)} to='/follow_requests' />;
-  }
-
-  return (
-    <Column icon='asterisk' heading={intl.formatMessage(messages.heading)} hideHeadingOnMobile={true}>
-      <div className='getting-started__wrapper'>
-        <ColumnSubheading text={intl.formatMessage(messages.navigation_subheading)}/>
-        <ColumnLink icon='users' hideOnMobile={true} text={intl.formatMessage(messages.community_timeline)} to='/timelines/public/local' />
-        <ColumnLink icon='globe' hideOnMobile={true} text={intl.formatMessage(messages.public_timeline)} to='/timelines/public' />
-        <ColumnLink icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />
-        {followRequests}
-        <ColumnLink icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' />
-        <ColumnLink icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' />
-        <ColumnSubheading text={intl.formatMessage(messages.settings_subheading)}/>
-        <ColumnLink icon='book' text={intl.formatMessage(messages.info)} href='/about/more' />
-        <ColumnLink icon='cog' text={intl.formatMessage(messages.preferences)} href='/settings/preferences' />
-        <ColumnLink icon='sign-out' text={intl.formatMessage(messages.sign_out)} href='/auth/sign_out' method='delete' />
-      </div>
-
-      <div className='scrollable optionally-scrollable' style={{ display: 'flex', flexDirection: 'column' }}>
-        <div className='static-content getting-started'>
-          <p><FormattedMessage id='getting_started.open_source_notice' defaultMessage='Mastodon is open source software. You can contribute or report issues on GitHub at {github}. {apps}.' values={{ github: <a href="https://github.com/tootsuite/mastodon" target="_blank">tootsuite/mastodon</a>, apps: <a href="https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md" target="_blank"><FormattedMessage id='getting_started.apps' defaultMessage='Various apps are available' /></a> }} /></p>
-        </div>
-      </div>
-    </Column>
-  );
-};
-
-GettingStarted.propTypes = {
-  intl: PropTypes.object.isRequired,
-  me: ImmutablePropTypes.map.isRequired
-};
-
-export default connect(mapStateToProps)(injectIntl(GettingStarted));
diff --git a/app/assets/javascripts/components/locales/bg.jsx b/app/assets/javascripts/components/locales/bg.jsx
deleted file mode 100644
index a194cdbdd..000000000
--- a/app/assets/javascripts/components/locales/bg.jsx
+++ /dev/null
@@ -1,68 +0,0 @@
-const bg = {
-  "column_back_button.label": "Назад",
-  "lightbox.close": "Затвори",
-  "loading_indicator.label": "Зареждане...",
-  "status.mention": "Споменаване",
-  "status.delete": "Изтриване",
-  "status.reply": "Отговор",
-  "status.reblog": "Споделяне",
-  "status.favourite": "Предпочитани",
-  "status.reblogged_by": "{name} сподели",
-  "status.sensitive_warning": "Деликатно съдържание",
-  "status.sensitive_toggle": "Покажи",
-  "video_player.toggle_sound": "Звук",
-  "account.mention": "Споменаване",
-  "account.edit_profile": "Редактирай профила си",
-  "account.unblock": "Не блокирай",
-  "account.unfollow": "Не следвай",
-  "account.block": "Блокирай",
-  "account.follow": "Последвай",
-  "account.posts": "Публикации",
-  "account.follows": "Следвам",
-  "account.followers": "Последователи",
-  "account.follows_you": "Твой последовател",
-  "account.requested": "В очакване на одобрение",
-  "getting_started.heading": "Първи стъпки",
-  "getting_started.about_addressing": "Можеш да последваш потребител, ако знаеш потребителското му име и домейна, на който се намира, като в полето за търсене ги въведеш по този начин: име@домейн",
-  "getting_started.about_shortcuts": "Ако с търсения потребител се намирате на един и същ домейн, достатъчно е да въведеш само името. Същото важи и за споменаване на хора в публикации.",
-  "getting_started.about_developer": "Можеш да потърсиш разработчика на този проект като: Gargron@mastodon.social",
-  "getting_started.open_source_notice": "Mastodon е софтуер с отворен код. Можеш да помогнеш или да докладваш за проблеми в Github: {github}.",
-  "column.home": "Начало",
-  "column.mentions": "Споменавания",
-  "column.public": "Публичен канал",
-  "column.notifications": "Известия",
-  "tabs_bar.compose": "Съставяне",
-  "tabs_bar.home": "Начало",
-  "tabs_bar.mentions": "Споменавания",
-  "tabs_bar.public": "Публичен канал",
-  "tabs_bar.notifications": "Известия",
-  "compose_form.placeholder": "Какво си мислиш?",
-  "compose_form.publish": "Раздумай",
-  "compose_form.sensitive": "Отбележи съдържанието като деликатно",
-  "compose_form.spoiler": "Скрий текста зад предупреждение",
-  "compose_form.private": "Отбележи като поверително",
-  "compose_form.privacy_disclaimer": "Поверителни публикации ще бъдат изпратени до споменатите потребители на {domains}. Доверяваш ли се на {domainsCount, plural, one {that server} other {those servers}}, че няма да издаде твоята публикация?",
-  "compose_form.unlisted": "Не показвай в публичния канал",
-  "navigation_bar.edit_profile": "Редактирай профил",
-  "navigation_bar.preferences": "Предпочитания",
-  "navigation_bar.public_timeline": "Публичен канал",
-  "navigation_bar.logout": "Излизане",
-  "reply_indicator.cancel": "Отказ",
-  "search.placeholder": "Търсене",
-  "search.account": "Акаунт",
-  "search.hashtag": "Хаштаг",
-  "upload_button.label": "Добави медия",
-  "upload_form.undo": "Отмяна",
-  "notification.follow": "{name} те последва",
-  "notification.favourite": "{name} хареса твоята публикация",
-  "notification.reblog": "{name} сподели твоята публикация",
-  "notification.mention": "{name} те спомена",
-  "notifications.column_settings.alert": "Десктоп известия",
-  "notifications.column_settings.show": "Покажи в колона",
-  "notifications.column_settings.follow": "Нови последователи:",
-  "notifications.column_settings.favourite": "Предпочитани:",
-  "notifications.column_settings.mention": "Споменавания:",
-  "notifications.column_settings.reblog": "Споделяния:",
-};
-
-export default bg;
diff --git a/app/assets/javascripts/components/locales/eo.jsx b/app/assets/javascripts/components/locales/eo.jsx
deleted file mode 100644
index 8c118b31f..000000000
--- a/app/assets/javascripts/components/locales/eo.jsx
+++ /dev/null
@@ -1,68 +0,0 @@
-const eo = {
-  "column_back_button.label": "Reveni",
-  "lightbox.close": "Fermi",
-  "loading_indicator.label": "Ŝarĝanta...",
-  "status.mention": "Mencii @{name}",
-  "status.delete": "Forigi",
-  "status.reply": "Respondi",
-  "status.reblog": "Diskonigi",
-  "status.favourite": "Favori",
-  "status.reblogged_by": "{name} diskonigita",
-  "status.sensitive_warning": "Tikla enhavo",
-  "status.sensitive_toggle": "Alklaki por vidi",
-  "video_player.toggle_sound": "Aktivigi sonojn",
-  "account.mention": "Mencii @{name}",
-  "account.edit_profile": "Redakti la profilon",
-  "account.unblock": "Malbloki @{name}",
-  "account.unfollow": "Malsekvi",
-  "account.block": "Bloki @{name}",
-  "account.follow": "Sekvi",
-  "account.posts": "Mesaĝoj",
-  "account.follows": "Sekvatoj",
-  "account.followers": "Sekvantoj",
-  "account.follows_you": "Sekvas vin",
-  "account.requested": "Atendas aprobon",
-  "getting_started.heading": "Por komenci",
-  "getting_started.about_addressing": "Vi povas sekvi homojn se vi konas la uzantnomon kaj domajnon tajpinte retpoŝtecan adreson en la serĉilon.",
-  "getting_started.about_shortcuts": "Se la celita uzanto troviĝas en la sama domajno de vi, uzi nur la uzantnomon sufiĉos. La sama regulo validas por mencii aliajn uzantojn en mesaĝo.",
-  "getting_started.open_source_notice": "Mastodon estas malfermitkoda programo. Vi povas kontribui aŭ raporti problemojn en github je {github}. {apps}.",
-  "column.home": "Hejmo",
-  "column.community": "Loka tempolinio",
-  "column.public": "Fratara tempolinio",
-  "column.notifications": "Sciigoj",
-  "tabs_bar.compose": "Ekskribi",
-  "tabs_bar.home": "Hejmo",
-  "tabs_bar.mentions": "Sciigoj",
-  "tabs_bar.public": "Fratara tempolinio",
-  "tabs_bar.notifications": "Sciigoj",
-  "compose_form.placeholder": "Pri kio vi pensas?",
-  "compose_form.publish": "Hup",
-  "compose_form.sensitive": "Marki ke la enhavo estas tikla",
-  "compose_form.spoiler": "Kaŝi la tekston malantaŭ averto",
-  "compose_form.private": "Marki ke la enhavo estas privata",
-  "compose_form.privacy_disclaimer": "Via privata mesaĝo estos sendita nur al menciitaj uzantoj en {domains}. Ĉu vi fidas {domainsCount, plural, one {tiun servilon} other {tiujn servilojn}}? Mesaĝa privateco funkcias nur en aperaĵoj de Mastodon. Se {domains} {domainsCount, plural, one {ne estas aperaĵo de Mastodon} other {ne estas aperaĵoj de Mastodon}}, estos neniu indiko ke via mesaĝo estas privata, kaj ĝi povus esti diskonigita aŭ videbligita al necelitaj ricevantoj.",
-  "compose_form.unlisted": "Ne afiŝi en publikaj tempolinioj",
-  "navigation_bar.edit_profile": "Redakti la profilon",
-  "navigation_bar.preferences": "Preferoj",
-  "navigation_bar.community_timeline": "Loka tempolinio",
-  "navigation_bar.public_timeline": "Fratara tempolinio",
-  "navigation_bar.logout": "Elsaluti",
-  "reply_indicator.cancel": "Rezigni",
-  "search.placeholder": "Serĉi",
-  "search.account": "Konto",
-  "search.hashtag": "Kradvorto",
-  "upload_button.label": "Aldoni enhavaĵon",
-  "upload_form.undo": "Malfari",
-  "notification.follow": "{name} sekvis vin",
-  "notification.favourite": "{name} favoris vian mesaĝon",
-  "notification.reblog": "{name} diskonigis vian mesaĝon",
-  "notification.mention": "{name} menciis vin",
-  "notifications.column_settings.alert": "Retumilaj atentigoj",
-  "notifications.column_settings.show": "Montri en kolono",
-  "notifications.column_settings.follow": "Novaj sekvantoj:",
-  "notifications.column_settings.favourite": "Favoroj:",
-  "notifications.column_settings.mention": "Mencioj:",
-  "notifications.column_settings.reblog": "Diskonigoj:",
-};
-
-export default eo;
diff --git a/app/assets/javascripts/components/locales/es.jsx b/app/assets/javascripts/components/locales/es.jsx
deleted file mode 100644
index bbd6e07c1..000000000
--- a/app/assets/javascripts/components/locales/es.jsx
+++ /dev/null
@@ -1,93 +0,0 @@
-const es = {
-  "column_back_button.label": "Atrás",
-  "lightbox.close": "Cerrar",
-  "loading_indicator.label": "Cargando...",
-  "status.mention": "Mencionar",
-  "status.delete": "Borrar",
-  "status.reply": "Responder",
-  "status.reblog": "Retoot",
-  "status.favourite": "Favorito",
-  "status.reblogged_by": "Retooteado por {name}",
-  "status.sensitive_warning": "Contenido sensible",
-  "status.sensitive_toggle": "Click para ver",
-  "status.show_more": "Mostrar más",
-  "status.show_less": "Mostrar menos",
-  "status.open": "Expandir estado",
-  "status.report": "Reportar",
-  "video_player.toggle_sound": "Act/Desac. sonido",
-  "account.mention": "Mencionar",
-  "account.edit_profile": "Editar perfil",
-  "account.unblock": "Desbloquear",
-  "account.unfollow": "Dejar de seguir",
-  "account.mute": "Silenciar",
-  "account.block": "Bloquear",
-  "account.follow": "Seguir",
-  "account.posts": "Publicaciones",
-  "account.follows": "Seguir",
-  "account.followers": "Seguidores",
-  "account.follows_you": "Te sigue",
-  "account.requested": "Esperando aprobación",
-  "getting_started.heading": "Primeros pasos",
-  "getting_started.about_addressing": "Puedes seguir a gente si conoces su nombre de usuario y el dominio en el que están registrados, introduciendo algo similar a una dirección de correo electrónico en el formulario en la parte superior de la barra lateral.",
-  "getting_started.about_shortcuts": "Si el usuario que buscas está en el mismo dominio que tú, simplemente funcionará introduciendo el nombre de usuario. La misma regla se aplica para mencionar a usuarios.",
-  "getting_started.open_source_notice": "Mastodon es software libre. Puedes contribuir o reportar errores en {github}. {apps}.",
-  "column.home": "Inicio",
-  "column.community": "Historia local",
-  "column.public": "Historia federada",
-  "column.notifications": "Notificaciones",
-  "column.blocks": "Usuarios bloqueados",
-  "column.favourites": "Favoritos",
-  "column.follow_requests": "Solicitudes para seguirte",
-  "column.mutes": "Usuarios silenciados",
-  "tabs_bar.compose": "Redactar",
-  "tabs_bar.home": "Inicio",
-  "tabs_bar.mentions": "Menciones",
-  "tabs_bar.public": "Público",
-  "tabs_bar.notifications": "Notificaciones",
-  "compose_form.placeholder": "¿En qué estás pensando?",
-  "compose_form.publish": "Tootear",
-  "compose_form.sensitive": "Marcar contenido como sensible",
-  "compose_form.spoiler": "Ocultar texto tras advertencia",
-  "compose_form.spoiler_placeholder": "Advertencia de contenido",
-  "composer_form.private": "Marcar como privado",
-  "composer_form.privacy_disclaimer": "Tu estado se mostrará a los usuarios mencionados en {domains}. Tu estado podrá ser visto en otras instancias, quizás no quieras que tu estado sea visto por otros usuarios.",
-  "compose_form.unlisted": "No mostrar en la historia federada",
-  "navigation_bar.edit_profile": "Editar perfil",
-  "navigation_bar.preferences": "Preferencias",
-  "navigation_bar.community_timeline": "Historia local",
-  "navigation_bar.public_timeline": "Historia federada",
-  "navigation_bar.favourites": "Favoritos",
-  "navigation_bar.blocks": "Usuarios bloqueados",
-  "navigation_bar.info": "Información adicional",
-  "navigation_bar.logout": "Cerrar sesión",
-  "navigation_bar.follow_requests": "Solicitudes para seguirte",
-  "navigation_bar.mutes": "Usuarios silenciados",
-  "reply_indicator.cancel": "Cancelar",
-  "search.placeholder": "Buscar",
-  "search.account": "Cuenta",
-  "search.hashtag": "Etiqueta",
-  "upload_button.label": "Subir multimedia",
-  "upload_form.undo": "Deshacer",
-  "notification.follow": "{name} te empezó a seguir",
-  "notification.favourite": "{name} marcó tu estado como favorito",
-  "notification.reblog": "{name} ha retooteado tu estado",
-  "notification.mention": "{name} te ha mencionado",
-  "notifications.column_settings.alert": "Notificaciones de escritorio",
-  "notifications.column_settings.show": "Mostrar en columna",
-  "notifications.column_settings.follow": "Nuevos seguidores:",
-  "notifications.column_settings.favourite": "Favoritos:",
-  "notifications.column_settings.mention": "Menciones:",
-  "notifications.column_settings.reblog": "Retoots:",
-  "emoji_button.label": "Insertar emoji",
-  "privacy.public.short": "Público",
-  "privacy.public.long": "Mostrar en la historia federada",
-  "privacy.unlisted.short": "Sin federar",
-  "privacy.unlisted.long": "No mostrar en la historia federada",
-  "privacy.private.short": "Privado",
-  "privacy.private.long": "Sólo mostrar a seguidores",
-  "privacy.direct.short": "Directo",
-  "privacy.direct.long": "Sólo mostrar a los usuarios mencionados",
-  "privacy.change": "Ajustar privacidad"
-};
-
-export default es;
diff --git a/app/assets/javascripts/components/locales/fi.jsx b/app/assets/javascripts/components/locales/fi.jsx
deleted file mode 100644
index b3ae4bc56..000000000
--- a/app/assets/javascripts/components/locales/fi.jsx
+++ /dev/null
@@ -1,68 +0,0 @@
-const fi = {
-  "column_back_button.label": "Takaisin",
-  "lightbox.close": "Sulje",
-  "loading_indicator.label": "Ladataan...",
-  "status.mention": "Mainitse @{name}",
-  "status.delete": "Poista",
-  "status.reply": "Vastaa",
-  "status.reblog": "Buustaa",
-  "status.favourite": "Tykkää",
-  "status.reblogged_by": "{name} buustasi",
-  "status.sensitive_warning": "Arkaluontoista sisältöä",
-  "status.sensitive_toggle": "Klikkaa nähdäksesi",
-  "video_player.toggle_sound": "Äänet päälle/pois",
-  "account.mention": "Mainitse @{name}",
-  "account.edit_profile": "Muokkaa",
-  "account.unblock": "Salli @{name}",
-  "account.unfollow": "Lopeta seuraaminen",
-  "account.block": "Estä @{name}",
-  "account.follow": "Seuraa",
-  "account.posts": "Postit",
-  "account.follows": "Seuraa",
-  "account.followers": "Seuraajia",
-  "account.follows_you": "Seuraa sinua",
-  "account.requested": "Odottaa hyväksyntää",
-  "getting_started.heading": "Aloitus",
-  "getting_started.about_addressing": "Voit seurata ihmisiä jos tiedät heidän käyttäjänimensä ja domainin missä he ovat syöttämällä e-mail-esque osoitteen Etsi kenttään.",
-  "getting_started.about_shortcuts": "Jos etsimäsi henkilö on samassa domainissa kuin sinä, pelkkä käyttäjänimi kelpaa. Sama pätee kun mainitset ihmisiä statuksessasi",
-  "getting_started.open_source_notice": "Mastodon Mastodon on avoimen lähdekoodin ohjelma. Voit avustaa tai raportoida ongelmia GitHub palvelussa {github}. {apps}.",
-  "column.home": "Koti",
-  "column.community": "Paikallinen aikajana",
-  "column.public": "Yleinen aikajana",
-  "column.notifications": "Ilmoitukset",
-  "tabs_bar.compose": "Luo",
-  "tabs_bar.home": "Koti",
-  "tabs_bar.mentions": "Maininnat",
-  "tabs_bar.public": "Yleinen aikajana",
-  "tabs_bar.notifications": "Ilmoitukset",
-  "compose_form.placeholder": "Mitä sinulla on mielessä?",
-  "compose_form.publish": "Toot",
-  "compose_form.sensitive": "Merkitse media herkäksi",
-  "compose_form.spoiler": "Piiloita teksti varoituksen taakse",
-  "compose_form.private": "Merkitse yksityiseksi",
-  "compose_form.privacy_disclaimer": "Sinun yksityinen status toimitetaan mainitsemallesi käyttäjille domaineissa {domains}. Luotatko {domainsCount, plural, one {tähän palvelimeen} other {näihin palvelimiin}}? Postauksen yksityisyys toimii van Mastodon palvelimilla. Jos {domains} {domainsCount, plural, one {ei ole Mastodon palvelin} other {eivät ole Mastodon palvelin}}, viestiin ei tule Yksityinen-merkintää, ja sitä voidaan boostata tai muuten tehdä näkyväksi muille vastaanottajille.",
-  "compose_form.unlisted": "Älä näytä yleisillä aikajanoilla",
-  "navigation_bar.edit_profile": "Muokkaa profiilia",
-  "navigation_bar.preferences": "Ominaisuudet",
-  "navigation_bar.community_timeline": "Paikallinen aikajana",
-  "navigation_bar.public_timeline": "Yleinen aikajana",
-  "navigation_bar.logout": "Kirjaudu ulos",
-  "reply_indicator.cancel": "Peruuta",
-  "search.placeholder": "Hae",
-  "search.account": "Tili",
-  "search.hashtag": "Hashtag",
-  "upload_button.label": "Lisää mediaa",
-  "upload_form.undo": "Peru",
-  "notification.follow": "{name} seurasi sinua",
-  "notification.favourite": "{name} tykkäsi statuksestasi",
-  "notification.reblog": "{name} buustasi statustasi",
-  "notification.mention": "{name} mainitsi sinut",
-  "notifications.column_settings.alert": "Työpöytä ilmoitukset",
-  "notifications.column_settings.show": "Näytä sarakkeessa",
-  "notifications.column_settings.follow": "Uusia seuraajia:",
-  "notifications.column_settings.favourite": "Tykkäyksiä:",
-  "notifications.column_settings.mention": "Mainintoja:",
-  "notifications.column_settings.reblog": "Buusteja:",
-};
-
-export default fi;
diff --git a/app/assets/javascripts/components/locales/hu.jsx b/app/assets/javascripts/components/locales/hu.jsx
deleted file mode 100644
index b68df66fd..000000000
--- a/app/assets/javascripts/components/locales/hu.jsx
+++ /dev/null
@@ -1,57 +0,0 @@
-const hu = {
-  "column_back_button.label": "Vissza",
-  "lightbox.close": "Bezárás",
-  "loading_indicator.label": "Betöltés...",
-  "status.mention": "Említés",
-  "status.delete": "Törlés",
-  "status.reply": "Válasz",
-  "status.reblog": "Reblog",
-  "status.favourite": "Kedvenc",
-  "status.reblogged_by": "{name} reblogolta",
-  "status.sensitive_warning": "Érzékeny tartalom",
-  "status.sensitive_toggle": "Katt a megtekintéshez",
-  "video_player.toggle_sound": "Hang kapcsolása",
-  "account.mention": "Említés",
-  "account.edit_profile": "Profil szerkesztése",
-  "account.unblock": "Blokkolás levétele",
-  "account.unfollow": "Követés abbahagyása",
-  "account.block": "Blokkolás",
-  "account.follow": "Követés",
-  "account.posts": "Posts",
-  "account.follows": "Követve",
-  "account.followers": "Követők",
-  "account.follows_you": "Követnek téged",
-  "getting_started.heading": "Első lépések",
-  "getting_started.about_addressing": "Követhetsz embereket felhasználónevük és a doménjük ismeretében, amennyiben megadod ezt az e-mail-szerű címet az oldalsáv tetején lévő rubrikában.",
-  "getting_started.about_shortcuts": "Ha a célzott személy azonos doménen tartózkodik, a felhasználónév elegendő. Ugyanez érvényes mikor személyeket említesz az állapotokban.",
-  "getting_started.about_developer": "A projekt fejlesztője követhető, mint Gargron@mastodon.social",
-  "column.home": "Kezdőlap",
-  "column.mentions": "Említések",
-  "column.public": "Nyilvános",
-  "column.notifications": "Értesítések",
-  "tabs_bar.compose": "Összeállítás",
-  "tabs_bar.home": "Kezdőlap",
-  "tabs_bar.mentions": "Említések",
-  "tabs_bar.public": "Nyilvános",
-  "tabs_bar.notifications": "Notifications",
-  "compose_form.placeholder": "Mire gondolsz?",
-  "compose_form.publish": "Tülk!",
-  "compose_form.sensitive": "Tartalom érzékenynek jelölése",
-  "compose_form.unlisted": "Listázatlan mód",
-  "navigation_bar.edit_profile": "Profil szerkesztése",
-  "navigation_bar.preferences": "Beállítások",
-  "navigation_bar.public_timeline": "Nyilvános időfolyam",
-  "navigation_bar.logout": "Kijelentkezés",
-  "reply_indicator.cancel": "Mégsem",
-  "search.placeholder": "Keresés",
-  "search.account": "Fiók",
-  "search.hashtag": "Hashtag",
-  "upload_button.label": "Média hozzáadása",
-  "upload_form.undo": "Mégsem",
-  "notification.follow": "{name} követ téged",
-  "notification.favourite": "{name} kedvencnek jelölte az állapotod",
-  "notification.reblog": "{name} reblogolta az állapotod",
-  "notification.mention": "{name} megemlített"
-};
-
-export default hu;
diff --git a/app/assets/javascripts/components/locales/index.jsx b/app/assets/javascripts/components/locales/index.jsx
deleted file mode 100644
index 0c8472401..000000000
--- a/app/assets/javascripts/components/locales/index.jsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import ar from './ar';
-import en from './en';
-import de from './de';
-import es from './es';
-import fa from './fa';
-import he from './he';
-import hr from './hr';
-import hu from './hu';
-import io from './io';
-import it from './it';
-import fr from './fr';
-import nl from './nl';
-import no from './no';
-import oc from './oc';
-import pt from './pt';
-import pt_br from './pt-br';
-import uk from './uk';
-import fi from './fi';
-import eo from './eo';
-import ru from './ru';
-import ja from './ja';
-import zh_hk from './zh-hk';
-import zh_cn from './zh-cn';
-import bg from './bg';
-import id from './id';
-
-const locales = {
-  ar,
-  en,
-  de,
-  es,
-  fa,
-  he,
-  hr,
-  hu,
-  io,
-  it,
-  fr,
-  nl,
-  no,
-  oc,
-  pt,
-  'pt-BR': pt_br,
-  uk,
-  fi,
-  eo,
-  ru,
-  ja,
-  'zh-HK': zh_hk,
-  'zh-CN': zh_cn,
-  bg,
-  id,
-};
-
-export default function getMessagesForLocale (locale) {
-  return locales[locale];
-};
diff --git a/app/assets/javascripts/components/locales/uk.jsx b/app/assets/javascripts/components/locales/uk.jsx
deleted file mode 100644
index 84a348c21..000000000
--- a/app/assets/javascripts/components/locales/uk.jsx
+++ /dev/null
@@ -1,57 +0,0 @@
-const uk = {
-  "column_back_button.label": "Назад",
-  "lightbox.close": "Закрити",
-  "loading_indicator.label": "Завантаження...",
-  "status.mention": "Згадати",
-  "status.delete": "Видалити",
-  "status.reply": "Відповісти",
-  "status.reblog": "Передмухнути",
-  "status.favourite": "Подобається",
-  "status.reblogged_by": "{name} передмухнув(-ла)",
-  "status.sensitive_warning": "Непристойний зміст",
-  "status.sensitive_toggle": "Натисніть, щоб подивитися",
-  "video_player.toggle_sound": "Увімкнути/вимкнути звук",
-  "account.mention": "Згадати",
-  "account.edit_profile": "Налаштування профілю",
-  "account.unblock": "Розблокувати",
-  "account.unfollow": "Відписатися",
-  "account.block": "Заблокувати",
-  "account.follow": "Підписатися",
-  "account.posts": "Пости",
-  "account.follows": "Підписки",
-  "account.followers": "Підписники",
-  "account.follows_you": "Підписаний",
-  "getting_started.heading": "Ласкаво просимо",
-  "getting_started.about_addressing": "Ви можете підписуватись на людей, якщо ви знаєте їх ім'я користувача чи домен, шляхом введення email-подібної адреси у верхньому рядку бокової панелі.",
-  "getting_started.about_shortcuts": "Якщо користувач, якого ви шукаєте, знаходиться на тому ж домені, що й ви, можна просто ввести ім'я користувача. Це правило стосується й згадування людей у статусах.",
-  "getting_started.about_developer": "Розробник проекту знаходиться за адресою Gargron@mastodon.social",
-  "column.home": "Головна",
-  "column.mentions": "Згадування",
-  "column.public": "Стіна",
-  "column.notifications": "Сповіщення",
-  "tabs_bar.compose": "Написати",
-  "tabs_bar.home": "Головна",
-  "tabs_bar.mentions": "Згадування",
-  "tabs_bar.public": "Стіна",
-  "tabs_bar.notifications": "Сповіщення",
-  "compose_form.placeholder": "Що у Вас на думці?",
-  "compose_form.publish": "Дмухнути",
-  "compose_form.sensitive": "Непристойний зміст",
-  "compose_form.unlisted": "Таємний режим",
-  "navigation_bar.edit_profile": "Редагувати профіль",
-  "navigation_bar.preferences": "Налаштування",
-  "navigation_bar.public_timeline": "Публічна стіна",
-  "navigation_bar.logout": "Вийти",
-  "reply_indicator.cancel": "Відмінити",
-  "search.placeholder": "Пошук",
-  "search.account": "Аккаунт",
-  "search.hashtag": "Хештеґ",
-  "upload_button.label": "Додати медіа",
-  "upload_form.undo": "Відмінити",
-  "notification.follow": "{name} підписався(-лась) на Вас",
-  "notification.favourite": "{name} сподобався ваш допис",
-  "notification.reblog": "{name} передмухнув(-ла) Ваш статус",
-  "notification.mention": "{name} згадав(-ла) Вас"
-};
-
-export default uk;
diff --git a/app/assets/javascripts/components/.gitkeep b/app/assets/stylesheets/.gitkeep
index e69de29bb..e69de29bb 100644
--- a/app/assets/javascripts/components/.gitkeep
+++ b/app/assets/stylesheets/.gitkeep
diff --git a/app/assets/stylesheets/fonts/montserrat.scss b/app/assets/stylesheets/fonts/montserrat.scss
deleted file mode 100644
index 8705b3fa9..000000000
--- a/app/assets/stylesheets/fonts/montserrat.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-@font-face {
-  font-family: 'Montserrat';
-  src: local('Montserrat');
-  src: font-url('montserrat/Montserrat-Regular.eot');
-  src: font-url('montserrat/Montserrat-Regular.eot?#iefix') format('embedded-opentype'),
-  font-url('montserrat/Montserrat-Regular.woff2') format('woff2'),
-  font-url('montserrat/Montserrat-Regular.woff') format('woff'),
-  font-url('montserrat/Montserrat-Regular.ttf') format('truetype');
-  font-weight: 400;
-  font-style: normal;
-}
diff --git a/app/assets/stylesheets/fonts/roboto-mono.scss b/app/assets/stylesheets/fonts/roboto-mono.scss
deleted file mode 100644
index f46ce6b68..000000000
--- a/app/assets/stylesheets/fonts/roboto-mono.scss
+++ /dev/null
@@ -1,12 +0,0 @@
-@font-face {
-  font-family: 'Roboto Mono';
-  src: local('Roboto Mono');
-  src: font-url('roboto-mono/robotomono-regular-webfont.eot');
-  src: font-url('roboto-mono/robotomono-regular-webfont.eot?#iefix') format('embedded-opentype'),
-  font-url('roboto-mono/robotomono-regular-webfont.woff2') format('woff2'),
-  font-url('roboto-mono/robotomono-regular-webfont.woff') format('woff'),
-  font-url('roboto-mono/robotomono-regular-webfont.ttf') format('truetype'),
-  font-url('roboto-mono/robotomono-regular-webfont.svg#roboto_monoregular') format('svg');
-  font-weight: 400;
-  font-style: normal;
-}
diff --git a/app/assets/stylesheets/fonts/roboto.scss b/app/assets/stylesheets/fonts/roboto.scss
deleted file mode 100644
index fc62869d4..000000000
--- a/app/assets/stylesheets/fonts/roboto.scss
+++ /dev/null
@@ -1,52 +0,0 @@
-@font-face {
-  font-family: 'Roboto';
-  src: local('Roboto');
-  src: font-url('roboto/roboto-italic-webfont.eot');
-  src: font-url('roboto/roboto-italic-webfont.eot?#iefix') format('embedded-opentype'),
-    font-url('roboto/roboto-italic-webfont.woff2') format('woff2'),
-    font-url('roboto/roboto-italic-webfont.woff') format('woff'),
-    font-url('roboto/roboto-italic-webfont.ttf') format('truetype'),
-    font-url('roboto/roboto-italic-webfont.svg#roboto-italic-webfont') format('svg');
-  font-weight: normal;
-  font-style: italic;
-}
-
-@font-face {
-  font-family: 'Roboto';
-  src: local('Roboto');
-  src: font-url('roboto/roboto-bold-webfont.eot');
-  src: local('Roboto bold'), local('roboto-bold'),
-    font-url('roboto/roboto-bold-webfont.eot?#iefix') format('embedded-opentype'),
-    font-url('roboto/roboto-bold-webfont.woff2') format('woff2'),
-    font-url('roboto/roboto-bold-webfont.woff') format('woff'),
-    font-url('roboto/roboto-bold-webfont.ttf') format('truetype'),
-    font-url('roboto/roboto-bold-webfont.svg#roboto-bold-webfont') format('svg');
-  font-weight: bold;
-  font-style: normal;
-}
-
-@font-face {
-  font-family: 'Roboto';
-  src: local('Roboto');
-  src: font-url('roboto/roboto-medium-webfont.eot');
-  src: font-url('roboto/roboto-medium-webfont.eot?#iefix') format('embedded-opentype'),
-    font-url('roboto/roboto-medium-webfont.woff2') format('woff2'),
-    font-url('roboto/roboto-medium-webfont.woff') format('woff'),
-    font-url('roboto/roboto-medium-webfont.ttf') format('truetype'),
-    font-url('roboto/roboto-medium-webfont.svg#roboto-medium-webfont') format('svg');
-  font-weight: 500;
-  font-style: normal;
-}
-
-@font-face {
-  font-family: 'Roboto';
-  src: local('Roboto');
-  src: font-url('roboto/roboto-regular-webfont.eot');
-  src: font-url('roboto/roboto-regular-webfont.eot?#iefix') format('embedded-opentype'),
-    font-url('roboto/roboto-regular-webfont.woff2') format('woff2'),
-    font-url('roboto/roboto-regular-webfont.woff') format('woff'),
-    font-url('roboto/roboto-regular-webfont.ttf') format('truetype'),
-    font-url('roboto/roboto-regular-webfont.svg#roboto-regular-webfont') format('svg');
-  font-weight: normal;
-  font-style: normal;
-}
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 14c7447c0..92ffac33b 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -10,7 +10,7 @@ module ApplicationHelper
   end
 
   def add_rtl_body_class(other_classes)
-    other_classes = "#{other_classes} rtl" if [:ar, :fa].include?(I18n.locale)
+    other_classes = "#{other_classes} rtl" if [:ar, :fa, :he].include?(I18n.locale)
     other_classes
   end
 
@@ -22,4 +22,8 @@ module ApplicationHelper
   def title
     Rails.env.production? ? site_title : "#{site_title} (Dev)"
   end
+
+  def fa_icon(icon)
+    content_tag(:i, nil, class: 'fa ' + icon.split(' ').map { |cl| "fa-#{cl}" }.join(' '))
+  end
 end
diff --git a/app/assets/fonts/montserrat/Montserrat-Regular.eot b/app/javascript/fonts/montserrat/Montserrat-Regular.eot
index 2421c73cc..2421c73cc 100644
--- a/app/assets/fonts/montserrat/Montserrat-Regular.eot
+++ b/app/javascript/fonts/montserrat/Montserrat-Regular.eot
Binary files differdiff --git a/app/assets/fonts/montserrat/Montserrat-Regular.ttf b/app/javascript/fonts/montserrat/Montserrat-Regular.ttf
index 29ca85d4a..29ca85d4a 100644
--- a/app/assets/fonts/montserrat/Montserrat-Regular.ttf
+++ b/app/javascript/fonts/montserrat/Montserrat-Regular.ttf
Binary files differdiff --git a/app/assets/fonts/montserrat/Montserrat-Regular.woff b/app/javascript/fonts/montserrat/Montserrat-Regular.woff
index af3b5ec44..af3b5ec44 100644
--- a/app/assets/fonts/montserrat/Montserrat-Regular.woff
+++ b/app/javascript/fonts/montserrat/Montserrat-Regular.woff
Binary files differdiff --git a/app/assets/fonts/montserrat/Montserrat-Regular.woff2 b/app/javascript/fonts/montserrat/Montserrat-Regular.woff2
index 3d75434dd..3d75434dd 100644
--- a/app/assets/fonts/montserrat/Montserrat-Regular.woff2
+++ b/app/javascript/fonts/montserrat/Montserrat-Regular.woff2
Binary files differdiff --git a/app/assets/fonts/roboto-mono/robotomono-regular-webfont.eot b/app/javascript/fonts/roboto-mono/robotomono-regular-webfont.eot
index a4b8d961b..a4b8d961b 100644
--- a/app/assets/fonts/roboto-mono/robotomono-regular-webfont.eot
+++ b/app/javascript/fonts/roboto-mono/robotomono-regular-webfont.eot
Binary files differdiff --git a/app/assets/fonts/roboto-mono/robotomono-regular-webfont.svg b/app/javascript/fonts/roboto-mono/robotomono-regular-webfont.svg
index 8b0e15729..8b0e15729 100644
--- a/app/assets/fonts/roboto-mono/robotomono-regular-webfont.svg
+++ b/app/javascript/fonts/roboto-mono/robotomono-regular-webfont.svg
diff --git a/app/assets/fonts/roboto-mono/robotomono-regular-webfont.ttf b/app/javascript/fonts/roboto-mono/robotomono-regular-webfont.ttf
index 1ab663e40..1ab663e40 100644
--- a/app/assets/fonts/roboto-mono/robotomono-regular-webfont.ttf
+++ b/app/javascript/fonts/roboto-mono/robotomono-regular-webfont.ttf
Binary files differdiff --git a/app/assets/fonts/roboto-mono/robotomono-regular-webfont.woff b/app/javascript/fonts/roboto-mono/robotomono-regular-webfont.woff
index 1ed8af5d0..1ed8af5d0 100644
--- a/app/assets/fonts/roboto-mono/robotomono-regular-webfont.woff
+++ b/app/javascript/fonts/roboto-mono/robotomono-regular-webfont.woff
Binary files differdiff --git a/app/assets/fonts/roboto-mono/robotomono-regular-webfont.woff2 b/app/javascript/fonts/roboto-mono/robotomono-regular-webfont.woff2
index 1142739f6..1142739f6 100644
--- a/app/assets/fonts/roboto-mono/robotomono-regular-webfont.woff2
+++ b/app/javascript/fonts/roboto-mono/robotomono-regular-webfont.woff2
Binary files differdiff --git a/app/assets/fonts/roboto/roboto-bold-webfont.eot b/app/javascript/fonts/roboto/roboto-bold-webfont.eot
index dd8e1f24a..dd8e1f24a 100644
--- a/app/assets/fonts/roboto/roboto-bold-webfont.eot
+++ b/app/javascript/fonts/roboto/roboto-bold-webfont.eot
Binary files differdiff --git a/app/assets/fonts/roboto/roboto-bold-webfont.svg b/app/javascript/fonts/roboto/roboto-bold-webfont.svg
index 8b591f99e..8b591f99e 100644
--- a/app/assets/fonts/roboto/roboto-bold-webfont.svg
+++ b/app/javascript/fonts/roboto/roboto-bold-webfont.svg
diff --git a/app/assets/fonts/roboto/roboto-bold-webfont.ttf b/app/javascript/fonts/roboto/roboto-bold-webfont.ttf
index 08f6a72cc..08f6a72cc 100644
--- a/app/assets/fonts/roboto/roboto-bold-webfont.ttf
+++ b/app/javascript/fonts/roboto/roboto-bold-webfont.ttf
Binary files differdiff --git a/app/assets/fonts/roboto/roboto-bold-webfont.woff b/app/javascript/fonts/roboto/roboto-bold-webfont.woff
index c70f9410b..c70f9410b 100644
--- a/app/assets/fonts/roboto/roboto-bold-webfont.woff
+++ b/app/javascript/fonts/roboto/roboto-bold-webfont.woff
Binary files differdiff --git a/app/assets/fonts/roboto/roboto-bold-webfont.woff2 b/app/javascript/fonts/roboto/roboto-bold-webfont.woff2
index 4ce0bec66..4ce0bec66 100644
--- a/app/assets/fonts/roboto/roboto-bold-webfont.woff2
+++ b/app/javascript/fonts/roboto/roboto-bold-webfont.woff2
Binary files differdiff --git a/app/assets/fonts/roboto/roboto-italic-webfont.eot b/app/javascript/fonts/roboto/roboto-italic-webfont.eot
index 683f6c2cd..683f6c2cd 100644
--- a/app/assets/fonts/roboto/roboto-italic-webfont.eot
+++ b/app/javascript/fonts/roboto/roboto-italic-webfont.eot
Binary files differdiff --git a/app/assets/fonts/roboto/roboto-italic-webfont.svg b/app/javascript/fonts/roboto/roboto-italic-webfont.svg
index 44ffeb077..44ffeb077 100644
--- a/app/assets/fonts/roboto/roboto-italic-webfont.svg
+++ b/app/javascript/fonts/roboto/roboto-italic-webfont.svg
diff --git a/app/assets/fonts/roboto/roboto-italic-webfont.ttf b/app/javascript/fonts/roboto/roboto-italic-webfont.ttf
index f2175cb32..f2175cb32 100644
--- a/app/assets/fonts/roboto/roboto-italic-webfont.ttf
+++ b/app/javascript/fonts/roboto/roboto-italic-webfont.ttf
Binary files differdiff --git a/app/assets/fonts/roboto/roboto-italic-webfont.woff b/app/javascript/fonts/roboto/roboto-italic-webfont.woff
index 05e4efc6a..05e4efc6a 100644
--- a/app/assets/fonts/roboto/roboto-italic-webfont.woff
+++ b/app/javascript/fonts/roboto/roboto-italic-webfont.woff
Binary files differdiff --git a/app/assets/fonts/roboto/roboto-italic-webfont.woff2 b/app/javascript/fonts/roboto/roboto-italic-webfont.woff2
index 6b8dfd0b5..6b8dfd0b5 100644
--- a/app/assets/fonts/roboto/roboto-italic-webfont.woff2
+++ b/app/javascript/fonts/roboto/roboto-italic-webfont.woff2
Binary files differdiff --git a/app/assets/fonts/roboto/roboto-medium-webfont.eot b/app/javascript/fonts/roboto/roboto-medium-webfont.eot
index f3acc96f4..f3acc96f4 100644
--- a/app/assets/fonts/roboto/roboto-medium-webfont.eot
+++ b/app/javascript/fonts/roboto/roboto-medium-webfont.eot
Binary files differdiff --git a/app/assets/fonts/roboto/roboto-medium-webfont.svg b/app/javascript/fonts/roboto/roboto-medium-webfont.svg
index 290467b21..290467b21 100644
--- a/app/assets/fonts/roboto/roboto-medium-webfont.svg
+++ b/app/javascript/fonts/roboto/roboto-medium-webfont.svg
diff --git a/app/assets/fonts/roboto/roboto-medium-webfont.ttf b/app/javascript/fonts/roboto/roboto-medium-webfont.ttf
index 052420e8e..052420e8e 100644
--- a/app/assets/fonts/roboto/roboto-medium-webfont.ttf
+++ b/app/javascript/fonts/roboto/roboto-medium-webfont.ttf
Binary files differdiff --git a/app/assets/fonts/roboto/roboto-medium-webfont.woff b/app/javascript/fonts/roboto/roboto-medium-webfont.woff
index ade9ac255..ade9ac255 100644
--- a/app/assets/fonts/roboto/roboto-medium-webfont.woff
+++ b/app/javascript/fonts/roboto/roboto-medium-webfont.woff
Binary files differdiff --git a/app/assets/fonts/roboto/roboto-medium-webfont.woff2 b/app/javascript/fonts/roboto/roboto-medium-webfont.woff2
index 030f255eb..030f255eb 100644
--- a/app/assets/fonts/roboto/roboto-medium-webfont.woff2
+++ b/app/javascript/fonts/roboto/roboto-medium-webfont.woff2
Binary files differdiff --git a/app/assets/fonts/roboto/roboto-regular-webfont.eot b/app/javascript/fonts/roboto/roboto-regular-webfont.eot
index 5ca8e526c..5ca8e526c 100644
--- a/app/assets/fonts/roboto/roboto-regular-webfont.eot
+++ b/app/javascript/fonts/roboto/roboto-regular-webfont.eot
Binary files differdiff --git a/app/assets/fonts/roboto/roboto-regular-webfont.svg b/app/javascript/fonts/roboto/roboto-regular-webfont.svg
index 1d15b6bce..1d15b6bce 100644
--- a/app/assets/fonts/roboto/roboto-regular-webfont.svg
+++ b/app/javascript/fonts/roboto/roboto-regular-webfont.svg
diff --git a/app/assets/fonts/roboto/roboto-regular-webfont.ttf b/app/javascript/fonts/roboto/roboto-regular-webfont.ttf
index 696fd82b8..696fd82b8 100644
--- a/app/assets/fonts/roboto/roboto-regular-webfont.ttf
+++ b/app/javascript/fonts/roboto/roboto-regular-webfont.ttf
Binary files differdiff --git a/app/assets/fonts/roboto/roboto-regular-webfont.woff b/app/javascript/fonts/roboto/roboto-regular-webfont.woff
index b5e69e2b7..b5e69e2b7 100644
--- a/app/assets/fonts/roboto/roboto-regular-webfont.woff
+++ b/app/javascript/fonts/roboto/roboto-regular-webfont.woff
Binary files differdiff --git a/app/assets/fonts/roboto/roboto-regular-webfont.woff2 b/app/javascript/fonts/roboto/roboto-regular-webfont.woff2
index e01739b21..e01739b21 100644
--- a/app/assets/fonts/roboto/roboto-regular-webfont.woff2
+++ b/app/javascript/fonts/roboto/roboto-regular-webfont.woff2
Binary files differdiff --git a/app/assets/images/.keep b/app/javascript/images/.keep
index e69de29bb..e69de29bb 100644
--- a/app/assets/images/.keep
+++ b/app/javascript/images/.keep
diff --git a/app/assets/images/background-photo.jpg b/app/javascript/images/background-photo.jpg
index 03341b8ec..03341b8ec 100644
--- a/app/assets/images/background-photo.jpg
+++ b/app/javascript/images/background-photo.jpg
Binary files differdiff --git a/app/assets/images/boost_sprite.png b/app/javascript/images/boost_sprite.png
index 564bf2646..564bf2646 100644
--- a/app/assets/images/boost_sprite.png
+++ b/app/javascript/images/boost_sprite.png
Binary files differdiff --git a/app/assets/images/elephant-friend.png b/app/javascript/images/elephant-friend.png
index 3c5145ba9..3c5145ba9 100644
--- a/app/assets/images/elephant-friend.png
+++ b/app/javascript/images/elephant-friend.png
Binary files differdiff --git a/app/assets/images/fluffy-elephant-friend.png b/app/javascript/images/fluffy-elephant-friend.png
index f0df29927..f0df29927 100644
--- a/app/assets/images/fluffy-elephant-friend.png
+++ b/app/javascript/images/fluffy-elephant-friend.png
Binary files differdiff --git a/app/assets/images/logo.png b/app/javascript/images/logo.png
index f0c1c46c3..f0c1c46c3 100644
--- a/app/assets/images/logo.png
+++ b/app/javascript/images/logo.png
Binary files differdiff --git a/app/assets/images/logo.svg b/app/javascript/images/logo.svg
index c233db842..c233db842 100644
--- a/app/assets/images/logo.svg
+++ b/app/javascript/images/logo.svg
diff --git a/app/assets/images/mastodon-getting-started.png b/app/javascript/images/mastodon-getting-started.png
index e05dd493f..e05dd493f 100644
--- a/app/assets/images/mastodon-getting-started.png
+++ b/app/javascript/images/mastodon-getting-started.png
Binary files differdiff --git a/app/assets/images/mastodon-not-found.png b/app/javascript/images/mastodon-not-found.png
index 76108d41f..76108d41f 100644
--- a/app/assets/images/mastodon-not-found.png
+++ b/app/javascript/images/mastodon-not-found.png
Binary files differdiff --git a/app/assets/images/mastodon.jpg b/app/javascript/images/mastodon.jpg
index 2dfeb879f..2dfeb879f 100644
--- a/app/assets/images/mastodon.jpg
+++ b/app/javascript/images/mastodon.jpg
Binary files differdiff --git a/app/assets/images/mastodon_small.jpg b/app/javascript/images/mastodon_small.jpg
index 9c88ce3f7..9c88ce3f7 100644
--- a/app/assets/images/mastodon_small.jpg
+++ b/app/javascript/images/mastodon_small.jpg
Binary files differdiff --git a/app/assets/images/screenshot.png b/app/javascript/images/screenshot.png
index f248fd514..f248fd514 100644
--- a/app/assets/images/screenshot.png
+++ b/app/javascript/images/screenshot.png
Binary files differdiff --git a/app/assets/images/void.png b/app/javascript/images/void.png
index 10b274f18..10b274f18 100644
--- a/app/assets/images/void.png
+++ b/app/javascript/images/void.png
Binary files differdiff --git a/app/javascript/mastodon/.gitkeep b/app/javascript/mastodon/.gitkeep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/app/javascript/mastodon/.gitkeep
diff --git a/app/assets/javascripts/components/actions/accounts.jsx b/app/javascript/mastodon/actions/accounts.js
index eac5c78bb..eac5c78bb 100644
--- a/app/assets/javascripts/components/actions/accounts.jsx
+++ b/app/javascript/mastodon/actions/accounts.js
diff --git a/app/assets/javascripts/components/actions/alerts.jsx b/app/javascript/mastodon/actions/alerts.js
index 086e0727e..086e0727e 100644
--- a/app/assets/javascripts/components/actions/alerts.jsx
+++ b/app/javascript/mastodon/actions/alerts.js
diff --git a/app/assets/javascripts/components/actions/blocks.jsx b/app/javascript/mastodon/actions/blocks.js
index 79e316497..79e316497 100644
--- a/app/assets/javascripts/components/actions/blocks.jsx
+++ b/app/javascript/mastodon/actions/blocks.js
diff --git a/app/assets/javascripts/components/actions/cards.jsx b/app/javascript/mastodon/actions/cards.js
index 805be9709..805be9709 100644
--- a/app/assets/javascripts/components/actions/cards.jsx
+++ b/app/javascript/mastodon/actions/cards.js
diff --git a/app/assets/javascripts/components/actions/compose.jsx b/app/javascript/mastodon/actions/compose.js
index d7ff6ea63..d7ff6ea63 100644
--- a/app/assets/javascripts/components/actions/compose.jsx
+++ b/app/javascript/mastodon/actions/compose.js
diff --git a/app/assets/javascripts/components/actions/favourites.jsx b/app/javascript/mastodon/actions/favourites.js
index a25c1ae1c..a25c1ae1c 100644
--- a/app/assets/javascripts/components/actions/favourites.jsx
+++ b/app/javascript/mastodon/actions/favourites.js
diff --git a/app/assets/javascripts/components/actions/interactions.jsx b/app/javascript/mastodon/actions/interactions.js
index 45f4508f6..45f4508f6 100644
--- a/app/assets/javascripts/components/actions/interactions.jsx
+++ b/app/javascript/mastodon/actions/interactions.js
diff --git a/app/assets/javascripts/components/actions/modal.jsx b/app/javascript/mastodon/actions/modal.js
index 615cd6bfe..615cd6bfe 100644
--- a/app/assets/javascripts/components/actions/modal.jsx
+++ b/app/javascript/mastodon/actions/modal.js
diff --git a/app/assets/javascripts/components/actions/mutes.jsx b/app/javascript/mastodon/actions/mutes.js
index 824821594..824821594 100644
--- a/app/assets/javascripts/components/actions/mutes.jsx
+++ b/app/javascript/mastodon/actions/mutes.js
diff --git a/app/assets/javascripts/components/actions/notifications.jsx b/app/javascript/mastodon/actions/notifications.js
index b09ca0854..b09ca0854 100644
--- a/app/assets/javascripts/components/actions/notifications.jsx
+++ b/app/javascript/mastodon/actions/notifications.js
diff --git a/app/assets/javascripts/components/actions/onboarding.jsx b/app/javascript/mastodon/actions/onboarding.js
index a161c50ef..a161c50ef 100644
--- a/app/assets/javascripts/components/actions/onboarding.jsx
+++ b/app/javascript/mastodon/actions/onboarding.js
diff --git a/app/assets/javascripts/components/actions/reports.jsx b/app/javascript/mastodon/actions/reports.js
index 094670d62..094670d62 100644
--- a/app/assets/javascripts/components/actions/reports.jsx
+++ b/app/javascript/mastodon/actions/reports.js
diff --git a/app/assets/javascripts/components/actions/search.jsx b/app/javascript/mastodon/actions/search.js
index df3ae0db1..df3ae0db1 100644
--- a/app/assets/javascripts/components/actions/search.jsx
+++ b/app/javascript/mastodon/actions/search.js
diff --git a/app/assets/javascripts/components/actions/settings.jsx b/app/javascript/mastodon/actions/settings.js
index c754b30ca..c754b30ca 100644
--- a/app/assets/javascripts/components/actions/settings.jsx
+++ b/app/javascript/mastodon/actions/settings.js
diff --git a/app/assets/javascripts/components/actions/statuses.jsx b/app/javascript/mastodon/actions/statuses.js
index 19df2c36c..19df2c36c 100644
--- a/app/assets/javascripts/components/actions/statuses.jsx
+++ b/app/javascript/mastodon/actions/statuses.js
diff --git a/app/assets/javascripts/components/actions/store.jsx b/app/javascript/mastodon/actions/store.js
index 3bba99549..3bba99549 100644
--- a/app/assets/javascripts/components/actions/store.jsx
+++ b/app/javascript/mastodon/actions/store.js
diff --git a/app/assets/javascripts/components/actions/timelines.jsx b/app/javascript/mastodon/actions/timelines.js
index 6cd1f04b3..6cd1f04b3 100644
--- a/app/assets/javascripts/components/actions/timelines.jsx
+++ b/app/javascript/mastodon/actions/timelines.js
diff --git a/app/assets/javascripts/components/api.jsx b/app/javascript/mastodon/api.js
index 185729ce0..185729ce0 100644
--- a/app/assets/javascripts/components/api.jsx
+++ b/app/javascript/mastodon/api.js
diff --git a/app/assets/javascripts/components/components/account.jsx b/app/javascript/mastodon/components/account.js
index 81439bd25..9016bedb6 100644
--- a/app/assets/javascripts/components/components/account.jsx
+++ b/app/javascript/mastodon/components/account.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import Avatar from './avatar';
@@ -5,6 +6,7 @@ import DisplayName from './display_name';
 import Permalink from './permalink';
 import IconButton from './icon_button';
 import { defineMessages, injectIntl } from 'react-intl';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
 const messages = defineMessages({
   follow: { id: 'account.follow', defaultMessage: 'Follow' },
@@ -14,7 +16,7 @@ const messages = defineMessages({
   unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' }
 });
 
-class Account extends React.PureComponent {
+class Account extends ImmutablePureComponent {
 
   constructor (props, context) {
     super(props, context);
diff --git a/app/assets/javascripts/components/components/attachment_list.jsx b/app/javascript/mastodon/components/attachment_list.js
index 54841fa51..6df578b77 100644
--- a/app/assets/javascripts/components/components/attachment_list.jsx
+++ b/app/javascript/mastodon/components/attachment_list.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 
 const filename = url => url.split('/').pop().split('#')[0].split('?')[0];
diff --git a/app/assets/javascripts/components/components/autosuggest_textarea.jsx b/app/javascript/mastodon/components/autosuggest_textarea.js
index 9a4d5b7e3..6d8d3b2a3 100644
--- a/app/assets/javascripts/components/components/autosuggest_textarea.jsx
+++ b/app/javascript/mastodon/components/autosuggest_textarea.js
@@ -1,7 +1,9 @@
+import React from 'react';
 import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import { isRtl } from '../rtl';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
 const textAtCursorMatchesToken = (str, caretPosition) => {
   let word;
@@ -28,7 +30,7 @@ const textAtCursorMatchesToken = (str, caretPosition) => {
   }
 };
 
-class AutosuggestTextarea extends React.Component {
+class AutosuggestTextarea extends ImmutablePureComponent {
 
   constructor (props, context) {
     super(props, context);
diff --git a/app/assets/javascripts/components/components/avatar.jsx b/app/javascript/mastodon/components/avatar.js
index d1a00ac39..47f2715c7 100644
--- a/app/assets/javascripts/components/components/avatar.jsx
+++ b/app/javascript/mastodon/components/avatar.js
@@ -1,21 +1,26 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 
 class Avatar extends React.PureComponent {
 
   constructor (props, context) {
     super(props, context);
+
     this.state = {
       hovering: false
     };
+
     this.handleMouseEnter = this.handleMouseEnter.bind(this);
     this.handleMouseLeave = this.handleMouseLeave.bind(this);
   }
 
   handleMouseEnter () {
+    if (this.props.animate) return;
     this.setState({ hovering: true });
   }
 
   handleMouseLeave () {
+    if (this.props.animate) return;
     this.setState({ hovering: false });
   }
 
diff --git a/app/assets/javascripts/components/components/button.jsx b/app/javascript/mastodon/components/button.js
index 00d80b1fd..1063e0289 100644
--- a/app/assets/javascripts/components/components/button.jsx
+++ b/app/javascript/mastodon/components/button.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 
 class Button extends React.PureComponent {
diff --git a/app/assets/javascripts/components/components/collapsable.jsx b/app/javascript/mastodon/components/collapsable.js
index 0810768f0..a61f67d8e 100644
--- a/app/assets/javascripts/components/components/collapsable.jsx
+++ b/app/javascript/mastodon/components/collapsable.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { Motion, spring } from 'react-motion';
 import PropTypes from 'prop-types';
 
diff --git a/app/assets/javascripts/components/components/column_back_button.jsx b/app/javascript/mastodon/components/column_back_button.js
index 70110f0aa..bedc417fd 100644
--- a/app/assets/javascripts/components/components/column_back_button.jsx
+++ b/app/javascript/mastodon/components/column_back_button.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { FormattedMessage } from 'react-intl';
 import PropTypes from 'prop-types';
 
diff --git a/app/assets/javascripts/components/components/column_back_button_slim.jsx b/app/javascript/mastodon/components/column_back_button_slim.js
index 719690097..9aa7e92c2 100644
--- a/app/assets/javascripts/components/components/column_back_button_slim.jsx
+++ b/app/javascript/mastodon/components/column_back_button_slim.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { FormattedMessage } from 'react-intl';
 import PropTypes from 'prop-types';
 
diff --git a/app/assets/javascripts/components/components/column_collapsable.jsx b/app/javascript/mastodon/components/column_collapsable.js
index 2cb440862..797946859 100644
--- a/app/assets/javascripts/components/components/column_collapsable.jsx
+++ b/app/javascript/mastodon/components/column_collapsable.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { Motion, spring } from 'react-motion';
 import PropTypes from 'prop-types';
 
diff --git a/app/assets/javascripts/components/components/display_name.jsx b/app/javascript/mastodon/components/display_name.js
index d7257e092..6bdd06db7 100644
--- a/app/assets/javascripts/components/components/display_name.jsx
+++ b/app/javascript/mastodon/components/display_name.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import escapeTextContentForBrowser from 'escape-html';
 import emojify from '../emoji';
diff --git a/app/assets/javascripts/components/components/dropdown_menu.jsx b/app/javascript/mastodon/components/dropdown_menu.js
index f5ee27a11..aed0757b1 100644
--- a/app/assets/javascripts/components/components/dropdown_menu.jsx
+++ b/app/javascript/mastodon/components/dropdown_menu.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import Dropdown, { DropdownTrigger, DropdownContent } from 'react-simple-dropdown';
 import PropTypes from 'prop-types';
 
diff --git a/app/assets/javascripts/components/components/extended_video_player.jsx b/app/javascript/mastodon/components/extended_video_player.js
index bbbe09da8..34ede66fd 100644
--- a/app/assets/javascripts/components/components/extended_video_player.jsx
+++ b/app/javascript/mastodon/components/extended_video_player.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 
 class ExtendedVideoPlayer extends React.PureComponent {
diff --git a/app/assets/javascripts/components/components/icon_button.jsx b/app/javascript/mastodon/components/icon_button.js
index 67c6513fd..87324b6c8 100644
--- a/app/assets/javascripts/components/components/icon_button.jsx
+++ b/app/javascript/mastodon/components/icon_button.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { Motion, spring } from 'react-motion';
 import PropTypes from 'prop-types';
 
diff --git a/app/assets/javascripts/components/components/load_more.jsx b/app/javascript/mastodon/components/load_more.js
index f59ff1103..36dae79af 100644
--- a/app/assets/javascripts/components/components/load_more.jsx
+++ b/app/javascript/mastodon/components/load_more.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { FormattedMessage } from 'react-intl';
 import PropTypes from 'prop-types';
 
diff --git a/app/assets/javascripts/components/components/loading_indicator.jsx b/app/javascript/mastodon/components/loading_indicator.js
index 61e0a0f15..c09244834 100644
--- a/app/assets/javascripts/components/components/loading_indicator.jsx
+++ b/app/javascript/mastodon/components/loading_indicator.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { FormattedMessage } from 'react-intl';
 
 const LoadingIndicator = () => (
diff --git a/app/assets/javascripts/components/components/media_gallery.jsx b/app/javascript/mastodon/components/media_gallery.js
index 58e134f50..dc08c457d 100644
--- a/app/assets/javascripts/components/components/media_gallery.jsx
+++ b/app/javascript/mastodon/components/media_gallery.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import IconButton from './icon_button';
diff --git a/app/assets/javascripts/components/components/missing_indicator.jsx b/app/javascript/mastodon/components/missing_indicator.js
index 75129ae14..87df7f61c 100644
--- a/app/assets/javascripts/components/components/missing_indicator.jsx
+++ b/app/javascript/mastodon/components/missing_indicator.js
@@ -1,8 +1,11 @@
+import React from 'react';
 import { FormattedMessage } from 'react-intl';
 
 const MissingIndicator = () => (
   <div className='missing-indicator'>
-    <FormattedMessage id='missing_indicator.label' defaultMessage='Not found' />
+    <div>
+      <FormattedMessage id='missing_indicator.label' defaultMessage='Not found' />
+    </div>
   </div>
 );
 
diff --git a/app/assets/javascripts/components/components/permalink.jsx b/app/javascript/mastodon/components/permalink.js
index ccbe4944f..26444f27c 100644
--- a/app/assets/javascripts/components/components/permalink.jsx
+++ b/app/javascript/mastodon/components/permalink.js
@@ -1,6 +1,7 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 
-class Permalink extends React.Component {
+class Permalink extends React.PureComponent {
 
   constructor (props, context) {
     super(props, context);
@@ -17,7 +18,11 @@ class Permalink extends React.Component {
   render () {
     const { href, children, className, ...other } = this.props;
 
-    return <a href={href} onClick={this.handleClick} {...other} className={'permalink ' + className}>{children}</a>;
+    return (
+      <a href={href} onClick={this.handleClick} {...other} className={'permalink ' + className}>
+        {children}
+      </a>
+    );
   }
 
 }
diff --git a/app/assets/javascripts/components/components/relative_timestamp.jsx b/app/javascript/mastodon/components/relative_timestamp.js
index 9ab472e2c..9c7a8121e 100644
--- a/app/assets/javascripts/components/components/relative_timestamp.jsx
+++ b/app/javascript/mastodon/components/relative_timestamp.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { injectIntl, FormattedRelative } from 'react-intl';
 import PropTypes from 'prop-types';
 
diff --git a/app/assets/javascripts/components/components/status.jsx b/app/javascript/mastodon/components/status.js
index 193231837..39ed6dd4f 100644
--- a/app/assets/javascripts/components/components/status.jsx
+++ b/app/javascript/mastodon/components/status.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import Avatar from './avatar';
@@ -11,8 +12,9 @@ import StatusActionBar from './status_action_bar';
 import { FormattedMessage } from 'react-intl';
 import emojify from '../emoji';
 import escapeTextContentForBrowser from 'escape-html';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
-class Status extends React.PureComponent {
+class Status extends ImmutablePureComponent {
 
   constructor (props, context) {
     super(props, context);
diff --git a/app/assets/javascripts/components/components/status_action_bar.jsx b/app/javascript/mastodon/components/status_action_bar.js
index 29938f23e..dc4466d6c 100644
--- a/app/assets/javascripts/components/components/status_action_bar.jsx
+++ b/app/javascript/mastodon/components/status_action_bar.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import IconButton from './icon_button';
diff --git a/app/assets/javascripts/components/components/status_content.jsx b/app/javascript/mastodon/components/status_content.js
index 714c00951..1d462103b 100644
--- a/app/assets/javascripts/components/components/status_content.jsx
+++ b/app/javascript/mastodon/components/status_content.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import escapeTextContentForBrowser from 'escape-html';
 import PropTypes from 'prop-types';
@@ -18,10 +19,11 @@ class StatusContent extends React.PureComponent {
     this.handleMouseDown = this.handleMouseDown.bind(this)
     this.handleMouseUp = this.handleMouseUp.bind(this);
     this.handleSpoilerClick = this.handleSpoilerClick.bind(this);
+    this.setRef = this.setRef.bind(this);
   };
 
   componentDidMount () {
-    const node  = ReactDOM.findDOMNode(this);
+    const node  = this.node;
     const links = node.querySelectorAll('a');
 
     for (var i = 0; i < links.length; ++i) {
@@ -84,6 +86,10 @@ class StatusContent extends React.PureComponent {
     this.setState({ hidden: !this.state.hidden });
   }
 
+  setRef (c) {
+    this.node = c;
+  }
+
   render () {
     const { status } = this.props;
     const { hidden } = this.state;
@@ -119,12 +125,13 @@ class StatusContent extends React.PureComponent {
 
           {mentionsPlaceholder}
 
-          <div style={{ display: hidden ? 'none' : 'block', ...directionStyle }} dangerouslySetInnerHTML={content} />
+          <div ref={this.setRef} style={{ display: hidden ? 'none' : 'block', ...directionStyle }} dangerouslySetInnerHTML={content} />
         </div>
       );
     } else if (this.props.onClick) {
       return (
         <div
+          ref={this.setRef}
           className='status__content'
           style={{ ...directionStyle }}
           onMouseDown={this.handleMouseDown}
@@ -135,6 +142,7 @@ class StatusContent extends React.PureComponent {
     } else {
       return (
         <div
+          ref={this.setRef}
           className='status__content status__content--no-action'
           style={{ ...directionStyle }}
           dangerouslySetInnerHTML={content}
diff --git a/app/assets/javascripts/components/components/status_list.jsx b/app/javascript/mastodon/components/status_list.js
index 517c8fe5d..9abf1fbfe 100644
--- a/app/assets/javascripts/components/components/status_list.jsx
+++ b/app/javascript/mastodon/components/status_list.js
@@ -1,11 +1,13 @@
+import React from 'react';
 import Status from './status';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { ScrollContainer } from 'react-router-scroll';
 import PropTypes from 'prop-types';
 import StatusContainer from '../containers/status_container';
 import LoadMore from './load_more';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
-class StatusList extends React.PureComponent {
+class StatusList extends ImmutablePureComponent {
 
   constructor (props, context) {
     super(props, context);
diff --git a/app/assets/javascripts/components/components/video_player.jsx b/app/javascript/mastodon/components/video_player.js
index 09c8ed875..0c8aea3a9 100644
--- a/app/assets/javascripts/components/components/video_player.jsx
+++ b/app/javascript/mastodon/components/video_player.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import IconButton from './icon_button';
@@ -40,7 +41,7 @@ class VideoPlayer extends React.PureComponent {
   handleVideoClick (e) {
     e.stopPropagation();
 
-    const node = ReactDOM.findDOMNode(this).querySelector('video');
+    const node = this.video;
 
     if (node.paused) {
       node.play();
@@ -173,7 +174,18 @@ class VideoPlayer extends React.PureComponent {
         {spoilerButton}
         {muteButton}
         {expandButton}
-        <video className='status__video-player-video' role='button' tabIndex='0' ref={this.setRef} src={media.get('url')} autoPlay={!isIOS()} loop={true} muted={this.state.muted} onClick={this.handleVideoClick} />
+
+        <video
+          className='status__video-player-video'
+          role='button'
+          tabIndex='0'
+          ref={this.setRef}
+          src={media.get('url')}
+          autoPlay={!isIOS()}
+          loop={true}
+          muted={this.state.muted}
+          onClick={this.handleVideoClick}
+        />
       </div>
     );
   }
diff --git a/app/assets/javascripts/components/containers/account_container.jsx b/app/javascript/mastodon/containers/account_container.js
index 3c30be715..3c30be715 100644
--- a/app/assets/javascripts/components/containers/account_container.jsx
+++ b/app/javascript/mastodon/containers/account_container.js
diff --git a/app/assets/javascripts/components/containers/mastodon.jsx b/app/javascript/mastodon/containers/mastodon.js
index 3f58b257a..637199686 100644
--- a/app/assets/javascripts/components/containers/mastodon.jsx
+++ b/app/javascript/mastodon/containers/mastodon.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { Provider } from 'react-redux';
 import PropTypes from 'prop-types';
 import configureStore from '../store/configureStore';
@@ -62,9 +63,6 @@ import uk from 'react-intl/locale-data/uk';
 import zh from 'react-intl/locale-data/zh';
 import bg from 'react-intl/locale-data/bg';
 import id from 'react-intl/locale-data/id';
-import { localeData as zh_hk } from '../locales/zh-hk';
-import { localeData as zh_cn } from '../locales/zh-cn';
-import pt_br from '../locales/pt-br';
 import getMessagesForLocale from '../locales';
 import { hydrateStore } from '../actions/store';
 import createStream from '../stream';
@@ -91,14 +89,11 @@ addLocaleData([
   ...it,
   ...ja,
   ...pt,
-  ...pt_br,
   ...nl,
   ...no,
   ...ru,
   ...uk,
   ...zh,
-  ...zh_hk,
-  ...zh_cn,
   ...bg,
   ...id,
 ]);
@@ -284,8 +279,7 @@ class Mastodon extends React.Component {
         <Provider store={store}>
           <Router history={browserHistory} render={applyRouterMiddleware(useScroll())}>
             <Route path='/' component={Container}>
-              <IndexRedirect to="/getting-started" />
-
+              <IndexRedirect to='/getting-started' />
               <Route path='getting-started' component={GettingStarted} />
               <Route path='timelines/tag/:id' component={HashtagTimeline} />
 
diff --git a/app/assets/javascripts/components/containers/status_container.jsx b/app/javascript/mastodon/containers/status_container.js
index ae83d36c9..eb1f1ab79 100644
--- a/app/assets/javascripts/components/containers/status_container.jsx
+++ b/app/javascript/mastodon/containers/status_container.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { connect } from 'react-redux';
 import Status from '../components/status';
 import { makeGetStatus } from '../selectors';
diff --git a/app/assets/javascripts/components/emoji.jsx b/app/javascript/mastodon/emoji.js
index eee657b86..eee657b86 100644
--- a/app/assets/javascripts/components/emoji.jsx
+++ b/app/javascript/mastodon/emoji.js
diff --git a/app/assets/javascripts/components/features/account/components/action_bar.jsx b/app/javascript/mastodon/features/account/components/action_bar.js
index 772ea3a38..069348050 100644
--- a/app/assets/javascripts/components/features/account/components/action_bar.jsx
+++ b/app/javascript/mastodon/features/account/components/action_bar.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import DropdownMenu from '../../../components/dropdown_menu';
diff --git a/app/assets/javascripts/components/features/account/components/header.jsx b/app/javascript/mastodon/features/account/components/header.js
index 958a5206b..fbaa5e9e6 100644
--- a/app/assets/javascripts/components/features/account/components/header.jsx
+++ b/app/javascript/mastodon/features/account/components/header.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import emojify from '../../../emoji';
@@ -6,6 +7,7 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import IconButton from '../../../components/icon_button';
 import { Motion, spring } from 'react-motion';
 import { connect } from 'react-redux';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
 const messages = defineMessages({
   unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
@@ -21,7 +23,7 @@ const makeMapStateToProps = () => {
   return mapStateToProps;
 };
 
-class Avatar extends React.PureComponent {
+class Avatar extends ImmutablePureComponent {
 
   constructor (props, context) {
     super(props, context);
@@ -74,7 +76,7 @@ Avatar.propTypes = {
   autoPlayGif: PropTypes.bool.isRequired
 };
 
-class Header extends React.Component {
+class Header extends ImmutablePureComponent {
 
   render () {
     const { account, me, intl } = this.props;
diff --git a/app/assets/javascripts/components/features/account_timeline/components/header.jsx b/app/javascript/mastodon/features/account_timeline/components/header.js
index fd66c13e0..b4dca3a57 100644
--- a/app/assets/javascripts/components/features/account_timeline/components/header.jsx
+++ b/app/javascript/mastodon/features/account_timeline/components/header.js
@@ -1,10 +1,12 @@
+import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import InnerHeader from '../../account/components/header';
 import ActionBar from '../../account/components/action_bar';
 import MissingIndicator from '../../../components/missing_indicator';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
-class Header extends React.PureComponent {
+class Header extends ImmutablePureComponent {
 
   constructor (props, context) {
     super(props, context);
diff --git a/app/assets/javascripts/components/features/account_timeline/containers/header_container.jsx b/app/javascript/mastodon/features/account_timeline/containers/header_container.js
index f924e7f5e..50999d2e0 100644
--- a/app/assets/javascripts/components/features/account_timeline/containers/header_container.jsx
+++ b/app/javascript/mastodon/features/account_timeline/containers/header_container.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { connect } from 'react-redux';
 import { makeGetAccount } from '../../../selectors';
 import Header from '../components/header';
diff --git a/app/assets/javascripts/components/features/account_timeline/index.jsx b/app/javascript/mastodon/features/account_timeline/index.js
index a06de3d21..fb76f4d2e 100644
--- a/app/assets/javascripts/components/features/account_timeline/index.jsx
+++ b/app/javascript/mastodon/features/account_timeline/index.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { connect } from 'react-redux';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
@@ -12,6 +13,7 @@ import Column from '../ui/components/column';
 import HeaderContainer from './containers/header_container';
 import ColumnBackButton from '../../components/column_back_button';
 import Immutable from 'immutable';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
 const mapStateToProps = (state, props) => ({
   statusIds: state.getIn(['timelines', 'accounts_timelines', Number(props.params.accountId), 'items'], Immutable.List()),
@@ -20,7 +22,7 @@ const mapStateToProps = (state, props) => ({
   me: state.getIn(['meta', 'me'])
 });
 
-class AccountTimeline extends React.PureComponent {
+class AccountTimeline extends ImmutablePureComponent {
 
   constructor (props, context) {
     super(props, context);
diff --git a/app/assets/javascripts/components/features/blocks/index.jsx b/app/javascript/mastodon/features/blocks/index.js
index 8b973ebb1..e25d9b2b4 100644
--- a/app/assets/javascripts/components/features/blocks/index.jsx
+++ b/app/javascript/mastodon/features/blocks/index.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { connect } from 'react-redux';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
@@ -8,6 +9,7 @@ import ColumnBackButtonSlim from '../../components/column_back_button_slim';
 import AccountContainer from '../../containers/account_container';
 import { fetchBlocks, expandBlocks } from '../../actions/blocks';
 import { defineMessages, injectIntl } from 'react-intl';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
 const messages = defineMessages({
   heading: { id: 'column.blocks', defaultMessage: 'Blocked users' }
@@ -17,7 +19,7 @@ const mapStateToProps = state => ({
   accountIds: state.getIn(['user_lists', 'blocks', 'items'])
 });
 
-class Blocks extends React.PureComponent {
+class Blocks extends ImmutablePureComponent {
 
   constructor (props, context) {
     super(props, context);
diff --git a/app/assets/javascripts/components/features/community_timeline/index.jsx b/app/javascript/mastodon/features/community_timeline/index.js
index 3877888ba..883263631 100644
--- a/app/assets/javascripts/components/features/community_timeline/index.jsx
+++ b/app/javascript/mastodon/features/community_timeline/index.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import StatusListContainer from '../ui/containers/status_list_container';
diff --git a/app/javascript/mastodon/features/compose/components/autosuggest_account.js b/app/javascript/mastodon/features/compose/components/autosuggest_account.js
new file mode 100644
index 000000000..3d87c4649
--- /dev/null
+++ b/app/javascript/mastodon/features/compose/components/autosuggest_account.js
@@ -0,0 +1,26 @@
+import React from 'react';
+import Avatar from '../../../components/avatar';
+import DisplayName from '../../../components/display_name';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+
+class AutosuggestAccount extends ImmutablePureComponent {
+
+  render () {
+    const { account } = this.props;
+
+    return (
+      <div className='autosuggest-account'>
+        <div className='autosuggest-account-icon'><Avatar src={account.get('avatar')} staticSrc={account.get('avatar_static')} size={18} /></div>
+        <DisplayName account={account} />
+      </div>
+    );
+  }
+
+}
+
+AutosuggestAccount.propTypes = {
+  account: ImmutablePropTypes.map.isRequired
+};
+
+export default AutosuggestAccount;
diff --git a/app/assets/javascripts/components/features/compose/components/character_counter.jsx b/app/javascript/mastodon/features/compose/components/character_counter.js
index 08d2ac4d1..617f85cfe 100644
--- a/app/assets/javascripts/components/features/compose/components/character_counter.jsx
+++ b/app/javascript/mastodon/features/compose/components/character_counter.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 import { length } from 'stringz';
 
diff --git a/app/assets/javascripts/components/features/compose/components/compose_form.jsx b/app/javascript/mastodon/features/compose/components/compose_form.js
index 6bc811160..0b9c097e3 100644
--- a/app/assets/javascripts/components/features/compose/components/compose_form.jsx
+++ b/app/javascript/mastodon/features/compose/components/compose_form.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import CharacterCounter from './character_counter';
 import Button from '../../../components/button';
 import ImmutablePropTypes from 'react-immutable-proptypes';
@@ -16,6 +17,7 @@ import EmojiPickerDropdown from './emoji_picker_dropdown';
 import UploadFormContainer from '../containers/upload_form_container';
 import TextIconButton from './text_icon_button';
 import WarningContainer from '../containers/warning_container';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
 const messages = defineMessages({
   placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' },
@@ -23,7 +25,7 @@ const messages = defineMessages({
   publish: { id: 'compose_form.publish', defaultMessage: 'Toot' }
 });
 
-class ComposeForm extends React.PureComponent {
+class ComposeForm extends ImmutablePureComponent {
 
   constructor (props, context) {
     super(props, context);
diff --git a/app/assets/javascripts/components/features/compose/components/emoji_picker_dropdown.jsx b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js
index bc22b074d..3e0b290d6 100644
--- a/app/assets/javascripts/components/features/compose/components/emoji_picker_dropdown.jsx
+++ b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import Dropdown, { DropdownTrigger, DropdownContent } from 'react-simple-dropdown';
 import EmojiPicker from 'emojione-picker';
 import PropTypes from 'prop-types';
diff --git a/app/assets/javascripts/components/features/compose/components/navigation_bar.jsx b/app/javascript/mastodon/features/compose/components/navigation_bar.js
index aae0592c6..aec8f6153 100644
--- a/app/assets/javascripts/components/features/compose/components/navigation_bar.jsx
+++ b/app/javascript/mastodon/features/compose/components/navigation_bar.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import Avatar from '../../../components/avatar';
 import IconButton from '../../../components/icon_button';
@@ -5,18 +6,22 @@ import DisplayName from '../../../components/display_name';
 import Permalink from '../../../components/permalink';
 import { FormattedMessage } from 'react-intl';
 import { Link } from 'react-router';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
-class NavigationBar extends React.PureComponent {
+class NavigationBar extends ImmutablePureComponent {
 
   render () {
     return (
       <div className='navigation-bar'>
-        <Permalink href={this.props.account.get('url')} to={`/accounts/${this.props.account.get('id')}`}><Avatar src={this.props.account.get('avatar')} animate size={40} /></Permalink>
+        <Permalink href={this.props.account.get('url')} to={`/accounts/${this.props.account.get('id')}`}>
+          <Avatar src={this.props.account.get('avatar')} animate size={40} />
+        </Permalink>
 
         <div className='navigation-bar__profile'>
           <Permalink href={this.props.account.get('url')} to={`/accounts/${this.props.account.get('id')}`}>
             <strong className='navigation-bar__profile-account'>@{this.props.account.get('acct')}</strong>
           </Permalink>
+
           <a href='/settings/profile' className='navigation-bar__profile-edit'><FormattedMessage id='navigation_bar.edit_profile' defaultMessage='Edit profile' /></a>
         </div>
       </div>
diff --git a/app/assets/javascripts/components/features/compose/components/privacy_dropdown.jsx b/app/javascript/mastodon/features/compose/components/privacy_dropdown.js
index 82b3454c6..b77d55f4d 100644
--- a/app/assets/javascripts/components/features/compose/components/privacy_dropdown.jsx
+++ b/app/javascript/mastodon/features/compose/components/privacy_dropdown.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 import { injectIntl, defineMessages } from 'react-intl';
 import IconButton from '../../../components/icon_button';
diff --git a/app/assets/javascripts/components/features/compose/components/reply_indicator.jsx b/app/javascript/mastodon/features/compose/components/reply_indicator.js
index 442ed5a35..e53831b60 100644
--- a/app/assets/javascripts/components/features/compose/components/reply_indicator.jsx
+++ b/app/javascript/mastodon/features/compose/components/reply_indicator.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import Avatar from '../../../components/avatar';
@@ -5,12 +6,13 @@ import IconButton from '../../../components/icon_button';
 import DisplayName from '../../../components/display_name';
 import emojify from '../../../emoji';
 import { defineMessages, injectIntl } from 'react-intl';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
 const messages = defineMessages({
   cancel: { id: 'reply_indicator.cancel', defaultMessage: 'Cancel' }
 });
 
-class ReplyIndicator extends React.PureComponent {
+class ReplyIndicator extends ImmutablePureComponent {
 
   constructor (props, context) {
     super(props, context);
diff --git a/app/assets/javascripts/components/features/compose/components/search.jsx b/app/javascript/mastodon/features/compose/components/search.js
index f62248a33..61ae9ce23 100644
--- a/app/assets/javascripts/components/features/compose/components/search.jsx
+++ b/app/javascript/mastodon/features/compose/components/search.js
@@ -1,4 +1,4 @@
-import ImmutablePropTypes from 'react-immutable-proptypes';
+import React from 'react';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 
diff --git a/app/assets/javascripts/components/features/compose/components/search_results.jsx b/app/javascript/mastodon/features/compose/components/search_results.js
index 00bfd1786..79e880f0a 100644
--- a/app/assets/javascripts/components/features/compose/components/search_results.jsx
+++ b/app/javascript/mastodon/features/compose/components/search_results.js
@@ -1,10 +1,12 @@
+import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import AccountContainer from '../../../containers/account_container';
 import StatusContainer from '../../../containers/status_container';
 import { Link } from 'react-router';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
-class SearchResults extends React.PureComponent {
+class SearchResults extends ImmutablePureComponent {
 
   render () {
     const { results } = this.props;
diff --git a/app/assets/javascripts/components/features/compose/components/text_icon_button.jsx b/app/javascript/mastodon/features/compose/components/text_icon_button.js
index 4252596c2..bcfa21090 100644
--- a/app/assets/javascripts/components/features/compose/components/text_icon_button.jsx
+++ b/app/javascript/mastodon/features/compose/components/text_icon_button.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 
 class TextIconButton extends React.PureComponent {
diff --git a/app/assets/javascripts/components/features/compose/components/upload_button.jsx b/app/javascript/mastodon/features/compose/components/upload_button.js
index 9b2de0332..15ec2edd6 100644
--- a/app/assets/javascripts/components/features/compose/components/upload_button.jsx
+++ b/app/javascript/mastodon/features/compose/components/upload_button.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import IconButton from '../../../components/icon_button';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl } from 'react-intl';
diff --git a/app/assets/javascripts/components/features/compose/components/upload_form.jsx b/app/javascript/mastodon/features/compose/components/upload_form.js
index a2fb7cfe0..8e48538da 100644
--- a/app/assets/javascripts/components/features/compose/components/upload_form.jsx
+++ b/app/javascript/mastodon/features/compose/components/upload_form.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import IconButton from '../../../components/icon_button';
diff --git a/app/assets/javascripts/components/features/compose/components/upload_progress.jsx b/app/javascript/mastodon/features/compose/components/upload_progress.js
index 8f03bb76a..bb2932a55 100644
--- a/app/assets/javascripts/components/features/compose/components/upload_progress.jsx
+++ b/app/javascript/mastodon/features/compose/components/upload_progress.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 import { Motion, spring } from 'react-motion';
 import { FormattedMessage } from 'react-intl';
diff --git a/app/assets/javascripts/components/features/compose/components/warning.jsx b/app/javascript/mastodon/features/compose/components/warning.js
index ff1989755..6ad00b691 100644
--- a/app/assets/javascripts/components/features/compose/components/warning.jsx
+++ b/app/javascript/mastodon/features/compose/components/warning.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 
 class Warning extends React.PureComponent {
diff --git a/app/assets/javascripts/components/features/compose/containers/autosuggest_account_container.jsx b/app/javascript/mastodon/features/compose/containers/autosuggest_account_container.js
index de76a364d..de76a364d 100644
--- a/app/assets/javascripts/components/features/compose/containers/autosuggest_account_container.jsx
+++ b/app/javascript/mastodon/features/compose/containers/autosuggest_account_container.js
diff --git a/app/assets/javascripts/components/features/compose/containers/autosuggest_status_container.jsx b/app/javascript/mastodon/features/compose/containers/autosuggest_status_container.js
index ef46eb09c..ef46eb09c 100644
--- a/app/assets/javascripts/components/features/compose/containers/autosuggest_status_container.jsx
+++ b/app/javascript/mastodon/features/compose/containers/autosuggest_status_container.js
diff --git a/app/assets/javascripts/components/features/compose/containers/compose_form_container.jsx b/app/javascript/mastodon/features/compose/containers/compose_form_container.js
index 892183b83..892183b83 100644
--- a/app/assets/javascripts/components/features/compose/containers/compose_form_container.jsx
+++ b/app/javascript/mastodon/features/compose/containers/compose_form_container.js
diff --git a/app/assets/javascripts/components/features/compose/containers/navigation_container.jsx b/app/javascript/mastodon/features/compose/containers/navigation_container.js
index 0006608da..0006608da 100644
--- a/app/assets/javascripts/components/features/compose/containers/navigation_container.jsx
+++ b/app/javascript/mastodon/features/compose/containers/navigation_container.js
diff --git a/app/assets/javascripts/components/features/compose/containers/privacy_dropdown_container.jsx b/app/javascript/mastodon/features/compose/containers/privacy_dropdown_container.js
index 1eee8f84c..1eee8f84c 100644
--- a/app/assets/javascripts/components/features/compose/containers/privacy_dropdown_container.jsx
+++ b/app/javascript/mastodon/features/compose/containers/privacy_dropdown_container.js
diff --git a/app/assets/javascripts/components/features/compose/containers/reply_indicator_container.jsx b/app/javascript/mastodon/features/compose/containers/reply_indicator_container.js
index 39b48f3b6..39b48f3b6 100644
--- a/app/assets/javascripts/components/features/compose/containers/reply_indicator_container.jsx
+++ b/app/javascript/mastodon/features/compose/containers/reply_indicator_container.js
diff --git a/app/assets/javascripts/components/features/compose/containers/search_container.jsx b/app/javascript/mastodon/features/compose/containers/search_container.js
index 906c0c28c..906c0c28c 100644
--- a/app/assets/javascripts/components/features/compose/containers/search_container.jsx
+++ b/app/javascript/mastodon/features/compose/containers/search_container.js
diff --git a/app/assets/javascripts/components/features/compose/containers/search_results_container.jsx b/app/javascript/mastodon/features/compose/containers/search_results_container.js
index e5911fd38..e5911fd38 100644
--- a/app/assets/javascripts/components/features/compose/containers/search_results_container.jsx
+++ b/app/javascript/mastodon/features/compose/containers/search_results_container.js
diff --git a/app/assets/javascripts/components/features/compose/containers/sensitive_button_container.jsx b/app/javascript/mastodon/features/compose/containers/sensitive_button_container.js
index c83598a7d..78e40e048 100644
--- a/app/assets/javascripts/components/features/compose/containers/sensitive_button_container.jsx
+++ b/app/javascript/mastodon/features/compose/containers/sensitive_button_container.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import TextIconButton from '../components/text_icon_button';
diff --git a/app/assets/javascripts/components/features/compose/containers/spoiler_button_container.jsx b/app/javascript/mastodon/features/compose/containers/spoiler_button_container.js
index b1c80fe19..b1c80fe19 100644
--- a/app/assets/javascripts/components/features/compose/containers/spoiler_button_container.jsx
+++ b/app/javascript/mastodon/features/compose/containers/spoiler_button_container.js
diff --git a/app/assets/javascripts/components/features/compose/containers/upload_button_container.jsx b/app/javascript/mastodon/features/compose/containers/upload_button_container.js
index 78e5312f5..78e5312f5 100644
--- a/app/assets/javascripts/components/features/compose/containers/upload_button_container.jsx
+++ b/app/javascript/mastodon/features/compose/containers/upload_button_container.js
diff --git a/app/assets/javascripts/components/features/compose/containers/upload_form_container.jsx b/app/javascript/mastodon/features/compose/containers/upload_form_container.js
index a6a202e17..a6a202e17 100644
--- a/app/assets/javascripts/components/features/compose/containers/upload_form_container.jsx
+++ b/app/javascript/mastodon/features/compose/containers/upload_form_container.js
diff --git a/app/assets/javascripts/components/features/compose/containers/upload_progress_container.jsx b/app/javascript/mastodon/features/compose/containers/upload_progress_container.js
index b0f1d4d19..b0f1d4d19 100644
--- a/app/assets/javascripts/components/features/compose/containers/upload_progress_container.jsx
+++ b/app/javascript/mastodon/features/compose/containers/upload_progress_container.js
diff --git a/app/assets/javascripts/components/features/compose/containers/warning_container.jsx b/app/javascript/mastodon/features/compose/containers/warning_container.js
index cd744ed82..bf5e6a5f8 100644
--- a/app/assets/javascripts/components/features/compose/containers/warning_container.jsx
+++ b/app/javascript/mastodon/features/compose/containers/warning_container.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { connect } from 'react-redux';
 import Warning from '../components/warning';
 import { createSelector } from 'reselect';
diff --git a/app/assets/javascripts/components/features/compose/index.jsx b/app/javascript/mastodon/features/compose/index.js
index ae1b52ca0..68d779c6c 100644
--- a/app/assets/javascripts/components/features/compose/index.jsx
+++ b/app/javascript/mastodon/features/compose/index.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import ComposeFormContainer from './containers/compose_form_container';
 import UploadFormContainer from './containers/upload_form_container';
 import NavigationContainer from './containers/navigation_container';
diff --git a/app/assets/javascripts/components/features/favourited_statuses/index.jsx b/app/javascript/mastodon/features/favourited_statuses/index.js
index bc45ace51..995f61f17 100644
--- a/app/assets/javascripts/components/features/favourited_statuses/index.jsx
+++ b/app/javascript/mastodon/features/favourited_statuses/index.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
@@ -7,6 +8,7 @@ import Column from '../ui/components/column';
 import StatusList from '../../components/status_list';
 import ColumnBackButtonSlim from '../../components/column_back_button_slim';
 import { defineMessages, injectIntl } from 'react-intl';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
 const messages = defineMessages({
   heading: { id: 'column.favourites', defaultMessage: 'Favourites' }
@@ -18,7 +20,7 @@ const mapStateToProps = state => ({
   me: state.getIn(['meta', 'me'])
 });
 
-class Favourites extends React.PureComponent {
+class Favourites extends ImmutablePureComponent {
 
   constructor (props, context) {
     super(props, context);
@@ -47,7 +49,7 @@ class Favourites extends React.PureComponent {
     return (
       <Column icon='star' heading={intl.formatMessage(messages.heading)}>
         <ColumnBackButtonSlim />
-        <StatusList {...this.props} onScrollToBottom={this.handleScrollToBottom} />
+        <StatusList {...this.props} scrollKey='favourited_statuses' onScrollToBottom={this.handleScrollToBottom} />
       </Column>
     );
   }
@@ -55,7 +57,6 @@ class Favourites extends React.PureComponent {
 }
 
 Favourites.propTypes = {
-  params: PropTypes.object.isRequired,
   dispatch: PropTypes.func.isRequired,
   statusIds: ImmutablePropTypes.list.isRequired,
   loaded: PropTypes.bool,
diff --git a/app/assets/javascripts/components/features/favourites/index.jsx b/app/javascript/mastodon/features/favourites/index.js
index bd6cf8a90..c916aa176 100644
--- a/app/assets/javascripts/components/features/favourites/index.jsx
+++ b/app/javascript/mastodon/features/favourites/index.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
@@ -7,12 +8,13 @@ import { ScrollContainer } from 'react-router-scroll';
 import AccountContainer from '../../containers/account_container';
 import Column from '../ui/components/column';
 import ColumnBackButton from '../../components/column_back_button';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
 const mapStateToProps = (state, props) => ({
   accountIds: state.getIn(['user_lists', 'favourited_by', Number(props.params.statusId)])
 });
 
-class Favourites extends React.PureComponent {
+class Favourites extends ImmutablePureComponent {
 
   componentWillMount () {
     this.props.dispatch(fetchFavourites(Number(this.props.params.statusId)));
diff --git a/app/javascript/mastodon/features/follow_requests/components/account_authorize.js b/app/javascript/mastodon/features/follow_requests/components/account_authorize.js
new file mode 100644
index 000000000..9fe464628
--- /dev/null
+++ b/app/javascript/mastodon/features/follow_requests/components/account_authorize.js
@@ -0,0 +1,51 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import Permalink from '../../../components/permalink';
+import Avatar from '../../../components/avatar';
+import DisplayName from '../../../components/display_name';
+import emojify from '../../../emoji';
+import IconButton from '../../../components/icon_button';
+import { defineMessages, injectIntl } from 'react-intl';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+
+const messages = defineMessages({
+  authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' },
+  reject: { id: 'follow_request.reject', defaultMessage: 'Reject' }
+});
+
+class AccountAuthorize extends ImmutablePureComponent {
+
+  render () {
+    const { intl, account, onAuthorize, onReject } = this.props;
+    const content = { __html: emojify(account.get('note')) };
+
+    return (
+      <div className='account-authorize__wrapper'>
+        <div className='account-authorize'>
+          <Permalink href={account.get('url')} to={`/accounts/${account.get('id')}`} className='detailed-status__display-name'>
+            <div className='account-authorize__avatar'><Avatar src={account.get('avatar')} staticSrc={account.get('avatar_static')} size={48} /></div>
+            <DisplayName account={account} />
+          </Permalink>
+
+          <div className='account__header__content' dangerouslySetInnerHTML={content} />
+        </div>
+
+        <div className='account--panel'>
+          <div className='account--panel__button'><IconButton title={intl.formatMessage(messages.authorize)} icon='check' onClick={onAuthorize} /></div>
+          <div className='account--panel__button'><IconButton title={intl.formatMessage(messages.reject)} icon='times' onClick={onReject} /></div>
+        </div>
+      </div>
+    );
+  }
+
+}
+
+AccountAuthorize.propTypes = {
+  account: ImmutablePropTypes.map.isRequired,
+  onAuthorize: PropTypes.func.isRequired,
+  onReject: PropTypes.func.isRequired,
+  intl: PropTypes.object.isRequired
+};
+
+export default injectIntl(AccountAuthorize);
diff --git a/app/assets/javascripts/components/features/follow_requests/containers/account_authorize_container.jsx b/app/javascript/mastodon/features/follow_requests/containers/account_authorize_container.js
index da1e5eaa1..da1e5eaa1 100644
--- a/app/assets/javascripts/components/features/follow_requests/containers/account_authorize_container.jsx
+++ b/app/javascript/mastodon/features/follow_requests/containers/account_authorize_container.js
diff --git a/app/assets/javascripts/components/features/follow_requests/index.jsx b/app/javascript/mastodon/features/follow_requests/index.js
index 3dc709654..c88de48c0 100644
--- a/app/assets/javascripts/components/features/follow_requests/index.jsx
+++ b/app/javascript/mastodon/features/follow_requests/index.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
@@ -8,6 +9,7 @@ import ColumnBackButtonSlim from '../../components/column_back_button_slim';
 import AccountAuthorizeContainer from './containers/account_authorize_container';
 import { fetchFollowRequests, expandFollowRequests } from '../../actions/accounts';
 import { defineMessages, injectIntl } from 'react-intl';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
 const messages = defineMessages({
   heading: { id: 'column.follow_requests', defaultMessage: 'Follow requests' }
@@ -17,7 +19,7 @@ const mapStateToProps = state => ({
   accountIds: state.getIn(['user_lists', 'follow_requests', 'items'])
 });
 
-class FollowRequests extends React.PureComponent {
+class FollowRequests extends ImmutablePureComponent {
 
   constructor (props, context) {
     super(props, context);
diff --git a/app/assets/javascripts/components/features/followers/index.jsx b/app/javascript/mastodon/features/followers/index.js
index 2b1e3719e..8a1105b55 100644
--- a/app/assets/javascripts/components/features/followers/index.jsx
+++ b/app/javascript/mastodon/features/followers/index.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
@@ -13,12 +14,13 @@ import Column from '../ui/components/column';
 import HeaderContainer from '../account_timeline/containers/header_container';
 import LoadMore from '../../components/load_more';
 import ColumnBackButton from '../../components/column_back_button';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
 const mapStateToProps = (state, props) => ({
   accountIds: state.getIn(['user_lists', 'followers', Number(props.params.accountId), 'items'])
 });
 
-class Followers extends React.PureComponent {
+class Followers extends ImmutablePureComponent {
 
   constructor (props, context) {
     super(props, context);
diff --git a/app/assets/javascripts/components/features/following/index.jsx b/app/javascript/mastodon/features/following/index.js
index 30b320917..f181fe727 100644
--- a/app/assets/javascripts/components/features/following/index.jsx
+++ b/app/javascript/mastodon/features/following/index.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
@@ -13,12 +14,13 @@ import Column from '../ui/components/column';
 import HeaderContainer from '../account_timeline/containers/header_container';
 import LoadMore from '../../components/load_more';
 import ColumnBackButton from '../../components/column_back_button';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
 const mapStateToProps = (state, props) => ({
   accountIds: state.getIn(['user_lists', 'following', Number(props.params.accountId), 'items'])
 });
 
-class Following extends React.PureComponent {
+class Following extends ImmutablePureComponent {
 
   constructor (props, context) {
     super(props, context);
diff --git a/app/assets/javascripts/components/features/generic_not_found/index.jsx b/app/javascript/mastodon/features/generic_not_found/index.js
index a7afe29b0..0290be47f 100644
--- a/app/assets/javascripts/components/features/generic_not_found/index.jsx
+++ b/app/javascript/mastodon/features/generic_not_found/index.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import Column from '../ui/components/column';
 import MissingIndicator from '../../components/missing_indicator';
 
diff --git a/app/javascript/mastodon/features/getting_started/index.js b/app/javascript/mastodon/features/getting_started/index.js
new file mode 100644
index 000000000..6bdff2fba
--- /dev/null
+++ b/app/javascript/mastodon/features/getting_started/index.js
@@ -0,0 +1,73 @@
+import React from 'react';
+import Column from '../ui/components/column';
+import ColumnLink from '../ui/components/column_link';
+import ColumnSubheading from '../ui/components/column_subheading';
+import { Link } from 'react-router';
+import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+
+const messages = defineMessages({
+  heading: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
+  public_timeline: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' },
+  navigation_subheading: { id: 'column_subheading.navigation', defaultMessage: 'Navigation'},
+  settings_subheading: { id: 'column_subheading.settings', defaultMessage: 'Settings'},
+  community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
+  preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
+  follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
+  sign_out: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
+  favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
+  blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
+  mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
+  info: { id: 'navigation_bar.info', defaultMessage: 'Extended information' }
+});
+
+const mapStateToProps = state => ({
+  me: state.getIn(['accounts', state.getIn(['meta', 'me'])])
+});
+
+class GettingStarted extends ImmutablePureComponent {
+
+  render () {
+    const { intl, me } = this.props;
+
+    let followRequests = '';
+
+    if (me.get('locked')) {
+      followRequests = <ColumnLink icon='users' text={intl.formatMessage(messages.follow_requests)} to='/follow_requests' />;
+    }
+
+    return (
+      <Column icon='asterisk' heading={intl.formatMessage(messages.heading)} hideHeadingOnMobile={true}>
+        <div className='getting-started__wrapper'>
+          <ColumnSubheading text={intl.formatMessage(messages.navigation_subheading)}/>
+          <ColumnLink icon='users' hideOnMobile={true} text={intl.formatMessage(messages.community_timeline)} to='/timelines/public/local' />
+          <ColumnLink icon='globe' hideOnMobile={true} text={intl.formatMessage(messages.public_timeline)} to='/timelines/public' />
+          <ColumnLink icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />
+          {followRequests}
+          <ColumnLink icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' />
+          <ColumnLink icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' />
+          <ColumnSubheading text={intl.formatMessage(messages.settings_subheading)}/>
+          <ColumnLink icon='book' text={intl.formatMessage(messages.info)} href='/about/more' />
+          <ColumnLink icon='cog' text={intl.formatMessage(messages.preferences)} href='/settings/preferences' />
+          <ColumnLink icon='sign-out' text={intl.formatMessage(messages.sign_out)} href='/auth/sign_out' method='delete' />
+        </div>
+
+        <div className='scrollable optionally-scrollable' style={{ display: 'flex', flexDirection: 'column' }}>
+          <div className='static-content getting-started'>
+            <p><FormattedMessage id='getting_started.open_source_notice' defaultMessage='Mastodon is open source software. You can contribute or report issues on GitHub at {github}. {apps}.' values={{ github: <a href="https://github.com/tootsuite/mastodon" target="_blank">tootsuite/mastodon</a>, apps: <a href="https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md" target="_blank"><FormattedMessage id='getting_started.apps' defaultMessage='Various apps are available' /></a> }} /></p>
+          </div>
+        </div>
+      </Column>
+    );
+  }
+}
+
+GettingStarted.propTypes = {
+  intl: PropTypes.object.isRequired,
+  me: ImmutablePropTypes.map.isRequired
+};
+
+export default connect(mapStateToProps)(injectIntl(GettingStarted));
diff --git a/app/assets/javascripts/components/features/hashtag_timeline/index.jsx b/app/javascript/mastodon/features/hashtag_timeline/index.js
index 0575e9214..f5134decf 100644
--- a/app/assets/javascripts/components/features/hashtag_timeline/index.jsx
+++ b/app/javascript/mastodon/features/hashtag_timeline/index.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import StatusListContainer from '../ui/containers/status_list_container';
diff --git a/app/assets/javascripts/components/features/home_timeline/components/column_settings.jsx b/app/javascript/mastodon/features/home_timeline/components/column_settings.js
index 81a1a0e5b..460221fc3 100644
--- a/app/assets/javascripts/components/features/home_timeline/components/column_settings.jsx
+++ b/app/javascript/mastodon/features/home_timeline/components/column_settings.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
diff --git a/app/assets/javascripts/components/features/home_timeline/components/setting_text.jsx b/app/javascript/mastodon/features/home_timeline/components/setting_text.js
index 90b4aeb94..dfa2939b7 100644
--- a/app/assets/javascripts/components/features/home_timeline/components/setting_text.jsx
+++ b/app/javascript/mastodon/features/home_timeline/components/setting_text.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 
diff --git a/app/assets/javascripts/components/features/home_timeline/containers/column_settings_container.jsx b/app/javascript/mastodon/features/home_timeline/containers/column_settings_container.js
index 3b3ce19bc..3b3ce19bc 100644
--- a/app/assets/javascripts/components/features/home_timeline/containers/column_settings_container.jsx
+++ b/app/javascript/mastodon/features/home_timeline/containers/column_settings_container.js
diff --git a/app/assets/javascripts/components/features/home_timeline/index.jsx b/app/javascript/mastodon/features/home_timeline/index.js
index 52b94690d..d7c438122 100644
--- a/app/assets/javascripts/components/features/home_timeline/index.jsx
+++ b/app/javascript/mastodon/features/home_timeline/index.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import StatusListContainer from '../ui/containers/status_list_container';
diff --git a/app/assets/javascripts/components/features/mutes/index.jsx b/app/javascript/mastodon/features/mutes/index.js
index 0310fa7f2..884b3b3e7 100644
--- a/app/assets/javascripts/components/features/mutes/index.jsx
+++ b/app/javascript/mastodon/features/mutes/index.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
@@ -8,6 +9,7 @@ import ColumnBackButtonSlim from '../../components/column_back_button_slim';
 import AccountContainer from '../../containers/account_container';
 import { fetchMutes, expandMutes } from '../../actions/mutes';
 import { defineMessages, injectIntl } from 'react-intl';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
 const messages = defineMessages({
   heading: { id: 'column.mutes', defaultMessage: 'Muted users' }
@@ -17,7 +19,7 @@ const mapStateToProps = state => ({
   accountIds: state.getIn(['user_lists', 'mutes', 'items'])
 });
 
-class Mutes extends React.PureComponent {
+class Mutes extends ImmutablePureComponent {
 
   constructor (props, context) {
     super(props, context);
diff --git a/app/assets/javascripts/components/features/notifications/components/clear_column_button.jsx b/app/javascript/mastodon/features/notifications/components/clear_column_button.js
index 206b05f91..a948bff46 100644
--- a/app/assets/javascripts/components/features/notifications/components/clear_column_button.jsx
+++ b/app/javascript/mastodon/features/notifications/components/clear_column_button.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl } from 'react-intl';
 
diff --git a/app/assets/javascripts/components/features/notifications/components/column_settings.jsx b/app/javascript/mastodon/features/notifications/components/column_settings.js
index 30063010c..7d52b7dcd 100644
--- a/app/assets/javascripts/components/features/notifications/components/column_settings.jsx
+++ b/app/javascript/mastodon/features/notifications/components/column_settings.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
diff --git a/app/assets/javascripts/components/features/notifications/components/notification.jsx b/app/javascript/mastodon/features/notifications/components/notification.js
index 34dd76bb7..f54a65747 100644
--- a/app/assets/javascripts/components/features/notifications/components/notification.jsx
+++ b/app/javascript/mastodon/features/notifications/components/notification.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import StatusContainer from '../../../containers/status_container';
 import AccountContainer from '../../../containers/account_container';
@@ -5,8 +6,9 @@ import { FormattedMessage } from 'react-intl';
 import Permalink from '../../../components/permalink';
 import emojify from '../../../emoji';
 import escapeTextContentForBrowser from 'escape-html';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
-class Notification extends React.PureComponent {
+class Notification extends ImmutablePureComponent {
 
   renderFollow (account, link) {
     return (
diff --git a/app/assets/javascripts/components/features/notifications/components/setting_toggle.jsx b/app/javascript/mastodon/features/notifications/components/setting_toggle.js
index e9bca5928..080804a40 100644
--- a/app/assets/javascripts/components/features/notifications/components/setting_toggle.jsx
+++ b/app/javascript/mastodon/features/notifications/components/setting_toggle.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import Toggle from 'react-toggle';
diff --git a/app/assets/javascripts/components/features/notifications/containers/column_settings_container.jsx b/app/javascript/mastodon/features/notifications/containers/column_settings_container.js
index bc24c75e0..bc24c75e0 100644
--- a/app/assets/javascripts/components/features/notifications/containers/column_settings_container.jsx
+++ b/app/javascript/mastodon/features/notifications/containers/column_settings_container.js
diff --git a/app/assets/javascripts/components/features/notifications/containers/notification_container.jsx b/app/javascript/mastodon/features/notifications/containers/notification_container.js
index 4ca1b1b7b..4ca1b1b7b 100644
--- a/app/assets/javascripts/components/features/notifications/containers/notification_container.jsx
+++ b/app/javascript/mastodon/features/notifications/containers/notification_container.js
diff --git a/app/assets/javascripts/components/features/notifications/index.jsx b/app/javascript/mastodon/features/notifications/index.js
index da3ce2f62..989013cc7 100644
--- a/app/assets/javascripts/components/features/notifications/index.jsx
+++ b/app/javascript/mastodon/features/notifications/index.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
diff --git a/app/assets/javascripts/components/features/public_timeline/index.jsx b/app/javascript/mastodon/features/public_timeline/index.js
index 53be13686..3b270c62f 100644
--- a/app/assets/javascripts/components/features/public_timeline/index.jsx
+++ b/app/javascript/mastodon/features/public_timeline/index.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import StatusListContainer from '../ui/containers/status_list_container';
diff --git a/app/assets/javascripts/components/features/reblogs/index.jsx b/app/javascript/mastodon/features/reblogs/index.js
index 5e5671422..48df8451d 100644
--- a/app/assets/javascripts/components/features/reblogs/index.jsx
+++ b/app/javascript/mastodon/features/reblogs/index.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
@@ -7,12 +8,13 @@ import { ScrollContainer } from 'react-router-scroll';
 import AccountContainer from '../../containers/account_container';
 import Column from '../ui/components/column';
 import ColumnBackButton from '../../components/column_back_button';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
 const mapStateToProps = (state, props) => ({
   accountIds: state.getIn(['user_lists', 'reblogged_by', Number(props.params.statusId)])
 });
 
-class Reblogs extends React.PureComponent {
+class Reblogs extends ImmutablePureComponent {
 
   componentWillMount () {
     this.props.dispatch(fetchReblogs(Number(this.props.params.statusId)));
diff --git a/app/assets/javascripts/components/features/report/components/status_check_box.jsx b/app/javascript/mastodon/features/report/components/status_check_box.js
index bc866616a..85f792479 100644
--- a/app/assets/javascripts/components/features/report/components/status_check_box.jsx
+++ b/app/javascript/mastodon/features/report/components/status_check_box.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import emojify from '../../../emoji';
diff --git a/app/assets/javascripts/components/features/report/containers/status_check_box_container.jsx b/app/javascript/mastodon/features/report/containers/status_check_box_container.js
index 67ce9d9f3..67ce9d9f3 100644
--- a/app/assets/javascripts/components/features/report/containers/status_check_box_container.jsx
+++ b/app/javascript/mastodon/features/report/containers/status_check_box_container.js
diff --git a/app/assets/javascripts/components/features/report/index.jsx b/app/javascript/mastodon/features/report/index.js
index 6e3cfcb2a..661fffe56 100644
--- a/app/assets/javascripts/components/features/report/index.jsx
+++ b/app/javascript/mastodon/features/report/index.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { connect } from 'react-redux';
 import { cancelReport, changeReportComment, submitReport } from '../../actions/reports';
 import { fetchAccountTimeline } from '../../actions/accounts';
diff --git a/app/assets/javascripts/components/features/status/components/action_bar.jsx b/app/javascript/mastodon/features/status/components/action_bar.js
index 1e0b3f74d..384b47c8f 100644
--- a/app/assets/javascripts/components/features/status/components/action_bar.jsx
+++ b/app/javascript/mastodon/features/status/components/action_bar.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 import IconButton from '../../../components/icon_button';
 import ImmutablePropTypes from 'react-immutable-proptypes';
diff --git a/app/assets/javascripts/components/features/status/components/card.jsx b/app/javascript/mastodon/features/status/components/card.js
index a5ce7f08a..9e7d4f884 100644
--- a/app/assets/javascripts/components/features/status/components/card.jsx
+++ b/app/javascript/mastodon/features/status/components/card.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 
 const hostStyle = {
diff --git a/app/assets/javascripts/components/features/status/components/detailed_status.jsx b/app/javascript/mastodon/features/status/components/detailed_status.js
index 0e2a7c17e..913a186b9 100644
--- a/app/assets/javascripts/components/features/status/components/detailed_status.jsx
+++ b/app/javascript/mastodon/features/status/components/detailed_status.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import Avatar from '../../../components/avatar';
@@ -9,8 +10,9 @@ import AttachmentList from '../../../components/attachment_list';
 import { Link } from 'react-router';
 import { FormattedDate, FormattedNumber } from 'react-intl';
 import CardContainer from '../containers/card_container';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
-class DetailedStatus extends React.PureComponent {
+class DetailedStatus extends ImmutablePureComponent {
 
   constructor (props, context) {
     super(props, context);
diff --git a/app/assets/javascripts/components/features/status/containers/card_container.jsx b/app/javascript/mastodon/features/status/containers/card_container.js
index 5c8bfeec2..5c8bfeec2 100644
--- a/app/assets/javascripts/components/features/status/containers/card_container.jsx
+++ b/app/javascript/mastodon/features/status/containers/card_container.js
diff --git a/app/assets/javascripts/components/features/status/index.jsx b/app/javascript/mastodon/features/status/index.js
index 595df251c..2e8c9e56a 100644
--- a/app/assets/javascripts/components/features/status/index.jsx
+++ b/app/javascript/mastodon/features/status/index.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
@@ -31,6 +32,7 @@ import StatusContainer from '../../containers/status_container';
 import { openModal } from '../../actions/modal';
 import { isMobile } from '../../is_mobile'
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
 const messages = defineMessages({
   deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
@@ -52,7 +54,7 @@ const makeMapStateToProps = () => {
   return mapStateToProps;
 };
 
-class Status extends React.PureComponent {
+class Status extends ImmutablePureComponent {
 
   constructor (props, context) {
     super(props, context);
diff --git a/app/assets/javascripts/components/features/ui/components/boost_modal.jsx b/app/javascript/mastodon/features/ui/components/boost_modal.js
index 3bd82ceee..d6000fe4e 100644
--- a/app/assets/javascripts/components/features/ui/components/boost_modal.jsx
+++ b/app/javascript/mastodon/features/ui/components/boost_modal.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
@@ -7,12 +8,13 @@ import StatusContent from '../../../components/status_content';
 import Avatar from '../../../components/avatar';
 import RelativeTimestamp from '../../../components/relative_timestamp';
 import DisplayName from '../../../components/display_name';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
 const messages = defineMessages({
   reblog: { id: 'status.reblog', defaultMessage: 'Boost' }
 });
 
-class BoostModal extends React.PureComponent {
+class BoostModal extends ImmutablePureComponent {
 
   constructor (props, context) {
     super(props, context);
diff --git a/app/assets/javascripts/components/features/ui/components/column.jsx b/app/javascript/mastodon/features/ui/components/column.js
index aa09d0fd2..fcb197573 100644
--- a/app/assets/javascripts/components/features/ui/components/column.jsx
+++ b/app/javascript/mastodon/features/ui/components/column.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import ColumnHeader from './column_header';
 import PropTypes from 'prop-types';
 
@@ -35,10 +36,11 @@ class Column extends React.PureComponent {
     super(props, context);
     this.handleHeaderClick = this.handleHeaderClick.bind(this);
     this.handleWheel = this.handleWheel.bind(this);
+    this.setRef = this.setRef.bind(this);
   }
 
   handleHeaderClick () {
-    const scrollable = ReactDOM.findDOMNode(this).querySelector('.scrollable');
+    const scrollable = this.node.querySelector('.scrollable');
     if (!scrollable) {
       return;
     }
@@ -51,6 +53,10 @@ class Column extends React.PureComponent {
     }
   }
 
+  setRef (c) {
+    this.node = c;
+  }
+
   render () {
     const { heading, icon, children, active, hideHeadingOnMobile } = this.props;
 
@@ -62,7 +68,12 @@ class Column extends React.PureComponent {
       header = <ColumnHeader icon={icon} active={active} type={heading} onClick={this.handleHeaderClick} hideOnMobile={hideHeadingOnMobile} columnHeaderId={columnHeaderId}/>;
     }
     return (
-      <div role='region' aria-labelledby={columnHeaderId} className='column' onWheel={this.handleWheel}>
+      <div
+        ref={this.setRef}
+        role='region'
+        aria-labelledby={columnHeaderId}
+        className='column'
+        onWheel={this.handleWheel}>
         {header}
         {children}
       </div>
diff --git a/app/assets/javascripts/components/features/ui/components/column_header.jsx b/app/javascript/mastodon/features/ui/components/column_header.js
index 7ccd72e0b..2701cd57d 100644
--- a/app/assets/javascripts/components/features/ui/components/column_header.jsx
+++ b/app/javascript/mastodon/features/ui/components/column_header.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import PropTypes from 'prop-types'
 
 class ColumnHeader extends React.PureComponent {
diff --git a/app/assets/javascripts/components/features/ui/components/column_link.jsx b/app/javascript/mastodon/features/ui/components/column_link.js
index 820e4246a..cffe796ba 100644
--- a/app/assets/javascripts/components/features/ui/components/column_link.jsx
+++ b/app/javascript/mastodon/features/ui/components/column_link.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 import { Link } from 'react-router';
 
diff --git a/app/assets/javascripts/components/features/ui/components/column_subheading.jsx b/app/javascript/mastodon/features/ui/components/column_subheading.js
index 061c8be6c..8160c4aa3 100644
--- a/app/assets/javascripts/components/features/ui/components/column_subheading.jsx
+++ b/app/javascript/mastodon/features/ui/components/column_subheading.js
@@ -1,12 +1,13 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 
 const ColumnSubheading = ({ text }) => {
-    return (
-      <div className='column-subheading'>
-        {text}
-      </div>
-    );
-  };
+  return (
+    <div className='column-subheading'>
+      {text}
+    </div>
+  );
+};
 
 ColumnSubheading.propTypes = {
   text: PropTypes.string.isRequired,
diff --git a/app/assets/javascripts/components/features/ui/components/columns_area.jsx b/app/javascript/mastodon/features/ui/components/columns_area.js
index 360a759ae..05f9f3fb5 100644
--- a/app/assets/javascripts/components/features/ui/components/columns_area.jsx
+++ b/app/javascript/mastodon/features/ui/components/columns_area.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 
 class ColumnsArea extends React.PureComponent {
diff --git a/app/assets/javascripts/components/features/ui/components/confirmation_modal.jsx b/app/javascript/mastodon/features/ui/components/confirmation_modal.js
index 914c12f82..499993207 100644
--- a/app/assets/javascripts/components/features/ui/components/confirmation_modal.jsx
+++ b/app/javascript/mastodon/features/ui/components/confirmation_modal.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import Button from '../../../components/button';
diff --git a/app/assets/javascripts/components/features/ui/components/media_modal.jsx b/app/javascript/mastodon/features/ui/components/media_modal.js
index 02a577500..a8fb3858a 100644
--- a/app/assets/javascripts/components/features/ui/components/media_modal.jsx
+++ b/app/javascript/mastodon/features/ui/components/media_modal.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import LoadingIndicator from '../../../components/loading_indicator';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
@@ -5,12 +6,13 @@ import ExtendedVideoPlayer from '../../../components/extended_video_player';
 import ImageLoader from 'react-imageloader';
 import { defineMessages, injectIntl } from 'react-intl';
 import IconButton from '../../../components/icon_button';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
 const messages = defineMessages({
   close: { id: 'lightbox.close', defaultMessage: 'Close' }
 });
 
-class MediaModal extends React.PureComponent {
+class MediaModal extends ImmutablePureComponent {
 
   constructor (props, context) {
     super(props, context);
diff --git a/app/assets/javascripts/components/features/ui/components/modal_root.jsx b/app/javascript/mastodon/features/ui/components/modal_root.js
index 23057715c..5cde65907 100644
--- a/app/assets/javascripts/components/features/ui/components/modal_root.jsx
+++ b/app/javascript/mastodon/features/ui/components/modal_root.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 import MediaModal from './media_modal';
 import OnboardingModal from './onboarding_modal';
diff --git a/app/assets/javascripts/components/features/ui/components/onboarding_modal.jsx b/app/javascript/mastodon/features/ui/components/onboarding_modal.js
index 4c2c55f93..7cdd3527a 100644
--- a/app/assets/javascripts/components/features/ui/components/onboarding_modal.jsx
+++ b/app/javascript/mastodon/features/ui/components/onboarding_modal.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
diff --git a/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx b/app/javascript/mastodon/features/ui/components/tabs_bar.js
index 316b4bf7d..b6a30bc11 100644
--- a/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx
+++ b/app/javascript/mastodon/features/ui/components/tabs_bar.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import { Link } from 'react-router';
 import { FormattedMessage } from 'react-intl';
 
diff --git a/app/assets/javascripts/components/features/ui/components/upload_area.jsx b/app/javascript/mastodon/features/ui/components/upload_area.js
index 3a933398b..c5710ee69 100644
--- a/app/assets/javascripts/components/features/ui/components/upload_area.jsx
+++ b/app/javascript/mastodon/features/ui/components/upload_area.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import PropTypes from 'prop-types';
 import { Motion, spring } from 'react-motion';
 import { FormattedMessage } from 'react-intl';
diff --git a/app/assets/javascripts/components/features/ui/components/video_modal.jsx b/app/javascript/mastodon/features/ui/components/video_modal.js
index d98b42882..8e2e4a533 100644
--- a/app/assets/javascripts/components/features/ui/components/video_modal.jsx
+++ b/app/javascript/mastodon/features/ui/components/video_modal.js
@@ -1,15 +1,17 @@
+import React from 'react';
 import LoadingIndicator from '../../../components/loading_indicator';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import ExtendedVideoPlayer from '../../../components/extended_video_player';
 import { defineMessages, injectIntl } from 'react-intl';
 import IconButton from '../../../components/icon_button';
+import ImmutablePureComponent from 'react-immutable-pure-component';
 
 const messages = defineMessages({
   close: { id: 'lightbox.close', defaultMessage: 'Close' }
 });
 
-class VideoModal extends React.PureComponent {
+class VideoModal extends ImmutablePureComponent {
 
   render () {
     const { media, intl, time, onClose } = this.props;
diff --git a/app/assets/javascripts/components/features/ui/containers/loading_bar_container.jsx b/app/javascript/mastodon/features/ui/containers/loading_bar_container.js
index 6c4e73e38..6c4e73e38 100644
--- a/app/assets/javascripts/components/features/ui/containers/loading_bar_container.jsx
+++ b/app/javascript/mastodon/features/ui/containers/loading_bar_container.js
diff --git a/app/assets/javascripts/components/features/ui/containers/modal_container.jsx b/app/javascript/mastodon/features/ui/containers/modal_container.js
index 26d77818c..26d77818c 100644
--- a/app/assets/javascripts/components/features/ui/containers/modal_container.jsx
+++ b/app/javascript/mastodon/features/ui/containers/modal_container.js
diff --git a/app/assets/javascripts/components/features/ui/containers/notifications_container.jsx b/app/javascript/mastodon/features/ui/containers/notifications_container.js
index 529ebf6c8..529ebf6c8 100644
--- a/app/assets/javascripts/components/features/ui/containers/notifications_container.jsx
+++ b/app/javascript/mastodon/features/ui/containers/notifications_container.js
diff --git a/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx b/app/javascript/mastodon/features/ui/containers/status_list_container.js
index 1599000b5..1599000b5 100644
--- a/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx
+++ b/app/javascript/mastodon/features/ui/containers/status_list_container.js
diff --git a/app/assets/javascripts/components/features/ui/index.jsx b/app/javascript/mastodon/features/ui/index.js
index b402639ce..d096cb882 100644
--- a/app/assets/javascripts/components/features/ui/index.jsx
+++ b/app/javascript/mastodon/features/ui/index.js
@@ -1,3 +1,4 @@
+import React from 'react';
 import ColumnsArea from './components/columns_area';
 import NotificationsContainer from './containers/notifications_container';
 import PropTypes from 'prop-types';
@@ -15,6 +16,8 @@ import { refreshTimeline } from '../../actions/timelines';
 import { refreshNotifications } from '../../actions/notifications';
 import UploadArea from './components/upload_area';
 
+const noOp = () => false;
+
 class UI extends React.PureComponent {
 
   constructor (props, context) {
@@ -135,8 +138,8 @@ class UI extends React.PureComponent {
       mountedColumns = (
         <ColumnsArea>
           <Compose withHeader={true} />
-          <HomeTimeline shouldUpdateScroll={() => false} />
-          <Notifications shouldUpdateScroll={() => false} />
+          <HomeTimeline shouldUpdateScroll={noOp} />
+          <Notifications shouldUpdateScroll={noOp} />
           <div style={{display: 'flex', flex: '1 1 auto', position: 'relative'}}>{children}</div>
         </ColumnsArea>
       );
diff --git a/app/assets/javascripts/components/is_mobile.jsx b/app/javascript/mastodon/is_mobile.js
index 992e63727..992e63727 100644
--- a/app/assets/javascripts/components/is_mobile.jsx
+++ b/app/javascript/mastodon/is_mobile.js
diff --git a/app/assets/javascripts/components/link_header.jsx b/app/javascript/mastodon/link_header.js
index b872dc24a..b872dc24a 100644
--- a/app/assets/javascripts/components/link_header.jsx
+++ b/app/javascript/mastodon/link_header.js
diff --git a/app/assets/javascripts/components/locales/ar.jsx b/app/javascript/mastodon/locales/ar.json
index 06a83b609..b4f05a73e 100644
--- a/app/assets/javascripts/components/locales/ar.jsx
+++ b/app/javascript/mastodon/locales/ar.json
@@ -1,12 +1,4 @@
-/**
-  * ملاحظة للمساهمين و المساهمات :
-  * لجعل مهمة المساهمين الآخرين أسهل، رجاءا تذكر :
-  * 1. إضافة سلسلة جديدة هنا؛ و
-  * 2. لإزالة السلاسل القديمة التي لم تعد هناك حاجة إليها. و
-  * 3. لفرز السلاسل تبعا للأبجدية
-  * شكر!
- */
-const ar = {
+{
   "account.block": "حظر @{name}",
   "account.disclaimer": "هذا المستخدم من مثيل خادم آخر. قد يكون هذا الرقم أكبر.",
   "account.edit_profile": "تعديل الملف الشخصي",
@@ -60,16 +52,14 @@ const ar = {
   "emoji_button.travel": "أماكن و أسفار",
   "empty_column.community": "الخط الزمني المحلي فارغ. اكتب شيئا ما للعامة كبداية.",
   "empty_column.hashtag": "ليس هناك بعدُ أي محتوى ذو علاقة بهذا الوسم.",
-  "empty_column.home.public_timeline": "الخيط العام",
   "empty_column.home": "إنك لا تتبع بعد أي شخص إلى حد الآن. زر {public} أو استخدام حقل البحث لكي تبدأ على التعرف على مستخدمين آخرين.",
+  "empty_column.home.public_timeline": "الخيط العام",
   "empty_column.notifications": "لم تتلق أي إشعار بعدُ. تفاعل مع المستخدمين الآخرين لإنشاء محادثة.",
   "empty_column.public": "لا يوجد شيء هنا ! قم بتحرير شيء ما بشكل عام، أو اتبع مستخدمين آخرين في الخوادم المثيلة الأخرى لملء خيط المحادثات العام.",
   "follow_request.authorize": "ترخيص",
   "follow_request.reject": "رفض",
   "getting_started.apps": "عدة تطبيقات مختلفة متوفرة",
   "getting_started.heading": "إستعدّ للبدء",
-  "getting_started.about_addressing": "يمكنك متابعة الأشخاص إذا كنت تعرف اسم المستخدم الخاص بهم والنطاق الذي هم عليه عن طريق إدخال عنوان شبيه بالبريد الإلكتروني في الحقل المخصص للبحث.",
-  "getting_started.about_shortcuts": "إذا كان المستخدم المستهدف في نفس النطاق الذي تستخدمه، فإسم المستخدم وحده يكفي. وتنطبق نفس القاعدة على ذكر الأشخاص في المنشورات و التبويقات.",
   "getting_started.open_source_notice": "ماستدون برنامج مفتوح المصدر. يمكنك المساهمة، أو الإبلاغ عن تقارير الأخطاء، على GitHub {github}. {apps}.",
   "home.column_settings.advanced": "متقدمة",
   "home.column_settings.basic": "أساسية",
@@ -84,27 +74,80 @@ const ar = {
   "navigation_bar.blocks": "الحسابات المحجوبة",
   "navigation_bar.community_timeline": "الخيط العام المحلي",
   "navigation_bar.edit_profile": "تعديل الملف الشخصي",
+  "navigation_bar.favourites": "Favourites",
+  "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.info": "Extended information",
+  "navigation_bar.logout": "خروج",
+  "navigation_bar.mutes": "Muted users",
   "navigation_bar.preferences": "التفضيلات",
-  "navigation_bar.community_timeline": "الخيط العام المحلي",
   "navigation_bar.public_timeline": "الخيط العام الموحد",
-  "navigation_bar.logout": "خروج",
+  "notification.favourite": "{name} أعجب بمنشورك",
+  "notification.follow": "{name} يتبعك",
+  "notification.reblog": "{name} قام بترقية تبويقك",
+  "notifications.clear": "Clear notifications",
+  "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
+  "notifications.column_settings.alert": "إشعارات سطح المكتب",
+  "notifications.column_settings.favourite": "المُفَضَّلة :",
+  "notifications.column_settings.follow": "متابعُون جُدُد :",
+  "notifications.column_settings.mention": "الإشارات :",
+  "notifications.column_settings.reblog": "الترقيّات:",
+  "notifications.column_settings.show": "إعرِضها في عمود",
+  "notifications.column_settings.sound": "Play sound",
+  "notifications.settings": "Column settings",
+  "onboarding.done": "Done",
+  "onboarding.next": "Next",
+  "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+  "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
+  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to Mastodon!",
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
+  "onboarding.page_six.almost_done": "Almost done...",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
+  "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "community guidelines",
+  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
+  "onboarding.page_six.various_app": "mobile apps",
+  "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+  "onboarding.skip": "Skip",
+  "privacy.change": "Adjust status privacy",
+  "privacy.direct.long": "Post to mentioned users only",
+  "privacy.direct.short": "Direct",
+  "privacy.private.long": "Post to followers only",
+  "privacy.private.short": "Followers-only",
+  "privacy.public.long": "Post to public timelines",
+  "privacy.public.short": "Public",
+  "privacy.unlisted.long": "Do not show in public timelines",
+  "privacy.unlisted.short": "Unlisted",
   "reply_indicator.cancel": "إلغاء",
+  "report.heading": "New report",
+  "report.placeholder": "Additional comments",
+  "report.submit": "Submit",
+  "report.target": "Reporting",
   "search.placeholder": "ابحث",
-  "search.account": "حساب",
-  "search.hashtag": "وسم",
-  "status.mention": "أذكُر @{name}",
+  "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
+  "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "إحذف",
-  "status.reply": "ردّ",
-  "status.reblog": "رَقِّي",
   "status.favourite": "أضف إلى المفضلة",
+  "status.load_more": "Load more",
+  "status.media_hidden": "Media hidden",
+  "status.mention": "أذكُر @{name}",
+  "status.open": "وسع هذه المشاركة",
+  "status.reblog": "رَقِّي",
   "status.reblogged_by": "{name} رقى",
-  "status.sensitive_warning": "محتوى حساس",
+  "status.reply": "ردّ",
+  "status.replyAll": "Reply to thread",
+  "status.report": "إبلِغ عن @{name}",
   "status.sensitive_toggle": "اضغط للعرض",
-  "status.show_more": "أظهر المزيد",
+  "status.sensitive_warning": "محتوى حساس",
   "status.show_less": "إعرض أقلّ",
-  "status.open": "وسع هذه المشاركة",
-  "status.report": "إبلِغ عن @{name}",
+  "status.show_more": "أظهر المزيد",
   "tabs_bar.compose": "تحرير",
+  "tabs_bar.federated_timeline": "Federated",
   "tabs_bar.home": "الرئيسية",
   "tabs_bar.mentions": "الإشارات",
   "tabs_bar.public": "الخيط العام الموحد",
@@ -125,7 +168,5 @@ const ar = {
   "video_player.toggle_sound": "تبديل الصوت",
   "video_player.toggle_visible": "إظهار / إخفاء الفيديو",
   "video_player.expand": "وسّع الفيديو",
-  "video_player.video_error": "تعذر تشغيل الفيديو",
-};
-
-export default ar;
+  "video_player.video_error": "تعذر تشغيل الفيديو"
+}
diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json
new file mode 100644
index 000000000..38dbb8b61
--- /dev/null
+++ b/app/javascript/mastodon/locales/bg.json
@@ -0,0 +1,163 @@
+{
+  "account.block": "Блокирай",
+  "account.disclaimer": "This user is from another instance. This number may be larger.",
+  "account.edit_profile": "Редактирай профила си",
+  "account.follow": "Последвай",
+  "account.followers": "Последователи",
+  "account.follows": "Следвам",
+  "account.follows_you": "Твой последовател",
+  "account.mention": "Споменаване",
+  "account.mute": "Mute @{name}",
+  "account.posts": "Публикации",
+  "account.report": "Report @{name}",
+  "account.requested": "В очакване на одобрение",
+  "account.unblock": "Не блокирай",
+  "account.unfollow": "Не следвай",
+  "account.unmute": "Unmute @{name}",
+  "boost_modal.combo": "You can press {combo} to skip this next time",
+  "column.blocks": "Blocked users",
+  "column.community": "Local timeline",
+  "column.favourites": "Favourites",
+  "column.follow_requests": "Follow requests",
+  "column.home": "Начало",
+  "column.mutes": "Muted users",
+  "column.notifications": "Известия",
+  "column.public": "Публичен канал",
+  "column_back_button.label": "Назад",
+  "column_subheading.navigation": "Navigation",
+  "column_subheading.settings": "Settings",
+  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+  "compose_form.lock_disclaimer.lock": "locked",
+  "compose_form.placeholder": "Какво си мислиш?",
+  "compose_form.privacy_disclaimer": "Поверителни публикации ще бъдат изпратени до споменатите потребители на {domains}. Доверяваш ли се на {domainsCount, plural, one {that server} other {those servers}}, че няма да издаде твоята публикация?",
+  "compose_form.publish": "Раздумай",
+  "compose_form.sensitive": "Отбележи съдържанието като деликатно",
+  "compose_form.spoiler": "Скрий текста зад предупреждение",
+  "compose_form.spoiler_placeholder": "Content warning",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.confirm": "Block",
+  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.message": "Are you sure you want to mute {name}?",
+  "emoji_button.activity": "Activity",
+  "emoji_button.flags": "Flags",
+  "emoji_button.food": "Food & Drink",
+  "emoji_button.label": "Insert emoji",
+  "emoji_button.nature": "Nature",
+  "emoji_button.objects": "Objects",
+  "emoji_button.people": "People",
+  "emoji_button.search": "Search...",
+  "emoji_button.symbols": "Symbols",
+  "emoji_button.travel": "Travel & Places",
+  "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
+  "empty_column.hashtag": "There is nothing in this hashtag yet.",
+  "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.",
+  "empty_column.home.public_timeline": "the public timeline",
+  "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
+  "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up",
+  "follow_request.authorize": "Authorize",
+  "follow_request.reject": "Reject",
+  "getting_started.apps": "Various apps are available",
+  "getting_started.heading": "Първи стъпки",
+  "getting_started.open_source_notice": "Mastodon е софтуер с отворен код. Можеш да помогнеш или да докладваш за проблеми в Github: {github}.",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.basic": "Basic",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_reblogs": "Show boosts",
+  "home.column_settings.show_replies": "Show replies",
+  "home.settings": "Column settings",
+  "lightbox.close": "Затвори",
+  "loading_indicator.label": "Зареждане...",
+  "media_gallery.toggle_visible": "Toggle visibility",
+  "missing_indicator.label": "Not found",
+  "navigation_bar.blocks": "Blocked users",
+  "navigation_bar.community_timeline": "Local timeline",
+  "navigation_bar.edit_profile": "Редактирай профил",
+  "navigation_bar.favourites": "Favourites",
+  "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.info": "Extended information",
+  "navigation_bar.logout": "Излизане",
+  "navigation_bar.mutes": "Muted users",
+  "navigation_bar.preferences": "Предпочитания",
+  "navigation_bar.public_timeline": "Публичен канал",
+  "notification.favourite": "{name} хареса твоята публикация",
+  "notification.follow": "{name} те последва",
+  "notification.reblog": "{name} сподели твоята публикация",
+  "notifications.clear": "Clear notifications",
+  "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
+  "notifications.column_settings.alert": "Десктоп известия",
+  "notifications.column_settings.favourite": "Предпочитани:",
+  "notifications.column_settings.follow": "Нови последователи:",
+  "notifications.column_settings.mention": "Споменавания:",
+  "notifications.column_settings.reblog": "Споделяния:",
+  "notifications.column_settings.show": "Покажи в колона",
+  "notifications.column_settings.sound": "Play sound",
+  "notifications.settings": "Column settings",
+  "onboarding.done": "Done",
+  "onboarding.next": "Next",
+  "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+  "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
+  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to Mastodon!",
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
+  "onboarding.page_six.almost_done": "Almost done...",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
+  "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "community guidelines",
+  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
+  "onboarding.page_six.various_app": "mobile apps",
+  "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+  "onboarding.skip": "Skip",
+  "privacy.change": "Adjust status privacy",
+  "privacy.direct.long": "Post to mentioned users only",
+  "privacy.direct.short": "Direct",
+  "privacy.private.long": "Post to followers only",
+  "privacy.private.short": "Followers-only",
+  "privacy.public.long": "Post to public timelines",
+  "privacy.public.short": "Public",
+  "privacy.unlisted.long": "Do not show in public timelines",
+  "privacy.unlisted.short": "Unlisted",
+  "reply_indicator.cancel": "Отказ",
+  "report.heading": "New report",
+  "report.placeholder": "Additional comments",
+  "report.submit": "Submit",
+  "report.target": "Reporting",
+  "search.placeholder": "Търсене",
+  "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
+  "status.cannot_reblog": "This post cannot be boosted",
+  "status.delete": "Изтриване",
+  "status.favourite": "Предпочитани",
+  "status.load_more": "Load more",
+  "status.media_hidden": "Media hidden",
+  "status.mention": "Споменаване",
+  "status.open": "Expand this status",
+  "status.reblog": "Споделяне",
+  "status.reblogged_by": "{name} сподели",
+  "status.reply": "Отговор",
+  "status.replyAll": "Reply to thread",
+  "status.report": "Report @{name}",
+  "status.sensitive_toggle": "Покажи",
+  "status.sensitive_warning": "Деликатно съдържание",
+  "status.show_less": "Show less",
+  "status.show_more": "Show more",
+  "tabs_bar.compose": "Съставяне",
+  "tabs_bar.federated_timeline": "Federated",
+  "tabs_bar.home": "Начало",
+  "tabs_bar.local_timeline": "Local",
+  "tabs_bar.notifications": "Известия",
+  "upload_area.title": "Drag & drop to upload",
+  "upload_button.label": "Добави медия",
+  "upload_form.undo": "Отмяна",
+  "upload_progress.label": "Uploading...",
+  "video_player.expand": "Expand video",
+  "video_player.toggle_sound": "Звук",
+  "video_player.toggle_visible": "Toggle visibility",
+  "video_player.video_error": "Video could not be played"
+}
\ No newline at end of file
diff --git a/app/assets/javascripts/components/locales/de.jsx b/app/javascript/mastodon/locales/de.json
index 8720a2d36..1d79f0cb8 100644
--- a/app/assets/javascripts/components/locales/de.jsx
+++ b/app/javascript/mastodon/locales/de.json
@@ -1,4 +1,4 @@
-const de = {
+{
   "account.block": "@{name} blocken",
   "account.disclaimer": "Dieser Benutzer ist von einer anderen Instanz. Diese Zahl könnte größer sein.",
   "account.edit_profile": "Profil bearbeiten",
@@ -15,7 +15,6 @@ const de = {
   "account.unfollow": "Entfolgen",
   "account.unmute": "@{name} nicht mehr stummschalten",
   "boost_modal.combo": "Du kannst {combo} drücken, um dies beim nächsten Mal zu überspringen",
-  "column_back_button.label": "Zurück",
   "column.blocks": "Blockierte Benutzer",
   "column.community": "Lokale Zeitleiste",
   "column.favourites": "Favoriten",
@@ -24,17 +23,38 @@ const de = {
   "column.mutes": "Stummgeschaltete Benutzer",
   "column.notifications": "Mitteilungen",
   "column.public": "Gesamtes bekanntes Netz",
+  "column_back_button.label": "Zurück",
+  "column_subheading.navigation": "Navigation",
+  "column_subheading.settings": "Settings",
+  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+  "compose_form.lock_disclaimer.lock": "locked",
   "compose_form.placeholder": "Worüber möchtest du schreiben?",
   "compose_form.privacy_disclaimer": "Dein privater Status wird an die genannten Benutzer auf den Domains {domains} zugestellt. Vertraust du {domainsCount, plural, one {diesem Server} other {diesen Servern}}? Private Beiträge funktionieren nur auf Mastodon-Instanzen. Wenn {domains} {domainsCount, plural, one {keine Mastodon-Instanz ist} other {keine Mastodon-Instanzen sind}}, wird es dort kein Anzeichen geben, dass dein Beitrag privat ist und er könnte geteilt oder anderweitig für unerwünschte Empfänger sichtbar gemacht werden.",
   "compose_form.publish": "Tröt",
   "compose_form.sensitive": "Medien als heikel markieren",
-  "compose_form.spoiler_placeholder": "Inhaltswarnung",
   "compose_form.spoiler": "Text hinter Warnung verbergen",
+  "compose_form.spoiler_placeholder": "Inhaltswarnung",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.confirm": "Block",
+  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.message": "Are you sure you want to mute {name}?",
+  "emoji_button.activity": "Activity",
+  "emoji_button.flags": "Flags",
+  "emoji_button.food": "Food & Drink",
   "emoji_button.label": "Emoji einfügen",
+  "emoji_button.nature": "Nature",
+  "emoji_button.objects": "Objects",
+  "emoji_button.people": "People",
+  "emoji_button.search": "Search...",
+  "emoji_button.symbols": "Symbols",
+  "emoji_button.travel": "Travel & Places",
   "empty_column.community": "Die lokale Zeitleiste ist leer. Schreibe etwas öffentlich, um den Ball ins Rollen zu bringen!",
   "empty_column.hashtag": "Es gibt noch nichts unter diesem Hashtag.",
-  "empty_column.home.public_timeline": "die öffentliche Zeitleiste",
   "empty_column.home": "Du folgst noch niemandem. Besuche {public} oder benutze die Suche, um zu starten oder andere Benutzer anzutreffen.",
+  "empty_column.home.public_timeline": "die öffentliche Zeitleiste",
   "empty_column.notifications": "Du hast noch keine Mitteilungen. Interagiere mit anderen, um die Konversation zu starten.",
   "empty_column.public": "Hier ist nichts zu sehen! Schreibe etwas öffentlich oder folge Benutzern von anderen Instanzen, um es aufzufüllen.",
   "follow_request.authorize": "Erlauben",
@@ -64,10 +84,9 @@ const de = {
   "navigation_bar.public_timeline": "Föderierte Zeitleiste",
   "notification.favourite": "{name} favorisierte deinen Status",
   "notification.follow": "{name} folgt dir",
-  "notification.mention": "{name} erwähnte dich",
   "notification.reblog": "{name} teilte deinen Status",
-  "notifications.clear_confirmation": "Bist du sicher, dass du alle Mitteilungen beseitigen willst?",
   "notifications.clear": "Mitteilungen beseitigen",
+  "notifications.clear_confirmation": "Bist du sicher, dass du alle Mitteilungen beseitigen willst?",
   "notifications.column_settings.alert": "Desktop-Benachrichtigungen",
   "notifications.column_settings.favourite": "Favorisierungen:",
   "notifications.column_settings.follow": "Neue Folgende:",
@@ -76,6 +95,26 @@ const de = {
   "notifications.column_settings.show": "In der Spalte anzeigen",
   "notifications.column_settings.sound": "Ton abspielen",
   "notifications.settings": "Spalteneinstellungen",
+  "onboarding.done": "Done",
+  "onboarding.next": "Next",
+  "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+  "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
+  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to Mastodon!",
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
+  "onboarding.page_six.almost_done": "Almost done...",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
+  "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "community guidelines",
+  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
+  "onboarding.page_six.various_app": "mobile apps",
+  "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+  "onboarding.skip": "Skip",
   "privacy.change": "Privatsphäre des Status anpassen",
   "privacy.direct.long": "Beitrag nur an erwähnte Benutzer",
   "privacy.direct.short": "Direkt",
@@ -90,9 +129,9 @@ const de = {
   "report.placeholder": "Zusätzliche Kommentare",
   "report.submit": "Absenden",
   "report.target": "Melden",
-  "search_results.total": "{count, number} {count, plural, one {Ergebnis} other {Ergebnisse}}",
   "search.placeholder": "Suche",
-  "search.status_by": "Status von {name}",
+  "search_results.total": "{count, number} {count, plural, one {Ergebnis} other {Ergebnisse}}",
+  "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Löschen",
   "status.favourite": "Favorisieren",
   "status.load_more": "Weitere laden",
@@ -117,10 +156,8 @@ const de = {
   "upload_button.label": "Mediendatei hinzufügen",
   "upload_form.undo": "Entfernen",
   "upload_progress.label": "Lade hoch…",
+  "video_player.expand": "Videoanzeige vergrößern",
   "video_player.toggle_sound": "Ton umschalten",
   "video_player.toggle_visible": "Sichtbarkeit umschalten",
-  "video_player.expand": "Videoanzeige vergrößern",
-  "video_player.video_error": "Video konnte nicht abgespielt werden",
-};
-
-export default de;
+  "video_player.video_error": "Video konnte nicht abgespielt werden"
+}
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json
new file mode 100644
index 000000000..ea481e154
--- /dev/null
+++ b/app/javascript/mastodon/locales/defaultMessages.json
@@ -0,0 +1,1068 @@
+[
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Follow",
+        "id": "account.follow"
+      },
+      {
+        "defaultMessage": "Unfollow",
+        "id": "account.unfollow"
+      },
+      {
+        "defaultMessage": "Awaiting approval",
+        "id": "account.requested"
+      },
+      {
+        "defaultMessage": "Unblock @{name}",
+        "id": "account.unblock"
+      },
+      {
+        "defaultMessage": "Unmute @{name}",
+        "id": "account.unmute"
+      }
+    ],
+    "path": "app/javascript/mastodon/components/account.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Back",
+        "id": "column_back_button.label"
+      }
+    ],
+    "path": "app/javascript/mastodon/components/column_back_button_slim.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Back",
+        "id": "column_back_button.label"
+      }
+    ],
+    "path": "app/javascript/mastodon/components/column_back_button.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Load more",
+        "id": "status.load_more"
+      }
+    ],
+    "path": "app/javascript/mastodon/components/load_more.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Loading...",
+        "id": "loading_indicator.label"
+      }
+    ],
+    "path": "app/javascript/mastodon/components/loading_indicator.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Toggle visibility",
+        "id": "media_gallery.toggle_visible"
+      },
+      {
+        "defaultMessage": "Sensitive content",
+        "id": "status.sensitive_warning"
+      },
+      {
+        "defaultMessage": "Media hidden",
+        "id": "status.media_hidden"
+      },
+      {
+        "defaultMessage": "Click to view",
+        "id": "status.sensitive_toggle"
+      }
+    ],
+    "path": "app/javascript/mastodon/components/media_gallery.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Not found",
+        "id": "missing_indicator.label"
+      }
+    ],
+    "path": "app/javascript/mastodon/components/missing_indicator.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Delete",
+        "id": "status.delete"
+      },
+      {
+        "defaultMessage": "Mention @{name}",
+        "id": "status.mention"
+      },
+      {
+        "defaultMessage": "Mute @{name}",
+        "id": "account.mute"
+      },
+      {
+        "defaultMessage": "Block @{name}",
+        "id": "account.block"
+      },
+      {
+        "defaultMessage": "Reply",
+        "id": "status.reply"
+      },
+      {
+        "defaultMessage": "Reply to thread",
+        "id": "status.replyAll"
+      },
+      {
+        "defaultMessage": "Boost",
+        "id": "status.reblog"
+      },
+      {
+        "defaultMessage": "This post cannot be boosted",
+        "id": "status.cannot_reblog"
+      },
+      {
+        "defaultMessage": "Favourite",
+        "id": "status.favourite"
+      },
+      {
+        "defaultMessage": "Expand this status",
+        "id": "status.open"
+      },
+      {
+        "defaultMessage": "Report @{name}",
+        "id": "status.report"
+      }
+    ],
+    "path": "app/javascript/mastodon/components/status_action_bar.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Show more",
+        "id": "status.show_more"
+      },
+      {
+        "defaultMessage": "Show less",
+        "id": "status.show_less"
+      }
+    ],
+    "path": "app/javascript/mastodon/components/status_content.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "{name} boosted",
+        "id": "status.reblogged_by"
+      }
+    ],
+    "path": "app/javascript/mastodon/components/status.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Toggle sound",
+        "id": "video_player.toggle_sound"
+      },
+      {
+        "defaultMessage": "Toggle visibility",
+        "id": "video_player.toggle_visible"
+      },
+      {
+        "defaultMessage": "Expand video",
+        "id": "video_player.expand"
+      },
+      {
+        "defaultMessage": "Video could not be played",
+        "id": "video_player.video_error"
+      },
+      {
+        "defaultMessage": "Sensitive content",
+        "id": "status.sensitive_warning"
+      },
+      {
+        "defaultMessage": "Click to view",
+        "id": "status.sensitive_toggle"
+      },
+      {
+        "defaultMessage": "Media hidden",
+        "id": "status.media_hidden"
+      }
+    ],
+    "path": "app/javascript/mastodon/components/video_player.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Delete",
+        "id": "confirmations.delete.confirm"
+      },
+      {
+        "defaultMessage": "Are you sure you want to delete this status?",
+        "id": "confirmations.delete.message"
+      },
+      {
+        "defaultMessage": "Block",
+        "id": "confirmations.block.confirm"
+      },
+      {
+        "defaultMessage": "Mute",
+        "id": "confirmations.mute.confirm"
+      },
+      {
+        "defaultMessage": "Are you sure you want to block {name}?",
+        "id": "confirmations.block.message"
+      },
+      {
+        "defaultMessage": "Are you sure you want to mute {name}?",
+        "id": "confirmations.mute.message"
+      }
+    ],
+    "path": "app/javascript/mastodon/containers/status_container.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Block",
+        "id": "confirmations.block.confirm"
+      },
+      {
+        "defaultMessage": "Mute",
+        "id": "confirmations.mute.confirm"
+      },
+      {
+        "defaultMessage": "Are you sure you want to block {name}?",
+        "id": "confirmations.block.message"
+      },
+      {
+        "defaultMessage": "Are you sure you want to mute {name}?",
+        "id": "confirmations.mute.message"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/account_timeline/containers/header_container.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Mention @{name}",
+        "id": "account.mention"
+      },
+      {
+        "defaultMessage": "Edit profile",
+        "id": "account.edit_profile"
+      },
+      {
+        "defaultMessage": "Unblock @{name}",
+        "id": "account.unblock"
+      },
+      {
+        "defaultMessage": "Unfollow",
+        "id": "account.unfollow"
+      },
+      {
+        "defaultMessage": "Unmute @{name}",
+        "id": "account.unmute"
+      },
+      {
+        "defaultMessage": "Block @{name}",
+        "id": "account.block"
+      },
+      {
+        "defaultMessage": "Mute @{name}",
+        "id": "account.mute"
+      },
+      {
+        "defaultMessage": "Follow",
+        "id": "account.follow"
+      },
+      {
+        "defaultMessage": "Report @{name}",
+        "id": "account.report"
+      },
+      {
+        "defaultMessage": "This user is from another instance. This number may be larger.",
+        "id": "account.disclaimer"
+      },
+      {
+        "defaultMessage": "Posts",
+        "id": "account.posts"
+      },
+      {
+        "defaultMessage": "Follows",
+        "id": "account.follows"
+      },
+      {
+        "defaultMessage": "Followers",
+        "id": "account.followers"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/account/components/action_bar.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Unfollow",
+        "id": "account.unfollow"
+      },
+      {
+        "defaultMessage": "Follow",
+        "id": "account.follow"
+      },
+      {
+        "defaultMessage": "Awaiting approval",
+        "id": "account.requested"
+      },
+      {
+        "defaultMessage": "Follows you",
+        "id": "account.follows_you"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/account/components/header.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Blocked users",
+        "id": "column.blocks"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/blocks/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Local timeline",
+        "id": "column.community"
+      },
+      {
+        "defaultMessage": "The local timeline is empty. Write something publicly to get the ball rolling!",
+        "id": "empty_column.community"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/community_timeline/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "What is on your mind?",
+        "id": "compose_form.placeholder"
+      },
+      {
+        "defaultMessage": "Content warning",
+        "id": "compose_form.spoiler_placeholder"
+      },
+      {
+        "defaultMessage": "Toot",
+        "id": "compose_form.publish"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/compose/components/compose_form.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Insert emoji",
+        "id": "emoji_button.label"
+      },
+      {
+        "defaultMessage": "Search...",
+        "id": "emoji_button.search"
+      },
+      {
+        "defaultMessage": "People",
+        "id": "emoji_button.people"
+      },
+      {
+        "defaultMessage": "Nature",
+        "id": "emoji_button.nature"
+      },
+      {
+        "defaultMessage": "Food & Drink",
+        "id": "emoji_button.food"
+      },
+      {
+        "defaultMessage": "Activity",
+        "id": "emoji_button.activity"
+      },
+      {
+        "defaultMessage": "Travel & Places",
+        "id": "emoji_button.travel"
+      },
+      {
+        "defaultMessage": "Objects",
+        "id": "emoji_button.objects"
+      },
+      {
+        "defaultMessage": "Symbols",
+        "id": "emoji_button.symbols"
+      },
+      {
+        "defaultMessage": "Flags",
+        "id": "emoji_button.flags"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Edit profile",
+        "id": "navigation_bar.edit_profile"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/compose/components/navigation_bar.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Public",
+        "id": "privacy.public.short"
+      },
+      {
+        "defaultMessage": "Post to public timelines",
+        "id": "privacy.public.long"
+      },
+      {
+        "defaultMessage": "Unlisted",
+        "id": "privacy.unlisted.short"
+      },
+      {
+        "defaultMessage": "Do not show in public timelines",
+        "id": "privacy.unlisted.long"
+      },
+      {
+        "defaultMessage": "Followers-only",
+        "id": "privacy.private.short"
+      },
+      {
+        "defaultMessage": "Post to followers only",
+        "id": "privacy.private.long"
+      },
+      {
+        "defaultMessage": "Direct",
+        "id": "privacy.direct.short"
+      },
+      {
+        "defaultMessage": "Post to mentioned users only",
+        "id": "privacy.direct.long"
+      },
+      {
+        "defaultMessage": "Adjust status privacy",
+        "id": "privacy.change"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/compose/components/privacy_dropdown.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Cancel",
+        "id": "reply_indicator.cancel"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/compose/components/reply_indicator.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "{count, number} {count, plural, one {result} other {results}}",
+        "id": "search_results.total"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/compose/components/search_results.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Search",
+        "id": "search.placeholder"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/compose/components/search.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Add media",
+        "id": "upload_button.label"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/compose/components/upload_button.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Undo",
+        "id": "upload_form.undo"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/compose/components/upload_form.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Uploading...",
+        "id": "upload_progress.label"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/compose/components/upload_progress.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Mark media as sensitive",
+        "id": "compose_form.sensitive"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/compose/containers/sensitive_button_container.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Hide text behind warning",
+        "id": "compose_form.spoiler"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/compose/containers/spoiler_button_container.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+        "id": "compose_form.lock_disclaimer"
+      },
+      {
+        "defaultMessage": "locked",
+        "id": "compose_form.lock_disclaimer.lock"
+      },
+      {
+        "defaultMessage": "Your private status will be delivered to mentioned users on {domains}. Do you trust {domainsCount, plural, one {that server} other {those servers}}? Post privacy only works on Mastodon instances. If {domains} {domainsCount, plural, one {is not a Mastodon instance} other {are not Mastodon instances}}, there will be no indication that your post is private, and it may be boosted or otherwise made visible to unintended recipients.",
+        "id": "compose_form.privacy_disclaimer"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/compose/containers/warning_container.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Getting started",
+        "id": "getting_started.heading"
+      },
+      {
+        "defaultMessage": "Federated timeline",
+        "id": "navigation_bar.public_timeline"
+      },
+      {
+        "defaultMessage": "Local timeline",
+        "id": "navigation_bar.community_timeline"
+      },
+      {
+        "defaultMessage": "Preferences",
+        "id": "navigation_bar.preferences"
+      },
+      {
+        "defaultMessage": "Logout",
+        "id": "navigation_bar.logout"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/compose/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Favourites",
+        "id": "column.favourites"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/favourited_statuses/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Authorize",
+        "id": "follow_request.authorize"
+      },
+      {
+        "defaultMessage": "Reject",
+        "id": "follow_request.reject"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/follow_requests/components/account_authorize.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Follow requests",
+        "id": "column.follow_requests"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/follow_requests/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Getting started",
+        "id": "getting_started.heading"
+      },
+      {
+        "defaultMessage": "Federated timeline",
+        "id": "navigation_bar.public_timeline"
+      },
+      {
+        "defaultMessage": "Navigation",
+        "id": "column_subheading.navigation"
+      },
+      {
+        "defaultMessage": "Settings",
+        "id": "column_subheading.settings"
+      },
+      {
+        "defaultMessage": "Local timeline",
+        "id": "navigation_bar.community_timeline"
+      },
+      {
+        "defaultMessage": "Preferences",
+        "id": "navigation_bar.preferences"
+      },
+      {
+        "defaultMessage": "Follow requests",
+        "id": "navigation_bar.follow_requests"
+      },
+      {
+        "defaultMessage": "Logout",
+        "id": "navigation_bar.logout"
+      },
+      {
+        "defaultMessage": "Favourites",
+        "id": "navigation_bar.favourites"
+      },
+      {
+        "defaultMessage": "Blocked users",
+        "id": "navigation_bar.blocks"
+      },
+      {
+        "defaultMessage": "Muted users",
+        "id": "navigation_bar.mutes"
+      },
+      {
+        "defaultMessage": "Extended information",
+        "id": "navigation_bar.info"
+      },
+      {
+        "defaultMessage": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}. {apps}.",
+        "id": "getting_started.open_source_notice"
+      },
+      {
+        "defaultMessage": "Various apps are available",
+        "id": "getting_started.apps"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/getting_started/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "There is nothing in this hashtag yet.",
+        "id": "empty_column.hashtag"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/hashtag_timeline/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Filter out by regular expressions",
+        "id": "home.column_settings.filter_regex"
+      },
+      {
+        "defaultMessage": "Column settings",
+        "id": "home.settings"
+      },
+      {
+        "defaultMessage": "Basic",
+        "id": "home.column_settings.basic"
+      },
+      {
+        "defaultMessage": "Show boosts",
+        "id": "home.column_settings.show_reblogs"
+      },
+      {
+        "defaultMessage": "Show replies",
+        "id": "home.column_settings.show_replies"
+      },
+      {
+        "defaultMessage": "Advanced",
+        "id": "home.column_settings.advanced"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/home_timeline/components/column_settings.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Home",
+        "id": "column.home"
+      },
+      {
+        "defaultMessage": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.",
+        "id": "empty_column.home"
+      },
+      {
+        "defaultMessage": "the public timeline",
+        "id": "empty_column.home.public_timeline"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/home_timeline/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Muted users",
+        "id": "column.mutes"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/mutes/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Clear notifications",
+        "id": "notifications.clear"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/notifications/components/clear_column_button.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Column settings",
+        "id": "notifications.settings"
+      },
+      {
+        "defaultMessage": "Desktop notifications",
+        "id": "notifications.column_settings.alert"
+      },
+      {
+        "defaultMessage": "Show in column",
+        "id": "notifications.column_settings.show"
+      },
+      {
+        "defaultMessage": "Play sound",
+        "id": "notifications.column_settings.sound"
+      },
+      {
+        "defaultMessage": "New followers:",
+        "id": "notifications.column_settings.follow"
+      },
+      {
+        "defaultMessage": "Favourites:",
+        "id": "notifications.column_settings.favourite"
+      },
+      {
+        "defaultMessage": "Mentions:",
+        "id": "notifications.column_settings.mention"
+      },
+      {
+        "defaultMessage": "Boosts:",
+        "id": "notifications.column_settings.reblog"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/notifications/components/column_settings.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "{name} followed you",
+        "id": "notification.follow"
+      },
+      {
+        "defaultMessage": "{name} favourited your status",
+        "id": "notification.favourite"
+      },
+      {
+        "defaultMessage": "{name} boosted your status",
+        "id": "notification.reblog"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/notifications/components/notification.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Notifications",
+        "id": "column.notifications"
+      },
+      {
+        "defaultMessage": "Are you sure you want to permanently clear all your notifications?",
+        "id": "notifications.clear_confirmation"
+      },
+      {
+        "defaultMessage": "Clear notifications",
+        "id": "notifications.clear"
+      },
+      {
+        "defaultMessage": "You don't have any notifications yet. Interact with others to start the conversation.",
+        "id": "empty_column.notifications"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/notifications/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Federated timeline",
+        "id": "column.public"
+      },
+      {
+        "defaultMessage": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up",
+        "id": "empty_column.public"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/public_timeline/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "New report",
+        "id": "report.heading"
+      },
+      {
+        "defaultMessage": "Additional comments",
+        "id": "report.placeholder"
+      },
+      {
+        "defaultMessage": "Submit",
+        "id": "report.submit"
+      },
+      {
+        "defaultMessage": "Reporting",
+        "id": "report.target"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/report/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Delete",
+        "id": "status.delete"
+      },
+      {
+        "defaultMessage": "Mention @{name}",
+        "id": "status.mention"
+      },
+      {
+        "defaultMessage": "Reply",
+        "id": "status.reply"
+      },
+      {
+        "defaultMessage": "Boost",
+        "id": "status.reblog"
+      },
+      {
+        "defaultMessage": "This post cannot be boosted",
+        "id": "status.cannot_reblog"
+      },
+      {
+        "defaultMessage": "Favourite",
+        "id": "status.favourite"
+      },
+      {
+        "defaultMessage": "Report @{name}",
+        "id": "status.report"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/status/components/action_bar.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Delete",
+        "id": "confirmations.delete.confirm"
+      },
+      {
+        "defaultMessage": "Are you sure you want to delete this status?",
+        "id": "confirmations.delete.message"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/status/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Boost",
+        "id": "status.reblog"
+      },
+      {
+        "defaultMessage": "You can press {combo} to skip this next time",
+        "id": "boost_modal.combo"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/ui/components/boost_modal.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Cancel",
+        "id": "confirmation_modal.cancel"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/ui/components/confirmation_modal.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Close",
+        "id": "lightbox.close"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/ui/components/media_modal.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Home",
+        "id": "column.home"
+      },
+      {
+        "defaultMessage": "Notifications",
+        "id": "column.notifications"
+      },
+      {
+        "defaultMessage": "Local timeline",
+        "id": "column.community"
+      },
+      {
+        "defaultMessage": "Federated timeline",
+        "id": "column.public"
+      },
+      {
+        "defaultMessage": "Welcome to Mastodon!",
+        "id": "onboarding.page_one.welcome"
+      },
+      {
+        "defaultMessage": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+        "id": "onboarding.page_one.federation"
+      },
+      {
+        "defaultMessage": "You are on {domain}, so your full handle is {handle}",
+        "id": "onboarding.page_one.handle"
+      },
+      {
+        "defaultMessage": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+        "id": "onboarding.page_two.compose"
+      },
+      {
+        "defaultMessage": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+        "id": "onboarding.page_three.search"
+      },
+      {
+        "defaultMessage": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+        "id": "onboarding.page_three.profile"
+      },
+      {
+        "defaultMessage": "The home timeline shows posts from people you follow.",
+        "id": "onboarding.page_four.home"
+      },
+      {
+        "defaultMessage": "The notifications column shows when someone interacts with you.",
+        "id": "onboarding.page_four.notifications"
+      },
+      {
+        "defaultMessage": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+        "id": "onboarding.page_five.public_timelines"
+      },
+      {
+        "defaultMessage": "Your instance's admin is {admin}.",
+        "id": "onboarding.page_six.admin"
+      },
+      {
+        "defaultMessage": "Please read {domain}'s {guidelines}!",
+        "id": "onboarding.page_six.read_guidelines"
+      },
+      {
+        "defaultMessage": "community guidelines",
+        "id": "onboarding.page_six.guidelines"
+      },
+      {
+        "defaultMessage": "Almost done...",
+        "id": "onboarding.page_six.almost_done"
+      },
+      {
+        "defaultMessage": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+        "id": "onboarding.page_six.github"
+      },
+      {
+        "defaultMessage": "There are {apps} available for iOS, Android and other platforms.",
+        "id": "onboarding.page_six.apps_available"
+      },
+      {
+        "defaultMessage": "mobile apps",
+        "id": "onboarding.page_six.various_app"
+      },
+      {
+        "defaultMessage": "Bon Appetoot!",
+        "id": "onboarding.page_six.appetoot"
+      },
+      {
+        "defaultMessage": "Next",
+        "id": "onboarding.next"
+      },
+      {
+        "defaultMessage": "Done",
+        "id": "onboarding.done"
+      },
+      {
+        "defaultMessage": "Skip",
+        "id": "onboarding.skip"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/ui/components/onboarding_modal.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Compose",
+        "id": "tabs_bar.compose"
+      },
+      {
+        "defaultMessage": "Home",
+        "id": "tabs_bar.home"
+      },
+      {
+        "defaultMessage": "Notifications",
+        "id": "tabs_bar.notifications"
+      },
+      {
+        "defaultMessage": "Local",
+        "id": "tabs_bar.local_timeline"
+      },
+      {
+        "defaultMessage": "Federated",
+        "id": "tabs_bar.federated_timeline"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/ui/components/tabs_bar.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Drag & drop to upload",
+        "id": "upload_area.title"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/ui/components/upload_area.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Close",
+        "id": "lightbox.close"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/ui/components/video_modal.json"
+  }
+]
\ No newline at end of file
diff --git a/app/assets/javascripts/components/locales/en.jsx b/app/javascript/mastodon/locales/en.json
index afe714cac..797a1caf2 100644
--- a/app/assets/javascripts/components/locales/en.jsx
+++ b/app/javascript/mastodon/locales/en.json
@@ -1,14 +1,4 @@
-/**
- * Note for Contributors:
- * This file (en.jsx) serve as a template for other languages.
- * To make other contributors' life easier, please REMEMBER:
- *   1. to add your new string here; and
- *   2. to remove old strings that are no longer needed; and
- *   3. to sort the strings by the key.
- *   4. To rename the `en` const name and export default name to match your locale.
- * Thanks!
- */
-const en = {
+{
   "account.block": "Block @{name}",
   "account.disclaimer": "This user is from another instance. This number may be larger.",
   "account.edit_profile": "Edit profile",
@@ -63,8 +53,8 @@ const en = {
   "emoji_button.travel": "Travel & Places",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
-  "empty_column.home.public_timeline": "the public timeline",
   "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.",
+  "empty_column.home.public_timeline": "the public timeline",
   "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
   "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up",
   "follow_request.authorize": "Authorize",
@@ -94,7 +84,6 @@ const en = {
   "navigation_bar.public_timeline": "Federated timeline",
   "notification.favourite": "{name} favourited your status",
   "notification.follow": "{name} followed you",
-  "notification.mention": "{name} mentioned you",
   "notification.reblog": "{name} boosted your status",
   "notifications.clear": "Clear notifications",
   "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
@@ -141,7 +130,6 @@ const en = {
   "report.submit": "Submit",
   "report.target": "Reporting",
   "search.placeholder": "Search",
-  "search.status_by": "Status by {name}",
   "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
   "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Delete",
@@ -171,7 +159,5 @@ const en = {
   "video_player.expand": "Expand video",
   "video_player.toggle_sound": "Toggle sound",
   "video_player.toggle_visible": "Toggle visibility",
-  "video_player.video_error": "Video could not be played",
-};
-
-export default en;
+  "video_player.video_error": "Video could not be played"
+}
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
new file mode 100644
index 000000000..b71088490
--- /dev/null
+++ b/app/javascript/mastodon/locales/eo.json
@@ -0,0 +1,163 @@
+{
+  "account.block": "Bloki @{name}",
+  "account.disclaimer": "This user is from another instance. This number may be larger.",
+  "account.edit_profile": "Redakti la profilon",
+  "account.follow": "Sekvi",
+  "account.followers": "Sekvantoj",
+  "account.follows": "Sekvatoj",
+  "account.follows_you": "Sekvas vin",
+  "account.mention": "Mencii @{name}",
+  "account.mute": "Mute @{name}",
+  "account.posts": "Mesaĝoj",
+  "account.report": "Report @{name}",
+  "account.requested": "Atendas aprobon",
+  "account.unblock": "Malbloki @{name}",
+  "account.unfollow": "Malsekvi",
+  "account.unmute": "Unmute @{name}",
+  "boost_modal.combo": "You can press {combo} to skip this next time",
+  "column.blocks": "Blocked users",
+  "column.community": "Loka tempolinio",
+  "column.favourites": "Favourites",
+  "column.follow_requests": "Follow requests",
+  "column.home": "Hejmo",
+  "column.mutes": "Muted users",
+  "column.notifications": "Sciigoj",
+  "column.public": "Fratara tempolinio",
+  "column_back_button.label": "Reveni",
+  "column_subheading.navigation": "Navigation",
+  "column_subheading.settings": "Settings",
+  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+  "compose_form.lock_disclaimer.lock": "locked",
+  "compose_form.placeholder": "Pri kio vi pensas?",
+  "compose_form.privacy_disclaimer": "Via privata mesaĝo estos sendita nur al menciitaj uzantoj en {domains}. Ĉu vi fidas {domainsCount, plural, one {tiun servilon} other {tiujn servilojn}}? Mesaĝa privateco funkcias nur en aperaĵoj de Mastodon. Se {domains} {domainsCount, plural, one {ne estas aperaĵo de Mastodon} other {ne estas aperaĵoj de Mastodon}}, estos neniu indiko ke via mesaĝo estas privata, kaj ĝi povus esti diskonigita aŭ videbligita al necelitaj ricevantoj.",
+  "compose_form.publish": "Hup",
+  "compose_form.sensitive": "Marki ke la enhavo estas tikla",
+  "compose_form.spoiler": "Kaŝi la tekston malantaŭ averto",
+  "compose_form.spoiler_placeholder": "Content warning",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.confirm": "Block",
+  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.message": "Are you sure you want to mute {name}?",
+  "emoji_button.activity": "Activity",
+  "emoji_button.flags": "Flags",
+  "emoji_button.food": "Food & Drink",
+  "emoji_button.label": "Insert emoji",
+  "emoji_button.nature": "Nature",
+  "emoji_button.objects": "Objects",
+  "emoji_button.people": "People",
+  "emoji_button.search": "Search...",
+  "emoji_button.symbols": "Symbols",
+  "emoji_button.travel": "Travel & Places",
+  "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
+  "empty_column.hashtag": "There is nothing in this hashtag yet.",
+  "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.",
+  "empty_column.home.public_timeline": "the public timeline",
+  "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
+  "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up",
+  "follow_request.authorize": "Authorize",
+  "follow_request.reject": "Reject",
+  "getting_started.apps": "Various apps are available",
+  "getting_started.heading": "Por komenci",
+  "getting_started.open_source_notice": "Mastodon estas malfermitkoda programo. Vi povas kontribui aŭ raporti problemojn en github je {github}. {apps}.",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.basic": "Basic",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_reblogs": "Show boosts",
+  "home.column_settings.show_replies": "Show replies",
+  "home.settings": "Column settings",
+  "lightbox.close": "Fermi",
+  "loading_indicator.label": "Ŝarĝanta...",
+  "media_gallery.toggle_visible": "Toggle visibility",
+  "missing_indicator.label": "Not found",
+  "navigation_bar.blocks": "Blocked users",
+  "navigation_bar.community_timeline": "Loka tempolinio",
+  "navigation_bar.edit_profile": "Redakti la profilon",
+  "navigation_bar.favourites": "Favourites",
+  "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.info": "Extended information",
+  "navigation_bar.logout": "Elsaluti",
+  "navigation_bar.mutes": "Muted users",
+  "navigation_bar.preferences": "Preferoj",
+  "navigation_bar.public_timeline": "Fratara tempolinio",
+  "notification.favourite": "{name} favoris vian mesaĝon",
+  "notification.follow": "{name} sekvis vin",
+  "notification.reblog": "{name} diskonigis vian mesaĝon",
+  "notifications.clear": "Clear notifications",
+  "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
+  "notifications.column_settings.alert": "Retumilaj atentigoj",
+  "notifications.column_settings.favourite": "Favoroj:",
+  "notifications.column_settings.follow": "Novaj sekvantoj:",
+  "notifications.column_settings.mention": "Mencioj:",
+  "notifications.column_settings.reblog": "Diskonigoj:",
+  "notifications.column_settings.show": "Montri en kolono",
+  "notifications.column_settings.sound": "Play sound",
+  "notifications.settings": "Column settings",
+  "onboarding.done": "Done",
+  "onboarding.next": "Next",
+  "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+  "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
+  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to Mastodon!",
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
+  "onboarding.page_six.almost_done": "Almost done...",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
+  "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "community guidelines",
+  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
+  "onboarding.page_six.various_app": "mobile apps",
+  "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+  "onboarding.skip": "Skip",
+  "privacy.change": "Adjust status privacy",
+  "privacy.direct.long": "Post to mentioned users only",
+  "privacy.direct.short": "Direct",
+  "privacy.private.long": "Post to followers only",
+  "privacy.private.short": "Followers-only",
+  "privacy.public.long": "Post to public timelines",
+  "privacy.public.short": "Public",
+  "privacy.unlisted.long": "Do not show in public timelines",
+  "privacy.unlisted.short": "Unlisted",
+  "reply_indicator.cancel": "Rezigni",
+  "report.heading": "New report",
+  "report.placeholder": "Additional comments",
+  "report.submit": "Submit",
+  "report.target": "Reporting",
+  "search.placeholder": "Serĉi",
+  "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
+  "status.cannot_reblog": "This post cannot be boosted",
+  "status.delete": "Forigi",
+  "status.favourite": "Favori",
+  "status.load_more": "Load more",
+  "status.media_hidden": "Media hidden",
+  "status.mention": "Mencii @{name}",
+  "status.open": "Expand this status",
+  "status.reblog": "Diskonigi",
+  "status.reblogged_by": "{name} diskonigita",
+  "status.reply": "Respondi",
+  "status.replyAll": "Reply to thread",
+  "status.report": "Report @{name}",
+  "status.sensitive_toggle": "Alklaki por vidi",
+  "status.sensitive_warning": "Tikla enhavo",
+  "status.show_less": "Show less",
+  "status.show_more": "Show more",
+  "tabs_bar.compose": "Ekskribi",
+  "tabs_bar.federated_timeline": "Federated",
+  "tabs_bar.home": "Hejmo",
+  "tabs_bar.local_timeline": "Local",
+  "tabs_bar.notifications": "Sciigoj",
+  "upload_area.title": "Drag & drop to upload",
+  "upload_button.label": "Aldoni enhavaĵon",
+  "upload_form.undo": "Malfari",
+  "upload_progress.label": "Uploading...",
+  "video_player.expand": "Expand video",
+  "video_player.toggle_sound": "Aktivigi sonojn",
+  "video_player.toggle_visible": "Toggle visibility",
+  "video_player.video_error": "Video could not be played"
+}
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json
new file mode 100644
index 000000000..c023a4b7e
--- /dev/null
+++ b/app/javascript/mastodon/locales/es.json
@@ -0,0 +1,163 @@
+{
+  "account.block": "Bloquear",
+  "account.disclaimer": "This user is from another instance. This number may be larger.",
+  "account.edit_profile": "Editar perfil",
+  "account.follow": "Seguir",
+  "account.followers": "Seguidores",
+  "account.follows": "Seguir",
+  "account.follows_you": "Te sigue",
+  "account.mention": "Mencionar",
+  "account.mute": "Silenciar",
+  "account.posts": "Publicaciones",
+  "account.report": "Report @{name}",
+  "account.requested": "Esperando aprobación",
+  "account.unblock": "Desbloquear",
+  "account.unfollow": "Dejar de seguir",
+  "account.unmute": "Unmute @{name}",
+  "boost_modal.combo": "You can press {combo} to skip this next time",
+  "column.blocks": "Usuarios bloqueados",
+  "column.community": "Historia local",
+  "column.favourites": "Favoritos",
+  "column.follow_requests": "Solicitudes para seguirte",
+  "column.home": "Inicio",
+  "column.mutes": "Usuarios silenciados",
+  "column.notifications": "Notificaciones",
+  "column.public": "Historia federada",
+  "column_back_button.label": "Atrás",
+  "column_subheading.navigation": "Navigation",
+  "column_subheading.settings": "Settings",
+  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+  "compose_form.lock_disclaimer.lock": "locked",
+  "compose_form.placeholder": "¿En qué estás pensando?",
+  "compose_form.privacy_disclaimer": "Your private status will be delivered to mentioned users on {domains}. Do you trust {domainsCount, plural, one {that server} other {those servers}}? Post privacy only works on Mastodon instances. If {domains} {domainsCount, plural, one {is not a Mastodon instance} other {are not Mastodon instances}}, there will be no indication that your post is private, and it may be boosted or otherwise made visible to unintended recipients.",
+  "compose_form.publish": "Tootear",
+  "compose_form.sensitive": "Marcar contenido como sensible",
+  "compose_form.spoiler": "Ocultar texto tras advertencia",
+  "compose_form.spoiler_placeholder": "Advertencia de contenido",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.confirm": "Block",
+  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.message": "Are you sure you want to mute {name}?",
+  "emoji_button.activity": "Activity",
+  "emoji_button.flags": "Flags",
+  "emoji_button.food": "Food & Drink",
+  "emoji_button.label": "Insertar emoji",
+  "emoji_button.nature": "Nature",
+  "emoji_button.objects": "Objects",
+  "emoji_button.people": "People",
+  "emoji_button.search": "Search...",
+  "emoji_button.symbols": "Symbols",
+  "emoji_button.travel": "Travel & Places",
+  "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
+  "empty_column.hashtag": "There is nothing in this hashtag yet.",
+  "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.",
+  "empty_column.home.public_timeline": "the public timeline",
+  "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
+  "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up",
+  "follow_request.authorize": "Authorize",
+  "follow_request.reject": "Reject",
+  "getting_started.apps": "Various apps are available",
+  "getting_started.heading": "Primeros pasos",
+  "getting_started.open_source_notice": "Mastodon es software libre. Puedes contribuir o reportar errores en {github}. {apps}.",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.basic": "Basic",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_reblogs": "Show boosts",
+  "home.column_settings.show_replies": "Show replies",
+  "home.settings": "Column settings",
+  "lightbox.close": "Cerrar",
+  "loading_indicator.label": "Cargando...",
+  "media_gallery.toggle_visible": "Toggle visibility",
+  "missing_indicator.label": "Not found",
+  "navigation_bar.blocks": "Usuarios bloqueados",
+  "navigation_bar.community_timeline": "Historia local",
+  "navigation_bar.edit_profile": "Editar perfil",
+  "navigation_bar.favourites": "Favoritos",
+  "navigation_bar.follow_requests": "Solicitudes para seguirte",
+  "navigation_bar.info": "Información adicional",
+  "navigation_bar.logout": "Cerrar sesión",
+  "navigation_bar.mutes": "Usuarios silenciados",
+  "navigation_bar.preferences": "Preferencias",
+  "navigation_bar.public_timeline": "Historia federada",
+  "notification.favourite": "{name} marcó tu estado como favorito",
+  "notification.follow": "{name} te empezó a seguir",
+  "notification.reblog": "{name} ha retooteado tu estado",
+  "notifications.clear": "Clear notifications",
+  "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
+  "notifications.column_settings.alert": "Notificaciones de escritorio",
+  "notifications.column_settings.favourite": "Favoritos:",
+  "notifications.column_settings.follow": "Nuevos seguidores:",
+  "notifications.column_settings.mention": "Menciones:",
+  "notifications.column_settings.reblog": "Retoots:",
+  "notifications.column_settings.show": "Mostrar en columna",
+  "notifications.column_settings.sound": "Play sound",
+  "notifications.settings": "Column settings",
+  "onboarding.done": "Done",
+  "onboarding.next": "Next",
+  "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+  "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
+  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to Mastodon!",
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
+  "onboarding.page_six.almost_done": "Almost done...",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
+  "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "community guidelines",
+  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
+  "onboarding.page_six.various_app": "mobile apps",
+  "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+  "onboarding.skip": "Skip",
+  "privacy.change": "Ajustar privacidad",
+  "privacy.direct.long": "Sólo mostrar a los usuarios mencionados",
+  "privacy.direct.short": "Directo",
+  "privacy.private.long": "Sólo mostrar a seguidores",
+  "privacy.private.short": "Privado",
+  "privacy.public.long": "Mostrar en la historia federada",
+  "privacy.public.short": "Público",
+  "privacy.unlisted.long": "No mostrar en la historia federada",
+  "privacy.unlisted.short": "Sin federar",
+  "reply_indicator.cancel": "Cancelar",
+  "report.heading": "New report",
+  "report.placeholder": "Additional comments",
+  "report.submit": "Submit",
+  "report.target": "Reporting",
+  "search.placeholder": "Buscar",
+  "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
+  "status.cannot_reblog": "This post cannot be boosted",
+  "status.delete": "Borrar",
+  "status.favourite": "Favorito",
+  "status.load_more": "Load more",
+  "status.media_hidden": "Media hidden",
+  "status.mention": "Mencionar",
+  "status.open": "Expandir estado",
+  "status.reblog": "Retoot",
+  "status.reblogged_by": "Retooteado por {name}",
+  "status.reply": "Responder",
+  "status.replyAll": "Reply to thread",
+  "status.report": "Reportar",
+  "status.sensitive_toggle": "Click para ver",
+  "status.sensitive_warning": "Contenido sensible",
+  "status.show_less": "Mostrar menos",
+  "status.show_more": "Mostrar más",
+  "tabs_bar.compose": "Redactar",
+  "tabs_bar.federated_timeline": "Federated",
+  "tabs_bar.home": "Inicio",
+  "tabs_bar.local_timeline": "Local",
+  "tabs_bar.notifications": "Notificaciones",
+  "upload_area.title": "Drag & drop to upload",
+  "upload_button.label": "Subir multimedia",
+  "upload_form.undo": "Deshacer",
+  "upload_progress.label": "Uploading...",
+  "video_player.expand": "Expand video",
+  "video_player.toggle_sound": "Act/Desac. sonido",
+  "video_player.toggle_visible": "Toggle visibility",
+  "video_player.video_error": "Video could not be played"
+}
\ No newline at end of file
diff --git a/app/assets/javascripts/components/locales/fa.jsx b/app/javascript/mastodon/locales/fa.json
index 40a750618..7f6585884 100644
--- a/app/assets/javascripts/components/locales/fa.jsx
+++ b/app/javascript/mastodon/locales/fa.json
@@ -1,11 +1,11 @@
-const fa = {
+{
   "account.block": "@{name} را مسدود کن",
   "account.disclaimer": "این کاربر عضو سرور متفاوتی است. شاید عدد واقعی بیشتر از این باشد.",
   "account.edit_profile": "ویرایش نمایه",
   "account.follow": "پی بگیرید",
   "account.followers": "پیگیران",
-  "account.follows_you": "پیگیر شماست",
   "account.follows": "پی می‌گیرد",
+  "account.follows_you": "پیگیر شماست",
   "account.mention": "نام‌بردن از @{name}",
   "account.mute": "بی‌صدا کردن @{name}",
   "account.posts": "نوشته‌ها",
@@ -15,7 +15,6 @@ const fa = {
   "account.unfollow": "پایان پیگیری",
   "account.unmute": "باصدا کردن @{name}",
   "boost_modal.combo": "دکمهٔ {combo} را بزنید تا دیگر این را نبینید",
-  "column_back_button.label": "بازگشت",
   "column.blocks": "کاربران مسدودشده",
   "column.community": "نوشته‌های محلی",
   "column.favourites": "پسندیده‌ها",
@@ -24,26 +23,38 @@ const fa = {
   "column.mutes": "کاربران بی‌صداشده",
   "column.notifications": "اعلان‌ها",
   "column.public": "نوشته‌های همه‌جا",
+  "column_back_button.label": "بازگشت",
+  "column_subheading.navigation": "Navigation",
+  "column_subheading.settings": "Settings",
+  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+  "compose_form.lock_disclaimer.lock": "locked",
   "compose_form.placeholder": "تازه چه خبر؟",
   "compose_form.privacy_disclaimer": "نوشتهٔ خصوصی شما به کاربران نام‌برده‌شده در {domains} فرستاده می‌شود. آیا به {domainsCount, plural, one {آن سرور} other {آن سرورها}} اعتماد دارید؟ تنظیمات حریم خصوصی نوشته‌ها تنها در سرورهای ماستدون کار می‌کند. اگر {domains} {domainsCount, plural, one {یک سرور ماستدون نباشد} other {سرورهای ماستدون نباشند}}، اشاره‌ای به خصوصی‌بودن نوشتهٔ شما نخواهد شد و شاید نوشتهٔ شما هم‌رسان شود یا برای کاربرانی که نمی‌خواهید نمایش یابد.",
   "compose_form.publish": "بوق",
   "compose_form.sensitive": "تصاویر حساس هستند",
-  "compose_form.spoiler_placeholder": "هشدار محتوا",
   "compose_form.spoiler": "نوشته را پشت هشدار پنهان کنید",
+  "compose_form.spoiler_placeholder": "هشدار محتوا",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.confirm": "Block",
+  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.message": "Are you sure you want to mute {name}?",
+  "emoji_button.activity": "فعالیت",
+  "emoji_button.flags": "پرچم‌ها",
+  "emoji_button.food": "غذا و نوشیدنی",
   "emoji_button.label": "افزودن شکلک",
-  "emoji_button.search": "جستجو...",
-  "emoji_button.people": "مردم",
   "emoji_button.nature": "طبیعت",
-  "emoji_button.food": "غذا و نوشیدنی",
-  "emoji_button.activity": "فعالیت",
-  "emoji_button.travel": "سفر و مکان",
   "emoji_button.objects": "اشیا",
+  "emoji_button.people": "مردم",
+  "emoji_button.search": "جستجو...",
   "emoji_button.symbols": "نمادها",
-  "emoji_button.flags": "پرچم‌ها",
+  "emoji_button.travel": "سفر و مکان",
   "empty_column.community": "فهرست نوشته‌های محلی خالی است. چیزی بنویسید تا چرخش بچرخد!",
   "empty_column.hashtag": "هنوز هیچ چیزی با این هشتگ نیست.",
-  "empty_column.home.public_timeline": "فهرست نوشته‌های همه‌جا",
   "empty_column.home": "شما هنوز پیگیر کسی نیستید. {public} را ببینید یا چیزی را جستجو کنید تا کاربران دیگر را ببینید.",
+  "empty_column.home.public_timeline": "فهرست نوشته‌های همه‌جا",
   "empty_column.notifications": "هنوز هیچ اعلانی ندارید. به نوشته‌های دیگران واکنش نشان دهید تا گفتگو آغاز شود.",
   "empty_column.public": "این‌جا هنوز چیزی نیست! خودتان چیزی بنویسید یا کاربران دیگر را پی بگیرید تا این‌جا پر شود",
   "follow_request.authorize": "اجازه دهید",
@@ -73,10 +84,9 @@ const fa = {
   "navigation_bar.public_timeline": "نوشته‌های همه‌جا",
   "notification.favourite": "{name} نوشتهٔ شما را پسندید",
   "notification.follow": "{name} پیگیر شما شد",
-  "notification.mention": "{name} از شما نام برد",
   "notification.reblog": "{name} نوشتهٔ شما را بازبوقید",
-  "notifications.clear_confirmation": "واقعاً می‌خواهید همهٔ اعلان‌هایتان را برای همیشه پاک کنید؟",
   "notifications.clear": "پاک‌کردن اعلان‌ها",
+  "notifications.clear_confirmation": "واقعاً می‌خواهید همهٔ اعلان‌هایتان را برای همیشه پاک کنید؟",
   "notifications.column_settings.alert": "اعلان در کامپیوتر",
   "notifications.column_settings.favourite": "پسندیده‌ها:",
   "notifications.column_settings.follow": "پیگیران تازه:",
@@ -85,6 +95,26 @@ const fa = {
   "notifications.column_settings.show": "در ستون نشان بده",
   "notifications.column_settings.sound": "صدا را پخش کن",
   "notifications.settings": "تنظیمات ستون",
+  "onboarding.done": "Done",
+  "onboarding.next": "Next",
+  "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+  "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
+  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to Mastodon!",
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
+  "onboarding.page_six.almost_done": "Almost done...",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
+  "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "community guidelines",
+  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
+  "onboarding.page_six.various_app": "mobile apps",
+  "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+  "onboarding.skip": "Skip",
   "privacy.change": "تنظیم حریم خصوصی نوشته‌ها",
   "privacy.direct.long": "تنها به کاربران نام‌برده‌شده نشان بده",
   "privacy.direct.short": "مستقیم",
@@ -99,9 +129,9 @@ const fa = {
   "report.placeholder": "توضیح اضافه",
   "report.submit": "بفرست",
   "report.target": "گزارش‌دادن",
-  "search_results.total": "{count, number} {count, plural, one {نتیجه} other {نتیجه}}",
   "search.placeholder": "جستجو",
-  "search.status_by": "نوشتهٔ {name}",
+  "search_results.total": "{count, number} {count, plural, one {نتیجه} other {نتیجه}}",
+  "status.cannot_reblog": "این نوشته را نمی‌شود بازبوقید",
   "status.delete": "پاک‌کردن",
   "status.favourite": "پسندیدن",
   "status.load_more": "بیشتر نشان بده",
@@ -109,7 +139,6 @@ const fa = {
   "status.mention": "از @{name} نام ببرید",
   "status.open": "این نوشته را باز کن",
   "status.reblog": "بوق",
-  "status.cannot_reblog": "این نوشته را نمی‌شود بازبوقید",
   "status.reblogged_by": "{name} بازبوقید",
   "status.reply": "پاسخ",
   "status.replyAll": "به نوشته پاسخ دهید",
@@ -127,10 +156,8 @@ const fa = {
   "upload_button.label": "افزودن تصویر",
   "upload_form.undo": "واگردانی",
   "upload_progress.label": "بارگذاری...",
+  "video_player.expand": "بازکردن ویدیو",
   "video_player.toggle_sound": "تغییر صداداری",
   "video_player.toggle_visible": "تغییر پیدایی",
-  "video_player.expand": "بازکردن ویدیو",
-  "video_player.video_error": "ویدیو نمی‌تواند پخش شود",
-};
-
-export default fa;
+  "video_player.video_error": "ویدیو نمی‌تواند پخش شود"
+}
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
new file mode 100644
index 000000000..148a371ae
--- /dev/null
+++ b/app/javascript/mastodon/locales/fi.json
@@ -0,0 +1,163 @@
+{
+  "account.block": "Estä @{name}",
+  "account.disclaimer": "This user is from another instance. This number may be larger.",
+  "account.edit_profile": "Muokkaa",
+  "account.follow": "Seuraa",
+  "account.followers": "Seuraajia",
+  "account.follows": "Seuraa",
+  "account.follows_you": "Seuraa sinua",
+  "account.mention": "Mainitse @{name}",
+  "account.mute": "Mute @{name}",
+  "account.posts": "Postit",
+  "account.report": "Report @{name}",
+  "account.requested": "Odottaa hyväksyntää",
+  "account.unblock": "Salli @{name}",
+  "account.unfollow": "Lopeta seuraaminen",
+  "account.unmute": "Unmute @{name}",
+  "boost_modal.combo": "You can press {combo} to skip this next time",
+  "column.blocks": "Blocked users",
+  "column.community": "Paikallinen aikajana",
+  "column.favourites": "Favourites",
+  "column.follow_requests": "Follow requests",
+  "column.home": "Koti",
+  "column.mutes": "Muted users",
+  "column.notifications": "Ilmoitukset",
+  "column.public": "Yleinen aikajana",
+  "column_back_button.label": "Takaisin",
+  "column_subheading.navigation": "Navigation",
+  "column_subheading.settings": "Settings",
+  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+  "compose_form.lock_disclaimer.lock": "locked",
+  "compose_form.placeholder": "Mitä sinulla on mielessä?",
+  "compose_form.privacy_disclaimer": "Sinun yksityinen status toimitetaan mainitsemallesi käyttäjille domaineissa {domains}. Luotatko {domainsCount, plural, one {tähän palvelimeen} other {näihin palvelimiin}}? Postauksen yksityisyys toimii van Mastodon palvelimilla. Jos {domains} {domainsCount, plural, one {ei ole Mastodon palvelin} other {eivät ole Mastodon palvelin}}, viestiin ei tule Yksityinen-merkintää, ja sitä voidaan boostata tai muuten tehdä näkyväksi muille vastaanottajille.",
+  "compose_form.publish": "Toot",
+  "compose_form.sensitive": "Merkitse media herkäksi",
+  "compose_form.spoiler": "Piiloita teksti varoituksen taakse",
+  "compose_form.spoiler_placeholder": "Content warning",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.confirm": "Block",
+  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.message": "Are you sure you want to mute {name}?",
+  "emoji_button.activity": "Activity",
+  "emoji_button.flags": "Flags",
+  "emoji_button.food": "Food & Drink",
+  "emoji_button.label": "Insert emoji",
+  "emoji_button.nature": "Nature",
+  "emoji_button.objects": "Objects",
+  "emoji_button.people": "People",
+  "emoji_button.search": "Search...",
+  "emoji_button.symbols": "Symbols",
+  "emoji_button.travel": "Travel & Places",
+  "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
+  "empty_column.hashtag": "There is nothing in this hashtag yet.",
+  "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.",
+  "empty_column.home.public_timeline": "the public timeline",
+  "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
+  "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up",
+  "follow_request.authorize": "Authorize",
+  "follow_request.reject": "Reject",
+  "getting_started.apps": "Various apps are available",
+  "getting_started.heading": "Aloitus",
+  "getting_started.open_source_notice": "Mastodon Mastodon on avoimen lähdekoodin ohjelma. Voit avustaa tai raportoida ongelmia GitHub palvelussa {github}. {apps}.",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.basic": "Basic",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_reblogs": "Show boosts",
+  "home.column_settings.show_replies": "Show replies",
+  "home.settings": "Column settings",
+  "lightbox.close": "Sulje",
+  "loading_indicator.label": "Ladataan...",
+  "media_gallery.toggle_visible": "Toggle visibility",
+  "missing_indicator.label": "Not found",
+  "navigation_bar.blocks": "Blocked users",
+  "navigation_bar.community_timeline": "Paikallinen aikajana",
+  "navigation_bar.edit_profile": "Muokkaa profiilia",
+  "navigation_bar.favourites": "Favourites",
+  "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.info": "Extended information",
+  "navigation_bar.logout": "Kirjaudu ulos",
+  "navigation_bar.mutes": "Muted users",
+  "navigation_bar.preferences": "Ominaisuudet",
+  "navigation_bar.public_timeline": "Yleinen aikajana",
+  "notification.favourite": "{name} tykkäsi statuksestasi",
+  "notification.follow": "{name} seurasi sinua",
+  "notification.reblog": "{name} buustasi statustasi",
+  "notifications.clear": "Clear notifications",
+  "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
+  "notifications.column_settings.alert": "Työpöytä ilmoitukset",
+  "notifications.column_settings.favourite": "Tykkäyksiä:",
+  "notifications.column_settings.follow": "Uusia seuraajia:",
+  "notifications.column_settings.mention": "Mainintoja:",
+  "notifications.column_settings.reblog": "Buusteja:",
+  "notifications.column_settings.show": "Näytä sarakkeessa",
+  "notifications.column_settings.sound": "Play sound",
+  "notifications.settings": "Column settings",
+  "onboarding.done": "Done",
+  "onboarding.next": "Next",
+  "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+  "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
+  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to Mastodon!",
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
+  "onboarding.page_six.almost_done": "Almost done...",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
+  "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "community guidelines",
+  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
+  "onboarding.page_six.various_app": "mobile apps",
+  "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+  "onboarding.skip": "Skip",
+  "privacy.change": "Adjust status privacy",
+  "privacy.direct.long": "Post to mentioned users only",
+  "privacy.direct.short": "Direct",
+  "privacy.private.long": "Post to followers only",
+  "privacy.private.short": "Followers-only",
+  "privacy.public.long": "Post to public timelines",
+  "privacy.public.short": "Public",
+  "privacy.unlisted.long": "Do not show in public timelines",
+  "privacy.unlisted.short": "Unlisted",
+  "reply_indicator.cancel": "Peruuta",
+  "report.heading": "New report",
+  "report.placeholder": "Additional comments",
+  "report.submit": "Submit",
+  "report.target": "Reporting",
+  "search.placeholder": "Hae",
+  "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
+  "status.cannot_reblog": "This post cannot be boosted",
+  "status.delete": "Poista",
+  "status.favourite": "Tykkää",
+  "status.load_more": "Load more",
+  "status.media_hidden": "Media hidden",
+  "status.mention": "Mainitse @{name}",
+  "status.open": "Expand this status",
+  "status.reblog": "Buustaa",
+  "status.reblogged_by": "{name} buustasi",
+  "status.reply": "Vastaa",
+  "status.replyAll": "Reply to thread",
+  "status.report": "Report @{name}",
+  "status.sensitive_toggle": "Klikkaa nähdäksesi",
+  "status.sensitive_warning": "Arkaluontoista sisältöä",
+  "status.show_less": "Show less",
+  "status.show_more": "Show more",
+  "tabs_bar.compose": "Luo",
+  "tabs_bar.federated_timeline": "Federated",
+  "tabs_bar.home": "Koti",
+  "tabs_bar.local_timeline": "Local",
+  "tabs_bar.notifications": "Ilmoitukset",
+  "upload_area.title": "Drag & drop to upload",
+  "upload_button.label": "Lisää mediaa",
+  "upload_form.undo": "Peru",
+  "upload_progress.label": "Uploading...",
+  "video_player.expand": "Expand video",
+  "video_player.toggle_sound": "Äänet päälle/pois",
+  "video_player.toggle_visible": "Toggle visibility",
+  "video_player.video_error": "Video could not be played"
+}
\ No newline at end of file
diff --git a/app/assets/javascripts/components/locales/fr.jsx b/app/javascript/mastodon/locales/fr.json
index de13284e5..36a5b04c6 100644
--- a/app/assets/javascripts/components/locales/fr.jsx
+++ b/app/javascript/mastodon/locales/fr.json
@@ -1,18 +1,10 @@
-/**
- * Note aux contributeurs⋅trices:
- * Pour rendre plus simple la vie des autres personnes
- * apportant leur contribution, merci de penser aux choses suivantes :
- *   1. Ajoutez les nouvelles chaînes traduites par ordre alphabétique
- *   2. Pensez à supprimer les chaînes inutilisées
- * Merci !
- */
-const fr = {
+{
   "account.block": "Bloquer",
   "account.disclaimer": "Ce compte est situé sur une autre instance. Les nombres peuvent être plus grands.",
   "account.edit_profile": "Modifier le profil",
+  "account.follow": "Suivre",
   "account.followers": "Abonné⋅e⋅s",
   "account.follows": "Abonnements",
-  "account.follow": "Suivre",
   "account.follows_you": "Vous suit",
   "account.mention": "Mentionner",
   "account.mute": "Masquer",
@@ -22,14 +14,20 @@ const fr = {
   "account.unblock": "Débloquer",
   "account.unfollow": "Ne plus suivre",
   "account.unmute": "Ne plus masquer",
-  "column_back_button.label": "Retour",
+  "boost_modal.combo": "You can press {combo} to skip this next time",
   "column.blocks": "Comptes bloqués",
   "column.community": "Fil public local",
   "column.favourites": "Favoris",
   "column.follow_requests": "Demandes de suivi",
   "column.home": "Accueil",
+  "column.mutes": "Muted users",
   "column.notifications": "Notifications",
   "column.public": "Fil public global",
+  "column_back_button.label": "Retour",
+  "column_subheading.navigation": "Navigation",
+  "column_subheading.settings": "Settings",
+  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+  "compose_form.lock_disclaimer.lock": "locked",
   "compose_form.placeholder": "Qu’avez-vous en tête ?",
   "compose_form.privacy_disclaimer": "Votre statut privé va être transmis aux personnes mentionnées sur {domains}. Avez-vous confiance en {domainsCount, plural, one {ce serveur} other {ces serveurs}} pour ne pas divulguer votre statut ? Les statuts privés ne fonctionnent que sur les instances de Mastodon. Si {domains} {domainsCount, plural, one {n’est pas une instance de Mastodon} other {ne sont pas des instances de Mastodon}}, il n’y aura aucune indication que votre statut est privé, et il pourrait être partagé ou rendu visible d’une autre manière à d’autres personnes imprévues.",
   "compose_form.private": "Rendre privé",
@@ -37,8 +35,23 @@ const fr = {
   "compose_form.sensitive": "Marquer le média comme délicat",
   "compose_form.spoiler": "Masquer le texte derrière un avertissement",
   "compose_form.spoiler_placeholder": "Avertissement",
-  "compose_form.unlisted": "Ne pas afficher dans les fils publics",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.confirm": "Block",
+  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.message": "Are you sure you want to mute {name}?",
+  "emoji_button.activity": "Activity",
+  "emoji_button.flags": "Flags",
+  "emoji_button.food": "Food & Drink",
   "emoji_button.label": "Insérer un emoji",
+  "emoji_button.nature": "Nature",
+  "emoji_button.objects": "Objects",
+  "emoji_button.people": "People",
+  "emoji_button.search": "Search...",
+  "emoji_button.symbols": "Symbols",
+  "emoji_button.travel": "Travel & Places",
   "empty_column.community": "Le fil public local est vide. Écrivez-donc quelque chose pour le remplir !",
   "empty_column.hashtag": "Il n’y a encore aucun contenu relatif à ce hashtag",
   "empty_column.home.public_timeline": "le fil public",
@@ -47,9 +60,7 @@ const fr = {
   "empty_column.public": "Il n’y a rien ici ! Écrivez quelque chose publiquement, ou bien suivez manuellement des utilisateurs⋅trices d’autres instances pour remplir le fil public.",
   "follow_request.authorize": "Autoriser",
   "follow_request.reject": "Rejeter",
-  "getting_started.about_addressing": "Vous pouvez suivre les statuts de quelqu’un en entrant dans le champ de recherche leur identifiant et le domaine de leur instance, séparés par un @ à la manière d’une adresse courriel.",
-  "getting_started.about_developer": "Pour suivre le développeur de ce projet, c’est Gargron@mastodon.social",
-  "getting_started.about_shortcuts": "Si cette personne utilise la même instance que vous, l’identifiant suffit. C’est le même principe pour mentionner quelqu’un dans vos statuts.",
+  "getting_started.apps": "Various apps are available",
   "getting_started.heading": "Pour commencer",
   "getting_started.open_source_notice": "Mastodon est un logiciel libre. Vous pouvez contribuer et envoyer vos commentaires et rapports de bogues via {github} sur GitHub.",
   "home.column_settings.advanced": "Avancé",
@@ -74,10 +85,9 @@ const fr = {
   "navigation_bar.public_timeline": "Fil public global",
   "notification.favourite": "{name} a ajouté à ses favoris :",
   "notification.follow": "{name} vous suit.",
-  "notification.mention": "{name} vous a mentionné⋅e :",
   "notification.reblog": "{name} a partagé votre statut :",
-  "notifications.clear_confirmation": "Voulez-vous vraiment supprimer toutes vos notifications ?",
   "notifications.clear": "Nettoyer",
+  "notifications.clear_confirmation": "Voulez-vous vraiment supprimer toutes vos notifications ?",
   "notifications.column_settings.alert": "Notifications locales",
   "notifications.column_settings.favourite": "Favoris :",
   "notifications.column_settings.follow": "Nouveaux abonné⋅e⋅s :",
@@ -86,6 +96,7 @@ const fr = {
   "notifications.column_settings.show": "Afficher dans la colonne",
   "notifications.column_settings.sound": "Émettre un son",
   "notifications.settings": "Paramètres de la colonne",
+  "onboarding.done": "Done",
   "onboarding.next": "Suivant",
   "onboarding.page_five.public_timelines": "Le fil public global affiche les posts de tou⋅te⋅s les utilisateurs⋅trices suivi⋅es par les membres de {domain}. Le fil public local est identique mais se limite aux utilisateurs⋅trices de {domain}.",
   "onboarding.page_four.home": "L’Accueil affiche les posts de tou⋅te⋅s les utilisateurs⋅trices que vous suivez",
@@ -118,20 +129,19 @@ const fr = {
   "report.placeholder": "Commentaires additionnels",
   "report.submit": "Envoyer",
   "report.target": "Signalement",
-  "search.account": "Compte",
-  "search.hashtag": "Mot-clé",
   "search.placeholder": "Rechercher",
   "search_results.total": "{count, number} {count, plural, one {résultat} other {résultats}}",
-  "search.status_by": "Statuts de {name}",
+  "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Effacer",
   "status.favourite": "Ajouter aux favoris",
   "status.load_more": "Charger plus",
   "status.media_hidden": "Média caché",
   "status.mention": "Mentionner",
   "status.open": "Déplier ce statut",
-  "status.reblogged_by": "{name} a partagé :",
   "status.reblog": "Partager",
+  "status.reblogged_by": "{name} a partagé :",
   "status.reply": "Répondre",
+  "status.replyAll": "Reply to thread",
   "status.report": "Signaler @{name}",
   "status.sensitive_toggle": "Cliquer pour dévoiler",
   "status.sensitive_warning": "Contenu délicat",
@@ -141,15 +151,13 @@ const fr = {
   "tabs_bar.federated_timeline": "Fil public global",
   "tabs_bar.home": "Accueil",
   "tabs_bar.local_timeline": "Fil public local",
-  "tabs_bar.mentions": "Mentions",
   "tabs_bar.notifications": "Notifications",
-  "tabs_bar.public": "Fil public global",
   "upload_area.title": "Glissez et déposez pour envoyer",
   "upload_button.label": "Joindre un média",
   "upload_form.undo": "Annuler",
   "upload_progress.label": "Envoi en cours…",
+  "video_player.expand": "Expand video",
   "video_player.toggle_sound": "Mettre/Couper le son",
   "video_player.toggle_visible": "Afficher/Cacher la vidéo",
-};
-
-export default fr;
+  "video_player.video_error": "Video could not be played"
+}
diff --git a/app/assets/javascripts/components/locales/he.jsx b/app/javascript/mastodon/locales/he.json
index 0fcb3d33e..f8945dc1c 100644
--- a/app/assets/javascripts/components/locales/he.jsx
+++ b/app/javascript/mastodon/locales/he.json
@@ -1,16 +1,6 @@
-/**
- * הערה לתורמים:
- * קובץ זה (he.jsx)מבוסס על en.jsx ויש לעדכנו מפעם לפעם כשיוצאות גרסאות חדשות.
- * אנא הקלו על התורמים העתידיים:
- *   1. הוסיפו לכאן מחרוזות חדשות
- *   2. הסירו מחרוזות ישנות שכבר לא בשימוש בגרסא האנגלית
- *   3. מיינו את השורות לפי סדר ABC כמו בקובץ המקורי.
- *   4. ובבקשה כבדו את סגנון התרגום שהנחלנו כאן, או תאמו איתנו אם ישנם שינויים יסודיים
- * תודה!
- */
-const he = {
+{
   "account.block": "חסימת @{name}",
-  "account.disclaimer": "&rlm;משתמש זה מגיע מקהילה אחרת. המספר הזה עשוי להיות גדול יותר.",
+  "account.disclaimer": "משתמש זה מגיע מקהילה אחרת. המספר הזה עשוי להיות גדול יותר.",
   "account.edit_profile": "עריכת פרופיל",
   "account.follow": "מעקב",
   "account.followers": "עוקבים",
@@ -38,9 +28,9 @@ const he = {
   "column_subheading.settings": "אפשרויות",
   "compose_form.lock_disclaimer": "חשבונך אינו {locked}. כל אחד יוכל לעקוב אחריך כדי לקרוא את הודעותיך המיועדות לעוקבים בלבד.",
   "compose_form.lock_disclaimer.lock": "נעול",
-  "compose_form.placeholder": "&rlm;מה עובר לך בראש?",
-  "compose_form.privacy_disclaimer": "&rlm;הודעתך הפרטית תשלח למשתמשים על {domains}. האם ניתן לסמוך על {domainsCount, plural, one {שרת זה} other {שרתים אלו}}? פרטיות ההודעה קיימת רק על שרתי מסטודון. אם {domains} {domainsCount, plural, one {הוא לא שרת מסטודון} other {הם לא שרתי מסטודון}}, לא יהיה שום סימן שההודעה פרטית, והוא עשוי להיות מקודם או להחשף למשתמשים שלא ברשימת היעד.",
-  "compose_form.publish": "&rlm;לחצרץ",
+  "compose_form.placeholder": "מה עובר לך בראש?",
+  "compose_form.privacy_disclaimer": "הודעתך הפרטית תשלח למשתמשים על {domains}. האם ניתן לסמוך על {domainsCount, plural, one {שרת זה} other {שרתים אלו}}? פרטיות ההודעה קיימת רק על שרתי מסטודון. אם {domains} {domainsCount, plural, one {הוא לא שרת מסטודון} other {הם לא שרתי מסטודון}}, לא יהיה שום סימן שההודעה פרטית, והוא עשוי להיות מקודם או להחשף למשתמשים שלא ברשימת היעד.",
+  "compose_form.publish": "לחצרץ",
   "compose_form.sensitive": "סימון תוכן כרגיש",
   "compose_form.spoiler": "הסתרה מאחורי אזהרת תוכן",
   "compose_form.spoiler_placeholder": "אזהרת תוכן",
@@ -58,15 +48,15 @@ const he = {
   "emoji_button.nature": "טבע",
   "emoji_button.objects": "חפצים",
   "emoji_button.people": "אנשים",
-  "emoji_button.search": "&rlm;חיפוש...",
+  "emoji_button.search": "חיפוש...",
   "emoji_button.symbols": "סמלים",
   "emoji_button.travel": "טיולים ואתרים",
-  "empty_column.community": "&rlm;טור הסביבה ריק. יש לפרסם משהו כדי שדברים יתרחילו להתגלגל!",
-  "empty_column.hashtag": "&rlm;אין כלום בהאשתג הזה עדיין.",
+  "empty_column.community": "טור הסביבה ריק. יש לפרסם משהו כדי שדברים יתרחילו להתגלגל!",
+  "empty_column.hashtag": "אין כלום בהאשתג הזה עדיין.",
   "empty_column.home.public_timeline": "בפרהסיה",
-  "empty_column.home": "&rlm;אף אחד לא במעקב עדיין. אפשר לבקר ב{public} או להשתמש בחיפוש כדי להתחיל ולהכיר חצוצרנים אחרים.",
-  "empty_column.notifications": "&rlm;אין התראות עדיין. יאללה, הגיע הזמן להתחיל להתערבב!",
-  "empty_column.public": "&rlm;אין פה כלום! כדי למלא את הטור הזה אפשר לכתוב משהו, או להתחיל לעקוב אחרי אנשים מקהילות אחרות.",
+  "empty_column.home": "אף אחד לא במעקב עדיין. אפשר לבקר ב{public} או להשתמש בחיפוש כדי להתחיל ולהכיר חצוצרנים אחרים.",
+  "empty_column.notifications": "אין התראות עדיין. יאללה, הגיע הזמן להתחיל להתערבב!",
+  "empty_column.public": "אין פה כלום! כדי למלא את הטור הזה אפשר לכתוב משהו, או להתחיל לעקוב אחרי אנשים מקהילות אחרות.",
   "follow_request.authorize": "קבלה",
   "follow_request.reject": "דחיה",
   "getting_started.apps": "קיים מבחר יישומונים לניידים",
@@ -74,7 +64,7 @@ const he = {
   "getting_started.open_source_notice": "מסטודון היא תוכנה חופשית (בקוד פתוח). ניתן לתרום או לדווח על בעיות בגיטהאב: {github}. {apps}.",
   "home.column_settings.advanced": "למתקדמים",
   "home.column_settings.basic": "למתחילים",
-  "home.column_settings.filter_regex": "&rlm;סינון באמצעות ביטויים רגולריים (regular expressions)",
+  "home.column_settings.filter_regex": "סינון באמצעות ביטויים רגולריים (regular expressions)",
   "home.column_settings.show_reblogs": "הצגת הדהודים",
   "home.column_settings.show_replies": "הצגת תגובות",
   "home.settings": "הגדרות טור",
@@ -94,15 +84,15 @@ const he = {
   "navigation_bar.public_timeline": "בפרהסיה",
   "notification.favourite": "חצרוצך חובב על ידי {name}",
   "notification.follow": "{name} במעקב אחרייך",
-  "notification.mention": 'אוזכרת ע"י {name}',
+  "notification.mention": "אוזכרת ע\"י {name}",
   "notification.reblog": "חצרוצך הודהד על ידי {name}",
   "notifications.clear": "הסרת התראות",
-  "notifications.clear_confirmation": "&rlm;להסיר את כל ההתראות? בטוח?",
+  "notifications.clear_confirmation": "להסיר את כל ההתראות? בטוח?",
   "notifications.column_settings.alert": "התראות לשולחן העבודה",
   "notifications.column_settings.favourite": "מחובבים:",
   "notifications.column_settings.follow": "עוקבים חדשים:",
-  "notifications.column_settings.mention": "&rlm;פניות:",
-  "notifications.column_settings.reblog": "&rlm;הדהודים:",
+  "notifications.column_settings.mention": "פניות:",
+  "notifications.column_settings.reblog": "הדהודים:",
   "notifications.column_settings.show": "הצגה בטור",
   "notifications.column_settings.sound": "שמע מופעל",
   "notifications.settings": "הגדרות טור",
@@ -120,7 +110,7 @@ const he = {
   "onboarding.page_six.apps_available": "קיימים {apps} זמינים עבור אנדרואיד, אייפון ופלטפורמות נוספות.",
   "onboarding.page_six.github": "מסטודון הוא תוכנה חופשית. ניתן לדווח על באגים, לבקש יכולות, או לתרום לקוד באתר {github}.",
   "onboarding.page_six.guidelines": "חוקי הקהילה",
-  "onboarding.page_six.read_guidelines": "&rlm;נא לקרוא את {guidelines} של {domain}!",
+  "onboarding.page_six.read_guidelines": "נא לקרוא את {guidelines} של {domain}!",
   "onboarding.page_six.various_app": "יישומונים ניידים",
   "onboarding.page_three.profile": "ץתחת 'עריכת פרופיל' ניתן להחליף את תמונת הפרופיל שלך, תיאור קצר, והשם המוצג. שם גם ניתן למצוא אפשרויות והעדפות נוספות.",
   "onboarding.page_three.search": "בחלונית החיפוש ניתן לחפש אנשים והאשתגים, כמו למשל {illustration} או {introductions}. כדי למצוא מישהו שלא על האינסטנס המקומי, יש להשתמש בכינוי המשתמש המלא.",
@@ -171,7 +161,5 @@ const he = {
   "video_player.expand": "הרחבת וידאו",
   "video_player.toggle_sound": "הפעלת\\ביטול שמע",
   "video_player.toggle_visible": "הפעלת\\ביטול תצוגה",
-  "video_player.video_error": "לא ניתן לנגן וידאו",
-};
-
-export default he;
+  "video_player.video_error": "לא ניתן לנגן וידאו"
+}
diff --git a/app/assets/javascripts/components/locales/hr.jsx b/app/javascript/mastodon/locales/hr.json
index 0ca3ef73e..45c3cb7f6 100644
--- a/app/assets/javascripts/components/locales/hr.jsx
+++ b/app/javascript/mastodon/locales/hr.json
@@ -1,11 +1,11 @@
-const hr = {
+{
   "account.block": "Blokiraj @{name}",
   "account.disclaimer": "Ovaj korisnik je sa druge instance. Ovaj broj bi mogao biti veći.",
   "account.edit_profile": "Uredi profil",
   "account.follow": "Slijedi",
   "account.followers": "Sljedbenici",
-  "account.follows_you": "te slijedi",
   "account.follows": "Slijedi",
+  "account.follows_you": "te slijedi",
   "account.mention": "Spomeni @{name}",
   "account.mute": "Utišaj @{name}",
   "account.posts": "Postovi",
@@ -15,25 +15,46 @@ const hr = {
   "account.unfollow": "Prestani slijediti",
   "account.unmute": "Poništi utišavanje @{name}",
   "boost_modal.combo": "Možeš pritisnuti {combo} kako bi ovo preskočio sljedeći put",
-  "column_back_button.label": "Natrag",
   "column.blocks": "Blokirani korisnici",
   "column.community": "Lokalni timeline",
   "column.favourites": "Favoriti",
   "column.follow_requests": "Zahtjevi za slijeđenje",
   "column.home": "Dom",
+  "column.mutes": "Muted users",
   "column.notifications": "Notifikacije",
   "column.public": "Federalni timeline",
+  "column_back_button.label": "Natrag",
+  "column_subheading.navigation": "Navigation",
+  "column_subheading.settings": "Settings",
+  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+  "compose_form.lock_disclaimer.lock": "locked",
   "compose_form.placeholder": "Što ti je na umu?",
   "compose_form.privacy_disclaimer": "Tvoj privatni status će biti dostavljen spomenutim korisnicima na {domains}. Vjeruješ li {domainsCount, plural, one {that server} drugim {those servers}}? Privatnost postova radi samo na Mastodon instancama. Ako {domains} {domainsCount, plural, one {is not a Mastodon instance} other {are not Mastodon instances}}, neće biti indikacije da je tvoj post privatan, i mogao bit biti podignut ili biti učinjen vidljivim na drugi način neželjenim primateljima.",
   "compose_form.publish": "Toot",
   "compose_form.sensitive": "Označi media sadržaj kao osjetljiv",
-  "compose_form.spoiler_placeholder": "Upozorenje o sadržaju",
   "compose_form.spoiler": "Sakrij text iza upozorenja",
+  "compose_form.spoiler_placeholder": "Upozorenje o sadržaju",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.confirm": "Block",
+  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.message": "Are you sure you want to mute {name}?",
+  "emoji_button.activity": "Activity",
+  "emoji_button.flags": "Flags",
+  "emoji_button.food": "Food & Drink",
   "emoji_button.label": "Umetni smajlije",
+  "emoji_button.nature": "Nature",
+  "emoji_button.objects": "Objects",
+  "emoji_button.people": "People",
+  "emoji_button.search": "Search...",
+  "emoji_button.symbols": "Symbols",
+  "emoji_button.travel": "Travel & Places",
   "empty_column.community": "Lokalni timeline je prazan. Napiši nešto javno kako bi pokrenuo stvari!",
   "empty_column.hashtag": "Još ne postoji ništa s ovim hashtagom.",
-  "empty_column.home.public_timeline": "javni timeline",
   "empty_column.home": "Još ne slijediš nikoga. Posjeti {public} ili koristi tražilicu kako bi počeo i upoznao druge korisnike.",
+  "empty_column.home.public_timeline": "javni timeline",
   "empty_column.notifications": "Još nemaš notifikacija. Komuniciraj sa drugima kako bi započeo razgovor.",
   "empty_column.public": "Ovdje nema ništa! Napiši nešto javno, ili ručno slijedi korisnike sa drugih instanci kako bi popunio",
   "follow_request.authorize": "Authoriziraj",
@@ -58,13 +79,14 @@ const hr = {
   "navigation_bar.follow_requests": "Zahtjevi za sljeđenje",
   "navigation_bar.info": "Proširena informacija",
   "navigation_bar.logout": "Odjavi se",
+  "navigation_bar.mutes": "Muted users",
   "navigation_bar.preferences": "Postavke",
   "navigation_bar.public_timeline": "Federalni timeline",
   "notification.favourite": "{name} je lajkao tvoj status",
   "notification.follow": "{name} te sada slijedi",
   "notification.reblog": "{name} je podigao tvoj status",
-  "notifications.clear_confirmation": "Želiš li zaista obrisati sve svoje notifikacije?",
   "notifications.clear": "Očisti notifikacije",
+  "notifications.clear_confirmation": "Želiš li zaista obrisati sve svoje notifikacije?",
   "notifications.column_settings.alert": "Desktop notifikacije",
   "notifications.column_settings.favourite": "Favoriti:",
   "notifications.column_settings.follow": "Novi sljedbenici:",
@@ -73,6 +95,26 @@ const hr = {
   "notifications.column_settings.show": "Prikaži u stupcu",
   "notifications.column_settings.sound": "Sviraj zvuk",
   "notifications.settings": "Postavke rubrike",
+  "onboarding.done": "Done",
+  "onboarding.next": "Next",
+  "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+  "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
+  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to Mastodon!",
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
+  "onboarding.page_six.almost_done": "Almost done...",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
+  "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "community guidelines",
+  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
+  "onboarding.page_six.various_app": "mobile apps",
+  "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+  "onboarding.skip": "Skip",
   "privacy.change": "Podesi status privatnosti",
   "privacy.direct.long": "Prikaži samo spomenutim korisnicima",
   "privacy.direct.short": "Direktno",
@@ -87,9 +129,9 @@ const hr = {
   "report.placeholder": "Dodatni komentari",
   "report.submit": "Pošalji",
   "report.target": "Prijavljivanje",
-  "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
   "search.placeholder": "Traži",
-  "search.status_by": "Status od {name}",
+  "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
+  "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Obriši",
   "status.favourite": "Označi omiljenim",
   "status.load_more": "Učitaj više",
@@ -99,6 +141,7 @@ const hr = {
   "status.reblog": "Podigni",
   "status.reblogged_by": "{name} je podigao",
   "status.reply": "Odgovori",
+  "status.replyAll": "Reply to thread",
   "status.report": "Prijavi @{name}",
   "status.sensitive_toggle": "Klikni da bi vidio",
   "status.sensitive_warning": "Osjetljiv sadržaj",
@@ -113,9 +156,8 @@ const hr = {
   "upload_button.label": "Dodaj media",
   "upload_form.undo": "Poništi",
   "upload_progress.label": "Uploadam...",
+  "video_player.expand": "Proširi video",
   "video_player.toggle_sound": "Toggle zvuk",
   "video_player.toggle_visible": "Preklopi vidljivost",
-  "video_player.expand": "Proširi video",
-};
-
-export default hr;
+  "video_player.video_error": "Video could not be played"
+}
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json
new file mode 100644
index 000000000..e1d9d36be
--- /dev/null
+++ b/app/javascript/mastodon/locales/hu.json
@@ -0,0 +1,163 @@
+{
+  "account.block": "Blokkolás",
+  "account.disclaimer": "This user is from another instance. This number may be larger.",
+  "account.edit_profile": "Profil szerkesztése",
+  "account.follow": "Követés",
+  "account.followers": "Követők",
+  "account.follows": "Követve",
+  "account.follows_you": "Követnek téged",
+  "account.mention": "Említés",
+  "account.mute": "Mute @{name}",
+  "account.posts": "Posts",
+  "account.report": "Report @{name}",
+  "account.requested": "Awaiting approval",
+  "account.unblock": "Blokkolás levétele",
+  "account.unfollow": "Követés abbahagyása",
+  "account.unmute": "Unmute @{name}",
+  "boost_modal.combo": "You can press {combo} to skip this next time",
+  "column.blocks": "Blocked users",
+  "column.community": "Local timeline",
+  "column.favourites": "Favourites",
+  "column.follow_requests": "Follow requests",
+  "column.home": "Kezdőlap",
+  "column.mutes": "Muted users",
+  "column.notifications": "Értesítések",
+  "column.public": "Nyilvános",
+  "column_back_button.label": "Vissza",
+  "column_subheading.navigation": "Navigation",
+  "column_subheading.settings": "Settings",
+  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+  "compose_form.lock_disclaimer.lock": "locked",
+  "compose_form.placeholder": "Mire gondolsz?",
+  "compose_form.privacy_disclaimer": "Your private status will be delivered to mentioned users on {domains}. Do you trust {domainsCount, plural, one {that server} other {those servers}}? Post privacy only works on Mastodon instances. If {domains} {domainsCount, plural, one {is not a Mastodon instance} other {are not Mastodon instances}}, there will be no indication that your post is private, and it may be boosted or otherwise made visible to unintended recipients.",
+  "compose_form.publish": "Tülk!",
+  "compose_form.sensitive": "Tartalom érzékenynek jelölése",
+  "compose_form.spoiler": "Hide text behind warning",
+  "compose_form.spoiler_placeholder": "Content warning",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.confirm": "Block",
+  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.message": "Are you sure you want to mute {name}?",
+  "emoji_button.activity": "Activity",
+  "emoji_button.flags": "Flags",
+  "emoji_button.food": "Food & Drink",
+  "emoji_button.label": "Insert emoji",
+  "emoji_button.nature": "Nature",
+  "emoji_button.objects": "Objects",
+  "emoji_button.people": "People",
+  "emoji_button.search": "Search...",
+  "emoji_button.symbols": "Symbols",
+  "emoji_button.travel": "Travel & Places",
+  "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
+  "empty_column.hashtag": "There is nothing in this hashtag yet.",
+  "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.",
+  "empty_column.home.public_timeline": "the public timeline",
+  "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
+  "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up",
+  "follow_request.authorize": "Authorize",
+  "follow_request.reject": "Reject",
+  "getting_started.apps": "Various apps are available",
+  "getting_started.heading": "Első lépések",
+  "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}. {apps}.",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.basic": "Basic",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_reblogs": "Show boosts",
+  "home.column_settings.show_replies": "Show replies",
+  "home.settings": "Column settings",
+  "lightbox.close": "Bezárás",
+  "loading_indicator.label": "Betöltés...",
+  "media_gallery.toggle_visible": "Toggle visibility",
+  "missing_indicator.label": "Not found",
+  "navigation_bar.blocks": "Blocked users",
+  "navigation_bar.community_timeline": "Local timeline",
+  "navigation_bar.edit_profile": "Profil szerkesztése",
+  "navigation_bar.favourites": "Favourites",
+  "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.info": "Extended information",
+  "navigation_bar.logout": "Kijelentkezés",
+  "navigation_bar.mutes": "Muted users",
+  "navigation_bar.preferences": "Beállítások",
+  "navigation_bar.public_timeline": "Nyilvános időfolyam",
+  "notification.favourite": "{name} kedvencnek jelölte az állapotod",
+  "notification.follow": "{name} követ téged",
+  "notification.reblog": "{name} reblogolta az állapotod",
+  "notifications.clear": "Clear notifications",
+  "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
+  "notifications.column_settings.alert": "Desktop notifications",
+  "notifications.column_settings.favourite": "Favourites:",
+  "notifications.column_settings.follow": "New followers:",
+  "notifications.column_settings.mention": "Mentions:",
+  "notifications.column_settings.reblog": "Boosts:",
+  "notifications.column_settings.show": "Show in column",
+  "notifications.column_settings.sound": "Play sound",
+  "notifications.settings": "Column settings",
+  "onboarding.done": "Done",
+  "onboarding.next": "Next",
+  "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+  "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
+  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to Mastodon!",
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
+  "onboarding.page_six.almost_done": "Almost done...",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
+  "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "community guidelines",
+  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
+  "onboarding.page_six.various_app": "mobile apps",
+  "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+  "onboarding.skip": "Skip",
+  "privacy.change": "Adjust status privacy",
+  "privacy.direct.long": "Post to mentioned users only",
+  "privacy.direct.short": "Direct",
+  "privacy.private.long": "Post to followers only",
+  "privacy.private.short": "Followers-only",
+  "privacy.public.long": "Post to public timelines",
+  "privacy.public.short": "Public",
+  "privacy.unlisted.long": "Do not show in public timelines",
+  "privacy.unlisted.short": "Unlisted",
+  "reply_indicator.cancel": "Mégsem",
+  "report.heading": "New report",
+  "report.placeholder": "Additional comments",
+  "report.submit": "Submit",
+  "report.target": "Reporting",
+  "search.placeholder": "Keresés",
+  "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
+  "status.cannot_reblog": "This post cannot be boosted",
+  "status.delete": "Törlés",
+  "status.favourite": "Kedvenc",
+  "status.load_more": "Load more",
+  "status.media_hidden": "Media hidden",
+  "status.mention": "Említés",
+  "status.open": "Expand this status",
+  "status.reblog": "Reblog",
+  "status.reblogged_by": "{name} reblogolta",
+  "status.reply": "Válasz",
+  "status.replyAll": "Reply to thread",
+  "status.report": "Report @{name}",
+  "status.sensitive_toggle": "Katt a megtekintéshez",
+  "status.sensitive_warning": "Érzékeny tartalom",
+  "status.show_less": "Show less",
+  "status.show_more": "Show more",
+  "tabs_bar.compose": "Összeállítás",
+  "tabs_bar.federated_timeline": "Federated",
+  "tabs_bar.home": "Kezdőlap",
+  "tabs_bar.local_timeline": "Local",
+  "tabs_bar.notifications": "Notifications",
+  "upload_area.title": "Drag & drop to upload",
+  "upload_button.label": "Média hozzáadása",
+  "upload_form.undo": "Mégsem",
+  "upload_progress.label": "Uploading...",
+  "video_player.expand": "Expand video",
+  "video_player.toggle_sound": "Hang kapcsolása",
+  "video_player.toggle_visible": "Toggle visibility",
+  "video_player.video_error": "Video could not be played"
+}
\ No newline at end of file
diff --git a/app/assets/javascripts/components/locales/id.jsx b/app/javascript/mastodon/locales/id.json
index 08ea6bf15..d73915a36 100644
--- a/app/assets/javascripts/components/locales/id.jsx
+++ b/app/javascript/mastodon/locales/id.json
@@ -1,11 +1,11 @@
-const id = {
+{
   "account.block": "Blokir @{name}",
   "account.disclaimer": "Pengguna ini berasal dari server lain. Angka berikut mungkin lebih besar.",
   "account.edit_profile": "Ubah profil",
   "account.follow": "Ikuti",
   "account.followers": "Pengikut",
-  "account.follows_you": "Mengikuti anda",
   "account.follows": "Mengikuti",
+  "account.follows_you": "Mengikuti anda",
   "account.mention": "Balasan @{name}",
   "account.mute": "Bisukan @{name}",
   "account.posts": "Postingan",
@@ -53,8 +53,8 @@ const id = {
   "emoji_button.travel": "Tempat Wisata",
   "empty_column.community": "Linimasa lokal masih kosong. Tulis sesuatu secara publik dan buat roda berputar!",
   "empty_column.hashtag": "Tidak ada apapun dalam hashtag ini.",
-  "empty_column.home.public_timeline": "linimasa publik",
   "empty_column.home": "Anda sedang tidak mengikuti siapapun. Kunjungi {public} atau gunakan pencarian untuk memulai dan bertemu pengguna lain.",
+  "empty_column.home.public_timeline": "linimasa publik",
   "empty_column.notifications": "Anda tidak memiliki notifikasi apapun. Berinteraksi dengan orang lain untuk memulai percakapan.",
   "empty_column.public": "Tidak ada apapun disini! Tulis sesuatu, atau ikuti pengguna lain dari server lain untuk mengisinya secara manual",
   "follow_request.authorize": "Izinkan",
@@ -133,7 +133,8 @@ const id = {
   "search_results.total": "{count} {count, plural, one {hasil} other {hasil}}",
   "status.cannot_reblog": "Postingan ini tidak dapat di-boost",
   "search.placeholder": "Pencarian",
-  "search.status_by": "Status oleh {name}",
+  "search_results.total": "{count} {count, plural, one {hasil} other {hasil}}",
+  "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Hapus",
   "status.favourite": "Difavoritkan",
   "status.load_more": "Tampilkan semua",
@@ -158,10 +159,9 @@ const id = {
   "upload_button.label": "Tambahkan media",
   "upload_form.undo": "Undo",
   "upload_progress.label": "Mengunggah...",
+  "video_player.expand": "Tampilkan video",
   "video_player.toggle_sound": "Suara",
   "video_player.toggle_visible": "Tampilan",
   "video_player.expand": "Tampilkan video",
-  "video_player.video_error": "Video tidak dapat diputar",
-};
-
-export default id;
+  "video_player.video_error": "Video tidak dapat diputar"
+}
diff --git a/app/javascript/mastodon/locales/index.js b/app/javascript/mastodon/locales/index.js
new file mode 100644
index 000000000..c4d580ff5
--- /dev/null
+++ b/app/javascript/mastodon/locales/index.js
@@ -0,0 +1,57 @@
+import ar from './ar.json';
+import en from './en.json';
+import de from './de.json';
+import es from './es.json';
+import fa from './fa.json';
+import he from './he.json';
+import hr from './hr.json';
+import hu from './hu.json';
+import io from './io.json';
+import it from './it.json';
+import fr from './fr.json';
+import nl from './nl.json';
+import no from './no.json';
+import oc from './oc.json';
+import pt from './pt.json';
+import pt_br from './pt-BR.json';
+import uk from './uk.json';
+import fi from './fi.json';
+import eo from './eo.json';
+import ru from './ru.json';
+import ja from './ja.json';
+import zh_hk from './zh-HK.json';
+import zh_cn from './zh-CN.json';
+import bg from './bg.json';
+import id from './id.json';
+
+const locales = {
+  ar,
+  en,
+  de,
+  es,
+  fa,
+  he,
+  hr,
+  hu,
+  io,
+  it,
+  fr,
+  nl,
+  no,
+  oc,
+  pt,
+  'pt-BR': pt_br,
+  uk,
+  fi,
+  eo,
+  ru,
+  ja,
+  'zh-HK': zh_hk,
+  'zh-CN': zh_cn,
+  bg,
+  id,
+};
+
+export default function getMessagesForLocale(locale) {
+  return locales[locale];
+};
diff --git a/app/assets/javascripts/components/locales/io.jsx b/app/javascript/mastodon/locales/io.json
index 6715663aa..bcf89cfc2 100644
--- a/app/assets/javascripts/components/locales/io.jsx
+++ b/app/javascript/mastodon/locales/io.json
@@ -1,11 +1,11 @@
-const io = {
+{
   "account.block": "Blokusar @{name}",
   "account.disclaimer": "Ca uzero esas de altra instaluro. Ca nombro forsan esas plu granda.",
   "account.edit_profile": "Modifikar profilo",
   "account.follow": "Sequar",
   "account.followers": "Sequanti",
-  "account.follows_you": "Sequas tu",
   "account.follows": "Sequas",
+  "account.follows_you": "Sequas tu",
   "account.mention": "Mencionar @{name}",
   "account.mute": "Celar @{name}",
   "account.posts": "Mesaji",
@@ -15,7 +15,6 @@ const io = {
   "account.unfollow": "Ne plus sequar",
   "account.unmute": "Ne plus celar @{name}",
   "boost_modal.combo": "Tu povas presar sur {combo} por omisar co en la venonta foyo",
-  "column_back_button.label": "Retro",
   "column.blocks": "Blokusita uzeri",
   "column.community": "Lokala tempolineo",
   "column.favourites": "Favorati",
@@ -24,17 +23,38 @@ const io = {
   "column.mutes": "Celita uzeri",
   "column.notifications": "Savigi",
   "column.public": "Federata tempolineo",
+  "column_back_button.label": "Retro",
+  "column_subheading.navigation": "Navigation",
+  "column_subheading.settings": "Settings",
+  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+  "compose_form.lock_disclaimer.lock": "locked",
   "compose_form.placeholder": "Quo esas en tua spirito?",
   "compose_form.privacy_disclaimer": "Tua privata mesajo livresos a mencionata uzeri en {domains}. Ka tu fidas {domainsCount, plural, one {ta servero} other {ta serveri}}? Privateso di mesaji funcionas nur en instaluri di Mastodon. Se {domains} {domainsCount, plural, one {ne esas instaluro di Mastodon} other {ne esas instaluri di Mastodon}}, esos nula indiko, ke tua mesajo esas privata, ed ol povos repetesar od altre divenar videbla da nedezirinda recevanti.",
   "compose_form.publish": "Siflar",
   "compose_form.sensitive": "Markizar kontenajo kom trubliva",
-  "compose_form.spoiler_placeholder": "Averto di kontenajo",
   "compose_form.spoiler": "Celar texto dop averto",
+  "compose_form.spoiler_placeholder": "Averto di kontenajo",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.confirm": "Block",
+  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.message": "Are you sure you want to mute {name}?",
+  "emoji_button.activity": "Activity",
+  "emoji_button.flags": "Flags",
+  "emoji_button.food": "Food & Drink",
   "emoji_button.label": "Insertar emoji",
+  "emoji_button.nature": "Nature",
+  "emoji_button.objects": "Objects",
+  "emoji_button.people": "People",
+  "emoji_button.search": "Search...",
+  "emoji_button.symbols": "Symbols",
+  "emoji_button.travel": "Travel & Places",
   "empty_column.community": "La lokala tempolineo esas vakua. Skribez ulo publike por iniciar la agiveso!",
   "empty_column.hashtag": "Esas ankore nulo en ta gretovorto.",
-  "empty_column.home.public_timeline": "la publika tempolineo",
   "empty_column.home": "Tu sequas ankore nulu. Vizitez {public} od uzez la serchilo por komencar e renkontrar altra uzeri.",
+  "empty_column.home.public_timeline": "la publika tempolineo",
   "empty_column.notifications": "Tu havas ankore nula savigo. Komunikez kun altri por debutar la konverso.",
   "empty_column.public": "Esas nulo hike! Skribez ulo publike, o manuale sequez uzeri de altra instaluri por plenigar ol.",
   "follow_request.authorize": "Yurizar",
@@ -64,10 +84,9 @@ const io = {
   "navigation_bar.public_timeline": "Federata tempolineo",
   "notification.favourite": "{name} favorizis tua mesajo",
   "notification.follow": "{name} sequeskis tu",
-  "notification.mention": "{name} mencionis tu",
   "notification.reblog": "{name} repetis tua mesajo",
-  "notifications.clear_confirmation": "Ka tu esas certa, ke tu volas efacar omna tua savigi?",
   "notifications.clear": "Efacar savigi",
+  "notifications.clear_confirmation": "Ka tu esas certa, ke tu volas efacar omna tua savigi?",
   "notifications.column_settings.alert": "Surtabla savigi",
   "notifications.column_settings.favourite": "Favorati:",
   "notifications.column_settings.follow": "Nova sequanti:",
@@ -76,6 +95,26 @@ const io = {
   "notifications.column_settings.show": "Montrar en kolumno",
   "notifications.column_settings.sound": "Plear sono",
   "notifications.settings": "Aranji di kolumno",
+  "onboarding.done": "Done",
+  "onboarding.next": "Next",
+  "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+  "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
+  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to Mastodon!",
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
+  "onboarding.page_six.almost_done": "Almost done...",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
+  "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "community guidelines",
+  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
+  "onboarding.page_six.various_app": "mobile apps",
+  "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+  "onboarding.skip": "Skip",
   "privacy.change": "Aranjar privateso di mesaji",
   "privacy.direct.long": "Sendar nur a mencionata uzeri",
   "privacy.direct.short": "Direte",
@@ -90,9 +129,9 @@ const io = {
   "report.placeholder": "Plusa komenti",
   "report.submit": "Sendar",
   "report.target": "Denuncante",
-  "search_results.total": "{count, number} {count, plural, one {rezulto} other {rezulti}}",
   "search.placeholder": "Serchez",
-  "search.status_by": "Mesajo da {name}",
+  "search_results.total": "{count, number} {count, plural, one {rezulto} other {rezulti}}",
+  "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Efacar",
   "status.favourite": "Favorizar",
   "status.load_more": "Kargar pluse",
@@ -117,10 +156,8 @@ const io = {
   "upload_button.label": "Adjuntar kontenajo",
   "upload_form.undo": "Desfacar",
   "upload_progress.label": "Kargante...",
+  "video_player.expand": "Extensar video",
   "video_player.toggle_sound": "Acendar sono",
   "video_player.toggle_visible": "Chanjar videbleso",
-  "video_player.expand": "Extensar video",
-  "video_player.video_error": "Video ne povus pleesar",
-};
-
-export default io;
+  "video_player.video_error": "Video ne povus pleesar"
+}
\ No newline at end of file
diff --git a/app/assets/javascripts/components/locales/it.jsx b/app/javascript/mastodon/locales/it.json
index 04ff1311f..0342f1e1f 100644
--- a/app/assets/javascripts/components/locales/it.jsx
+++ b/app/javascript/mastodon/locales/it.json
@@ -1,11 +1,11 @@
-const it = {
+{
   "account.block": "Blocca @{name}",
   "account.disclaimer": "Questo utente si trova su un altro server. Questo numero potrebbe essere maggiore.",
   "account.edit_profile": "Modifica profilo",
   "account.follow": "Segui",
   "account.followers": "Seguaci",
-  "account.follows_you": "Ti segue",
   "account.follows": "Segue",
+  "account.follows_you": "Ti segue",
   "account.mention": "Menziona @{name}",
   "account.mute": "Silenzia @{name}",
   "account.posts": "Posts",
@@ -15,7 +15,6 @@ const it = {
   "account.unfollow": "Non seguire",
   "account.unmute": "Non silenziare @{name}",
   "boost_modal.combo": "Puoi premere {combo} per saltare questo passaggio la prossima volta",
-  "column_back_button.label": "Indietro",
   "column.blocks": "Utenti bloccati",
   "column.community": "Timeline locale",
   "column.favourites": "Apprezzati",
@@ -24,17 +23,38 @@ const it = {
   "column.mutes": "Utenti silenziati",
   "column.notifications": "Notifiche",
   "column.public": "Timeline federata",
+  "column_back_button.label": "Indietro",
+  "column_subheading.navigation": "Navigation",
+  "column_subheading.settings": "Settings",
+  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+  "compose_form.lock_disclaimer.lock": "locked",
   "compose_form.placeholder": "A cosa stai pensando?",
   "compose_form.privacy_disclaimer": "Il tuo status privato verrà condiviso con gli utenti menzionati su {domains}. Ti fidi di {domainsCount, plural, one {quel server} other {quei server}}? Le impostazioni sulla privacy valgono solo su server Mastodon. Se {domains} {domainsCount, plural, one {non è un server Mastodon} other {non sono server Mastodon}}, non ci saranno indicazioni sulla privacy del tuo status, e potrebbe essere condiviso o reso visibile a destinatari indesiderati.",
   "compose_form.publish": "Toot",
   "compose_form.sensitive": "Segnala file come sensibile",
-  "compose_form.spoiler_placeholder": "Content warning",
   "compose_form.spoiler": "Nascondi testo con avvertimento",
+  "compose_form.spoiler_placeholder": "Content warning",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.confirm": "Block",
+  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.message": "Are you sure you want to mute {name}?",
+  "emoji_button.activity": "Activity",
+  "emoji_button.flags": "Flags",
+  "emoji_button.food": "Food & Drink",
   "emoji_button.label": "Inserisci emoji",
+  "emoji_button.nature": "Nature",
+  "emoji_button.objects": "Objects",
+  "emoji_button.people": "People",
+  "emoji_button.search": "Search...",
+  "emoji_button.symbols": "Symbols",
+  "emoji_button.travel": "Travel & Places",
   "empty_column.community": "La timeline locale è vuota. Condividi qualcosa pubblicamente per dare inizio alla festa!",
   "empty_column.hashtag": "Non c'è ancora nessun post con questo hashtag.",
-  "empty_column.home.public_timeline": "la timeline pubblica",
   "empty_column.home": "Non stai ancora seguendo nessuno. Visita {public} o usa la ricerca per incontrare nuove persone.",
+  "empty_column.home.public_timeline": "la timeline pubblica",
   "empty_column.notifications": "Non hai ancora nessuna notifica. Interagisci con altri per iniziare conversazioni.",
   "empty_column.public": "Qui non c'è nulla! Scrivi qualcosa pubblicamente, o aggiungi utenti da altri server per riempire questo spazio.",
   "follow_request.authorize": "Autorizza",
@@ -64,10 +84,9 @@ const it = {
   "navigation_bar.public_timeline": "Timeline federata",
   "notification.favourite": "{name} ha apprezzato il tuo post",
   "notification.follow": "{name} ha iniziato a seguirti",
-  "notification.mention": "{name} ti ha menzionato",
   "notification.reblog": "{name} ha condiviso il tuo post",
-  "notifications.clear_confirmation": "Vuoi davvero cancellare tutte le notifiche?",
   "notifications.clear": "Cancella notifiche",
+  "notifications.clear_confirmation": "Vuoi davvero cancellare tutte le notifiche?",
   "notifications.column_settings.alert": "Notifiche desktop",
   "notifications.column_settings.favourite": "Apprezzati:",
   "notifications.column_settings.follow": "Nuovi seguaci:",
@@ -76,6 +95,26 @@ const it = {
   "notifications.column_settings.show": "Mostra in colonna",
   "notifications.column_settings.sound": "Riproduci suono",
   "notifications.settings": "Impostazioni colonna",
+  "onboarding.done": "Done",
+  "onboarding.next": "Next",
+  "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+  "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
+  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to Mastodon!",
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
+  "onboarding.page_six.almost_done": "Almost done...",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
+  "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "community guidelines",
+  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
+  "onboarding.page_six.various_app": "mobile apps",
+  "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+  "onboarding.skip": "Skip",
   "privacy.change": "Modifica privacy post",
   "privacy.direct.long": "Invia solo a utenti menzionati",
   "privacy.direct.short": "Diretto",
@@ -90,9 +129,9 @@ const it = {
   "report.placeholder": "Commenti aggiuntivi",
   "report.submit": "Invia",
   "report.target": "Invio la segnalazione",
-  "search_results.total": "{count} {count, plural, one {risultato} other {risultati}}",
   "search.placeholder": "Cerca",
-  "search.status_by": "Status per {name}",
+  "search_results.total": "{count} {count, plural, one {risultato} other {risultati}}",
+  "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Elimina",
   "status.favourite": "Apprezzato",
   "status.load_more": "Mostra di più",
@@ -102,6 +141,7 @@ const it = {
   "status.reblog": "Condividi",
   "status.reblogged_by": "{name} ha condiviso",
   "status.reply": "Rispondi",
+  "status.replyAll": "Reply to thread",
   "status.report": "Segnala @{name}",
   "status.sensitive_toggle": "Clicca per vedere",
   "status.sensitive_warning": "Materiale sensibile",
@@ -116,10 +156,8 @@ const it = {
   "upload_button.label": "Aggiungi file multimediale",
   "upload_form.undo": "Annulla",
   "upload_progress.label": "Sto caricando...",
+  "video_player.expand": "Espandi video",
   "video_player.toggle_sound": "Attiva suono",
   "video_player.toggle_visible": "Attiva visibilità",
-  "video_player.expand": "Espandi video",
-  "video_player.video_error": "Il video non può essere riprodotto",
-};
-
-export default it;
\ No newline at end of file
+  "video_player.video_error": "Il video non può essere riprodotto"
+}
\ No newline at end of file
diff --git a/app/assets/javascripts/components/locales/ja.jsx b/app/javascript/mastodon/locales/ja.json
index 6a7536527..c9ddde083 100644
--- a/app/assets/javascripts/components/locales/ja.jsx
+++ b/app/javascript/mastodon/locales/ja.json
@@ -1,4 +1,4 @@
-const ja = {
+{
   "account.block": "ブロック",
   "account.disclaimer": "このユーザーは他のインスタンスに所属しているため、数字が正確で無い場合があります。",
   "account.edit_profile": "プロフィールを編集",
@@ -41,16 +41,16 @@ const ja = {
   "confirmations.delete.message": "本当に削除しますか?",
   "confirmations.mute.confirm": "ミュート",
   "confirmations.mute.message": "本当に {name} をミュートしますか?",
+  "emoji_button.activity": "活動",
+  "emoji_button.flags": "国旗",
+  "emoji_button.food": "食べ物",
   "emoji_button.label": "絵文字を追加",
-  "emoji_button.search": "検索...",
-  "emoji_button.people": "人々",
   "emoji_button.nature": "自然",
-  "emoji_button.food": "食べ物",
-  "emoji_button.activity": "活動",
-  "emoji_button.travel": "旅行と場所",
   "emoji_button.objects": "物",
+  "emoji_button.people": "人々",
+  "emoji_button.search": "検索...",
   "emoji_button.symbols": "記号",
-  "emoji_button.flags": "国旗",
+  "emoji_button.travel": "旅行と場所",
   "empty_column.community": "ローカルタイムラインはまだ使われていません。何か書いてみましょう!",
   "empty_column.hashtag": "このハッシュタグはまだ使われていません。",
   "empty_column.home": "まだ誰もフォローしていません。{public}を見に行くか、検索を使って他のユーザーを見つけましょう。",
@@ -84,7 +84,6 @@ const ja = {
   "navigation_bar.public_timeline": "連合タイムライン",
   "notification.favourite": "{name} さんがあなたのトゥートをお気に入りに登録しました",
   "notification.follow": "{name} さんにフォローされました",
-  "notification.mention": "{name} さんがあなたに返信しました",
   "notification.reblog": "{name} さんがあなたのトゥートをブーストしました",
   "notifications.clear": "通知を消去",
   "notifications.clear_confirmation": "本当に通知を消去しますか?",
@@ -98,23 +97,23 @@ const ja = {
   "notifications.settings": "カラム設定",
   "onboarding.done": "完了",
   "onboarding.next": "次へ",
-  "onboarding.page_one.welcome": "Mastodonへようこそ!",
-  "onboarding.page_one.federation": "Mastodonは誰でも参加できるSNSです。",
-  "onboarding.page_one.handle": "あなたは今数あるMastodonインスタンスの1つである{domain}にいます。あなたのフルハンドルは{handle}です。",
-  "onboarding.page_two.compose": "フォームから投稿できます。イメージや、公開範囲の設定や、表示時の警告の設定は下部のアイコンから行なえます。",
-  "onboarding.page_three.search": "検索バーで、{illustration}や{introductions}のように特定のハッシュタグの投稿を見たり、ユーザーを探したりできます。",
-  "onboarding.page_three.profile": "「プロフィールを編集」から、あなたの自己紹介や表示名を変更できます。またそこでは他の設定ができます。",
+  "onboarding.page_five.public_timelines": "連合タイムラインでは{domain}の人がフォローしているMastodon全体での公開投稿を表示します。同じくローカルタイムラインでは{domain}のみの公開投稿を表示します。",
   "onboarding.page_four.home": "「ホーム」タイムラインではあなたがフォローしている人の投稿を表示します。",
   "onboarding.page_four.notifications": "「通知」ではあなたへの他の人からの関わりを表示します。",
-  "onboarding.page_five.public_timelines": "連合タイムラインでは{domain}の人がフォローしているMastodon全体での公開投稿を表示します。同じくローカルタイムラインでは{domain}のみの公開投稿を表示します。",
-  "onboarding.page_six.almost_done": "以上です。",
+  "onboarding.page_one.federation": "Mastodonは誰でも参加できるSNSです。",
+  "onboarding.page_one.handle": "あなたは今数あるMastodonインスタンスの1つである{domain}にいます。あなたのフルハンドルは{handle}です。",
+  "onboarding.page_one.welcome": "Mastodonへようこそ!",
   "onboarding.page_six.admin": "あなたのインスタンスの管理者は{admin}です。",
-  "onboarding.page_six.read_guidelines": "{guidelines}を読むことを忘れないようにしてください。",
-  "onboarding.page_six.guidelines": "コミュニティガイドライン",
-  "onboarding.page_six.github": "MastodonはOSSです。バグ報告や機能要望あるいは貢献を{github}から行なえます。",
+  "onboarding.page_six.almost_done": "以上です。",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
   "onboarding.page_six.apps_available": "iOS、Androidあるいは他のプラットフォームで使える{apps}があります。",
+  "onboarding.page_six.github": "MastodonはOSSです。バグ報告や機能要望あるいは貢献を{github}から行なえます。",
+  "onboarding.page_six.guidelines": "コミュニティガイドライン",
+  "onboarding.page_six.read_guidelines": "{guidelines}を読むことを忘れないようにしてください。",
   "onboarding.page_six.various_app": "様々なモバイルアプリ",
-  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_three.profile": "「プロフィールを編集」から、あなたの自己紹介や表示名を変更できます。またそこでは他の設定ができます。",
+  "onboarding.page_three.search": "検索バーで、{illustration}や{introductions}のように特定のハッシュタグの投稿を見たり、ユーザーを探したりできます。",
+  "onboarding.page_two.compose": "フォームから投稿できます。イメージや、公開範囲の設定や、表示時の警告の設定は下部のアイコンから行なえます。",
   "onboarding.skip": "スキップ",
   "privacy.change": "投稿のプライバシーを変更",
   "privacy.direct.long": "メンションしたユーザーだけに公開",
@@ -131,7 +130,6 @@ const ja = {
   "report.submit": "通報する",
   "report.target": "問題のユーザー",
   "search.placeholder": "検索",
-  "search.status_by": "{name}からの投稿",
   "search_results.total": "{count, number} 件の結果",
   "status.cannot_reblog": "この投稿はブーストできません",
   "status.delete": "削除",
@@ -161,7 +159,5 @@ const ja = {
   "video_player.expand": "動画の詳細",
   "video_player.toggle_sound": "音の切り替え",
   "video_player.toggle_visible": "表示切り替え",
-  "video_player.video_error": "動画の再生に失敗しました",
-};
-
-export default ja;
+  "video_player.video_error": "動画の再生に失敗しました"
+}
\ No newline at end of file
diff --git a/app/assets/javascripts/components/locales/nl.jsx b/app/javascript/mastodon/locales/nl.json
index 388169cd5..730ef246d 100644
--- a/app/assets/javascripts/components/locales/nl.jsx
+++ b/app/javascript/mastodon/locales/nl.json
@@ -1,10 +1,11 @@
-const nl = {
+{
   "account.block": "Blokkeer @{name}",
+  "account.disclaimer": "This user is from another instance. This number may be larger.",
   "account.edit_profile": "Profiel bewerken",
+  "account.follow": "Volgen",
   "account.followers": "Volgers",
   "account.follows": "Volgt",
   "account.follows_you": "Volgt jou",
-  "account.follow": "Volgen",
   "account.mention": "Vermeld @{name}",
   "account.mute": "Negeer @{name}",
   "account.posts": "Berichten",
@@ -14,24 +15,32 @@ const nl = {
   "account.unfollow": "Ontvolgen",
   "account.unmute": "Negeer @{name} niet meer",
   "boost_modal.combo": "Je kunt {combo} klikken om dit de volgende keer over te slaan",
-  "column_back_button.label": "terug",
   "column.blocks": "Geblokkeerde gebruikers",
   "column.community": "Lokale tijdlijn",
   "column.favourites": "Favorieten",
+  "column.follow_requests": "Follow requests",
   "column.home": "Jouw tijdlijn",
   "column.mutes": "Genegeerde gebruikers",
   "column.notifications": "Meldingen",
   "column.public": "Globale tijdlijn",
+  "column_back_button.label": "terug",
   "column_subheading.navigation": "Navigatie",
   "column_subheading.settings": "Instellingen",
+  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+  "compose_form.lock_disclaimer.lock": "locked",
   "compose_form.placeholder": "Wat wil je kwijt?",
   "compose_form.privacy_disclaimer": "Jouw privétoot wordt afgeleverd aan de vermelde gebruikers op {domains}. Vertrouw jij {domainsCount, plural, one {die server} other {die servers}}? Het privé plaatsen van toots werkt alleen op Mastodon-servers. Wanneer {domains} {domainsCount, plural, one {geen Mastodon-server is} other {geen Mastodon-servers zijn}}, dan wordt er niet aangegeven dat de toot privé is, waardoor het kan worden geboost of op een andere manier zichtbaar wordt gemaakt voor mensen waarvoor het niet was bedoeld.",
-  "compose_form.private": "Als privé markeren",
   "compose_form.publish": "Toot",
   "compose_form.sensitive": "Media als gevoelig markeren",
-  "compose_form.spoiler_placeholder": "Waarschuwingstekst",
   "compose_form.spoiler": "Tekst achter waarschuwing verbergen",
-  "compose_form.unlisted": "Niet op openbare tijdlijnen tonen",
+  "compose_form.spoiler_placeholder": "Waarschuwingstekst",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.confirm": "Block",
+  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.message": "Are you sure you want to mute {name}?",
   "emoji_button.activity": "Activiteiten",
   "emoji_button.flags": "Vlaggen",
   "emoji_button.food": "Eten en drinken",
@@ -42,13 +51,27 @@ const nl = {
   "emoji_button.search": "Zoeken...",
   "emoji_button.symbols": "Symbolen",
   "emoji_button.travel": "Reizen en plekken",
-  "getting_started.about_addressing": "Je kunt mensen volgen als je hun gebruikersnaam en het domein van hun server kent. Voer hiervoor het e-mailachtige adres in het zoekveld in.",
-  "getting_started.about_shortcuts": "Als de gezochte gebruiker op hetzelfde domein zit als jijzelf, is invoeren van de gebruikersnaam genoeg. Dat geldt ook als je mensen in toots wilt vermelden.",
+  "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
+  "empty_column.hashtag": "There is nothing in this hashtag yet.",
+  "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.",
+  "empty_column.home.public_timeline": "the public timeline",
+  "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
+  "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up",
+  "follow_request.authorize": "Authorize",
+  "follow_request.reject": "Reject",
   "getting_started.apps": "Er zijn meerdere apps beschikbaar",
   "getting_started.heading": "Beginnen",
   "getting_started.open_source_notice": "Mastodon is open-sourcesoftware. Je kunt bijdragen of problemen melden op GitHub via {github}. {apps}.",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.basic": "Basic",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_reblogs": "Show boosts",
+  "home.column_settings.show_replies": "Show replies",
+  "home.settings": "Column settings",
   "lightbox.close": "Sluiten",
   "loading_indicator.label": "Laden…",
+  "media_gallery.toggle_visible": "Toggle visibility",
+  "missing_indicator.label": "Not found",
   "navigation_bar.blocks": "Geblokkeerde gebruikers",
   "navigation_bar.community_timeline": "Lokale tijdlijn",
   "navigation_bar.edit_profile": "Profiel bewerken",
@@ -61,10 +84,9 @@ const nl = {
   "navigation_bar.public_timeline": "Globale tijdlijn",
   "notification.favourite": "{name} markeerde jouw toot als favoriet",
   "notification.follow": "{name} volgt jou nu",
-  "notification.mention": "{name} vermeldde jou",
   "notification.reblog": "{name} boostte jouw toot",
-  "notifications.clear_confirmation": "Weet je zeker dat je al jouw meldingen wilt verwijderen?",
   "notifications.clear": "Meldingen verwijderen",
+  "notifications.clear_confirmation": "Weet je zeker dat je al jouw meldingen wilt verwijderen?",
   "notifications.column_settings.alert": "Desktopmeldingen",
   "notifications.column_settings.favourite": "Favorieten:",
   "notifications.column_settings.follow": "Nieuwe volgers:",
@@ -73,6 +95,7 @@ const nl = {
   "notifications.column_settings.show": "In kolom tonen",
   "notifications.column_settings.sound": "Geluid afspelen",
   "notifications.settings": "Kolom-instellingen",
+  "onboarding.done": "Done",
   "onboarding.next": "Volgende",
   "onboarding.page_five.public_timelines": "De lokale tijdlijn toont openbare toots van iedereen op {domain}. De globale tijdlijn toont openbare toots van iedereen die door gebruikers van {domain} worden gevolgd, dus ook mensen van andere Mastodon-servers. Dit zijn de openbare tijdlijnen en vormen een uitstekende manier om nieuwe mensen te ontdekken.",
   "onboarding.page_four.home": "Jouw tijdlijn laat toots zien van mensen die jij volgt.",
@@ -102,29 +125,39 @@ const nl = {
   "privacy.unlisted.long": "Niet op openbare tijdlijnen tonen",
   "privacy.unlisted.short": "Minder openbaar",
   "reply_indicator.cancel": "Annuleren",
-  "search.account": "Account",
-  "search.hashtag": "Hashtag",
+  "report.heading": "New report",
+  "report.placeholder": "Additional comments",
+  "report.submit": "Submit",
+  "report.target": "Reporting",
   "search.placeholder": "Zoeken",
   "search_results.total": "{count, number} {count, plural, one {resultaat} other {resultaten}}",
+  "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Verwijderen",
   "status.favourite": "Favoriet",
+  "status.load_more": "Load more",
+  "status.media_hidden": "Media hidden",
   "status.mention": "@{name} vermelden",
+  "status.open": "Expand this status",
   "status.reblog": "Boost",
   "status.reblogged_by": "{name} boostte",
   "status.reply": "Reageren",
+  "status.replyAll": "Reply to thread",
+  "status.report": "Report @{name}",
   "status.sensitive_toggle": "Klik om te zien",
   "status.sensitive_warning": "Gevoelige inhoud",
   "status.show_less": "Minder tonen",
   "status.show_more": "Meer tonen",
   "tabs_bar.compose": "Schrijven",
+  "tabs_bar.federated_timeline": "Federated",
   "tabs_bar.home": "Jouw tijdlijn",
-  "tabs_bar.mentions": "Vermeldingen",
+  "tabs_bar.local_timeline": "Local",
   "tabs_bar.notifications": "Meldingen",
-  "tabs_bar.public": "Globale tijdlijn",
+  "upload_area.title": "Drag & drop to upload",
   "upload_button.label": "Media toevoegen",
   "upload_form.undo": "Ongedaan maken",
+  "upload_progress.label": "Uploading...",
+  "video_player.expand": "Expand video",
   "video_player.toggle_sound": "Geluid in-/uitschakelen",
-
-};
-
-export default nl;
+  "video_player.toggle_visible": "Toggle visibility",
+  "video_player.video_error": "Video could not be played"
+}
\ No newline at end of file
diff --git a/app/assets/javascripts/components/locales/no.jsx b/app/javascript/mastodon/locales/no.json
index 17a0c2099..25dee25a3 100644
--- a/app/assets/javascripts/components/locales/no.jsx
+++ b/app/javascript/mastodon/locales/no.json
@@ -1,4 +1,4 @@
-const no = {
+{
   "account.block": "Blokkér @{name}",
   "account.disclaimer": "Denne brukeren er fra en annen instans. Dette tallet kan være høyere.",
   "account.edit_profile": "Rediger profil",
@@ -15,25 +15,46 @@ const no = {
   "account.unfollow": "Avfølg",
   "account.unmute": "Avdemp @{name}",
   "boost_modal.combo": "You kan trykke {combo} for å hoppe over dette neste gang",
-  "column_back_button.label": "Tilbake",
   "column.blocks": "Blokkerte brukere",
   "column.community": "Lokal tidslinje",
   "column.favourites": "Likt",
   "column.follow_requests": "Følgeforespørsler",
   "column.home": "Hjem",
+  "column.mutes": "Muted users",
   "column.notifications": "Varslinger",
   "column.public": "Felles tidslinje",
+  "column_back_button.label": "Tilbake",
+  "column_subheading.navigation": "Navigation",
+  "column_subheading.settings": "Settings",
+  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+  "compose_form.lock_disclaimer.lock": "locked",
   "compose_form.placeholder": "Hva har du på hjertet?",
   "compose_form.privacy_disclaimer": "Din private status vil leveres til nevnte brukere på {domains}. Stoler du på {domainsCount, plural, one {den serveren} other {de serverne}}? Synlighet fungerer kun på Mastodon-instanser. Hvis {domains} {domainsCount, plural, one {ike er en Mastodon-instans} other {ikke er Mastodon-instanser}}, vil det ikke indikeres at posten din er privat, og den kan kanskje bli fremhevd eller på annen måte bli synlig for uventede mottakere.",
   "compose_form.publish": "Tut",
   "compose_form.sensitive": "Merk media som følsomt",
-  "compose_form.spoiler_placeholder": "Innholdsadvarsel",
   "compose_form.spoiler": "Skjul tekst bak advarsel",
+  "compose_form.spoiler_placeholder": "Innholdsadvarsel",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.confirm": "Block",
+  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.message": "Are you sure you want to mute {name}?",
+  "emoji_button.activity": "Activity",
+  "emoji_button.flags": "Flags",
+  "emoji_button.food": "Food & Drink",
   "emoji_button.label": "Sett inn emoji",
+  "emoji_button.nature": "Nature",
+  "emoji_button.objects": "Objects",
+  "emoji_button.people": "People",
+  "emoji_button.search": "Search...",
+  "emoji_button.symbols": "Symbols",
+  "emoji_button.travel": "Travel & Places",
   "empty_column.community": "Den lokale tidslinjen er tom. Skriv noe offentlig for å få snøballen til å rulle!",
   "empty_column.hashtag": "Det er ingenting i denne hashtagen ennå.",
-  "empty_column.home.public_timeline": "en offentlig tidslinje",
   "empty_column.home": "Du har ikke fulgt noen ennå. Besøk {publlic} eller bruk søk for å komme i gang og møte andre brukere.",
+  "empty_column.home.public_timeline": "en offentlig tidslinje",
   "empty_column.notifications": "Du har ingen varsler ennå. Kommuniser med andre for å begynne samtalen.",
   "empty_column.public": "Det er ingenting her! Skriv noe offentlig, eller følg brukere manuelt fra andre instanser for å fylle den opp",
   "follow_request.authorize": "Autorisér",
@@ -58,13 +79,14 @@ const no = {
   "navigation_bar.follow_requests": "Følgeforespørsler",
   "navigation_bar.info": "Utvidet informasjon",
   "navigation_bar.logout": "Logg ut",
+  "navigation_bar.mutes": "Muted users",
   "navigation_bar.preferences": "Preferanser",
   "navigation_bar.public_timeline": "Felles tidslinje",
   "notification.favourite": "{name} likte din status",
   "notification.follow": "{name} fulgte deg",
   "notification.reblog": "{name} fremhevde din status",
-  "notifications.clear_confirmation": "Er du sikker på at du vil fjerne alle dine varsler?",
   "notifications.clear": "Fjern varsler",
+  "notifications.clear_confirmation": "Er du sikker på at du vil fjerne alle dine varsler?",
   "notifications.column_settings.alert": "Skrivebordsvarslinger",
   "notifications.column_settings.favourite": "Likt:",
   "notifications.column_settings.follow": "Nye følgere:",
@@ -73,6 +95,26 @@ const no = {
   "notifications.column_settings.show": "Vis i kolonne",
   "notifications.column_settings.sound": "Spill lyd",
   "notifications.settings": "Kolonneinstillinger",
+  "onboarding.done": "Done",
+  "onboarding.next": "Next",
+  "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+  "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
+  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to Mastodon!",
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
+  "onboarding.page_six.almost_done": "Almost done...",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
+  "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "community guidelines",
+  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
+  "onboarding.page_six.various_app": "mobile apps",
+  "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+  "onboarding.skip": "Skip",
   "privacy.change": "Justér synlighet",
   "privacy.direct.long": "Post kun til nevnte brukere",
   "privacy.direct.short": "Direkte",
@@ -87,9 +129,9 @@ const no = {
   "report.placeholder": "Tilleggskommentarer",
   "report.submit": "Send inn",
   "report.target": "Rapporterer",
-  "search_results.total": "{count, number} {count, plural, one {resultat} other {resultater}}",
   "search.placeholder": "Søk",
-  "search.status_by": "Status fra {name}",
+  "search_results.total": "{count, number} {count, plural, one {resultat} other {resultater}}",
+  "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Slett",
   "status.favourite": "Lik",
   "status.load_more": "Last mer",
@@ -99,6 +141,7 @@ const no = {
   "status.reblog": "Fremhev",
   "status.reblogged_by": "Fremhevd av {name}",
   "status.reply": "Svar",
+  "status.replyAll": "Reply to thread",
   "status.report": "Rapporter @{name}",
   "status.sensitive_toggle": "Klikk for å vise",
   "status.sensitive_warning": "Følsomt innhold",
@@ -113,18 +156,8 @@ const no = {
   "upload_button.label": "Legg til media",
   "upload_form.undo": "Angre",
   "upload_progress.label": "Laster opp...",
+  "video_player.expand": "Utvid video",
   "video_player.toggle_sound": "Veksle lyd",
   "video_player.toggle_visible": "Veksle synlighet",
-  "video_player.expand": "Utvid video",
-  "getting_started.about_addressing": "Du kan følge noen hvis du vet brukernavnet deres og domenet de er på ved å skrive en e-postadresse inn i søkeskjemaet.",
-  "getting_started.about_shortcuts": "Hvis målbrukeren er på samme domene som deg, vil kun brukernavnet også fungere. Den samme regelen gjelder når man nevner noen i statuser.",
-  "tabs_bar.mentions": "Nevninger",
-  "tabs_bar.public": "Felles tidslinje",
-  "compose_form.private": "Merk som privat",
-  "compose_form.unlisted": "Ikke vis på offentlige tidslinjer",
-  "search.account": "Konto",
-  "search.hashtag": "Hashtag",
-  "notification.mention": "{name} nevnte deg"
-};
-
-export default no;
+  "video_player.video_error": "Video could not be played"
+}
diff --git a/app/assets/javascripts/components/locales/oc.jsx b/app/javascript/mastodon/locales/oc.json
index 14f64e762..5a23fe9f8 100644
--- a/app/assets/javascripts/components/locales/oc.jsx
+++ b/app/javascript/mastodon/locales/oc.json
@@ -1,128 +1,163 @@
-const oc = {
-  "column_back_button.label": "Tornar",
-  "lightbox.close": "Tampar",
-  "loading_indicator.label": "Cargament…",
-  "status.mention": "Mencionar",
-  "status.delete": "Escafar",
-  "status.reply": "Respondre",
-  "status.reblog": "Partejar",
-  "status.favourite": "Apondre als favorits",
-  "status.reblogged_by": "{name} a partejat :",
-  "status.sensitive_warning": "Contengut embarrassant",
-  "status.sensitive_toggle": "Clicar per mostrar",
-  "status.show_more": "Desplegar",
-  "status.show_less": "Tornar plegar",
-  "status.open": "Desplegar aqueste estatut",
-  "status.report": "Senhalar @{name}",
-  "status.load_more": "Cargar mai",
-  "status.media_hidden": "Mèdia rescondut",
-  "video_player.toggle_sound": "Activar/Desactivar lo son",
-  "video_player.toggle_visible": "Mostrar/Rescondre la vidèo",
-  "account.mention": "Mencionar",
-  "account.edit_profile": "Modificar lo perfil",
-  "account.unblock": "Desblocar",
-  "account.unfollow": "Quitar de sègre",
+{
   "account.block": "Blocar",
-  "account.mute": "Rescondre",
-  "account.unmute": "Quitar de rescondre",
+  "account.disclaimer": "Aqueste compte es sus una autra instància. Los nombres pòdon èsser mai grandes.",
+  "account.edit_profile": "Modificar lo perfil",
   "account.follow": "Sègre",
-  "account.posts": "Estatuts",
-  "account.follows": "Abonaments",
   "account.followers": "Abonats",
+  "account.follows": "Abonaments",
   "account.follows_you": "Vos sèc",
-  "account.requested": "Invitacion mandada",
+  "account.mention": "Mencionar",
+  "account.mute": "Rescondre",
+  "account.posts": "Estatuts",
   "account.report": "Senhalar",
-  "account.disclaimer": "Aqueste compte es sus una autra instància. Los nombres pòdon èsser mai grandes.",
-  "getting_started.heading": "Per començar",
-  "getting_started.about_addressing": "Podètz sègre los estatuts de qualqu’un en picant son identificant e lo domeni de l’instància separat amb un @ coma una adreça de corrièl dins lo camp de recèrca.",
-  "getting_started.about_shortcuts": "S’aquesta persona emplega la meteissa instància que vos l’identifican basta. Atal foncionan tanben las mencions dins vòstres estatuts.",
-  "getting_started.about_developer": "Per sègre lo desvolopaire d’aqueste projècte : Gargron@mastodon.social",
-  "getting_started.open_source_notice": "Mastodon es un logicial liure. Podètz contribuir e mandar vòstres comentaris e rapòrt de bug via{github} sus GitHub.",
-  "column.home": "Acuèlh",
-  "column.community": "Fil public local",
-  "column.public": "Fil public global",
-  "column.notifications": "Notificacions",
+  "account.requested": "Invitacion mandada",
+  "account.unblock": "Desblocar",
+  "account.unfollow": "Quitar de sègre",
+  "account.unmute": "Quitar de rescondre",
+  "boost_modal.combo": "You can press {combo} to skip this next time",
   "column.blocks": "Personas blocadas",
+  "column.community": "Fil public local",
   "column.favourites": "Favorits",
   "column.follow_requests": "Demandas d’abonament",
-  "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í ! Escribètz quicòm de public, o seguètz de personas d’autras instàncias per garnir lo fil public.",
-  "empty_column.home": "Pel moment segètz pas segun. Visitatz {public} o utilizatz la recèrca per vos connectar a d’autras personas.",
-  "empty_column.home.public_timeline": "lo fil public",
-  "empty_column.community": "Lo fil public local es void. Escribètz quicòm per lo garnir !",
-  "empty_column.hashtag": "I a pas encara de contengut ligat a aqueste hashtag",
-  "tabs_bar.compose": "Compausar",
-  "tabs_bar.home": "Acuèlh",
-  "tabs_bar.mentions": "Mencions",
-  "tabs_bar.public": "Fil public global",
-  "tabs_bar.notifications": "Notifications",
-  "tabs_bar.local_timeline": "Fil public local",
-  "tabs_bar.federated_timeline": "Fil public global",
+  "column.home": "Acuèlh",
+  "column.mutes": "Muted users",
+  "column.notifications": "Notificacions",
+  "column.public": "Fil public global",
+  "column_back_button.label": "Tornar",
+  "column_subheading.navigation": "Navigation",
+  "column_subheading.settings": "Settings",
+  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+  "compose_form.lock_disclaimer.lock": "locked",
   "compose_form.placeholder": "A de qué pensatz ?",
+  "compose_form.privacy_disclaimer": "Vòstre estatut privat serà enviat a las personas mencionadas sus {domains}. Vos fisatz d’aqueste{domainsCount, plural, one { servidor} other {s servidors}} per divulgar pas vòstre estatut ? Los estatuts privats foncionan pas que sus las instàncias a Mastodons. Se {domains} {domainsCount, plural, one {es pas una instància a Mastodon} other {son pas d'instàncias a Mastodon}}, i aurà pas d’indicacion disent que vòstre estatut es privat e poirà èsser partejat o èsser visible a de mond pas prevists",
   "compose_form.publish": "Tut",
   "compose_form.sensitive": "Marcar lo mèdia coma embarrassant",
   "compose_form.spoiler": "Rescondre lo tèxte darrièr un avertiment",
   "compose_form.spoiler_placeholder": "Avertiment",
-  "compose_form.private": "Far venir privat",
-  "compose_form.privacy_disclaimer": "Vòstre estatut privat serà enviat a las personas mencionadas sus {domains}. Vos fisatz d’aqueste{domainsCount, plural, one { servidor} other {s servidors}} per divulgar pas vòstre estatut ? Los estatuts privats foncionan pas que sus las instàncias a Mastodons. Se {domains} {domainsCount, plural, one {es pas una instància a Mastodon} other {son pas d'instàncias a Mastodon}}, i aurà pas d’indicacion disent que vòstre estatut es privat e poirà èsser partejat o èsser visible a de mond pas prevists",
-  "compose_form.unlisted": "Mostrar pas dins los fils publics",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.confirm": "Block",
+  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.message": "Are you sure you want to mute {name}?",
+  "emoji_button.activity": "Activity",
+  "emoji_button.flags": "Flags",
+  "emoji_button.food": "Food & Drink",
   "emoji_button.label": "Inserir un emoji",
-  "navigation_bar.edit_profile": "Modificar lo perfil",
-  "navigation_bar.preferences": "Preferéncias",
-  "navigation_bar.community_timeline": "Fil public local",
-  "navigation_bar.public_timeline": "Fil public global",
+  "emoji_button.nature": "Nature",
+  "emoji_button.objects": "Objects",
+  "emoji_button.people": "People",
+  "emoji_button.search": "Search...",
+  "emoji_button.symbols": "Symbols",
+  "emoji_button.travel": "Travel & Places",
+  "empty_column.community": "Lo fil public local es void. Escribètz quicòm per lo garnir !",
+  "empty_column.hashtag": "I a pas encara de contengut ligat a aqueste hashtag",
+  "empty_column.home": "Pel moment segètz pas segun. Visitatz {public} o utilizatz la recèrca per vos connectar a d’autras personas.",
+  "empty_column.home.public_timeline": "lo fil public",
+  "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í ! Escribètz quicòm de public, o seguètz de personas d’autras instàncias per garnir lo fil public.",
+  "follow_request.authorize": "Autorizar",
+  "follow_request.reject": "Regetar",
+  "getting_started.apps": "Various apps are available",
+  "getting_started.heading": "Per començar",
+  "getting_started.open_source_notice": "Mastodon es un logicial liure. Podètz contribuir e mandar vòstres comentaris e rapòrt de bug via{github} sus GitHub.",
+  "home.column_settings.advanced": "Avançat",
+  "home.column_settings.basic": "Basic",
+  "home.column_settings.filter_regex": "Filtrar amb una expression racionala",
+  "home.column_settings.show_reblogs": "Mostrar los partatges",
+  "home.column_settings.show_replies": "Mostrar las responsas",
+  "home.settings": "Paramètres de la colomna",
+  "lightbox.close": "Tampar",
+  "loading_indicator.label": "Cargament…",
+  "media_gallery.toggle_visible": "Modificar la visibilitat",
+  "missing_indicator.label": "Pas trobat",
   "navigation_bar.blocks": "Personas blocadas",
+  "navigation_bar.community_timeline": "Fil public local",
+  "navigation_bar.edit_profile": "Modificar lo perfil",
   "navigation_bar.favourites": "Favorits",
+  "navigation_bar.follow_requests": "Demandas d'abonament",
   "navigation_bar.info": "Mai informacions",
   "navigation_bar.logout": "Desconnexion",
-  "navigation_bar.follow_requests": "Demandas d'abonament",
-  "reply_indicator.cancel": "Anullar",
-  "search.placeholder": "Recercar",
-  "search.account": "Compte",
-  "search.hashtag": "Mot-clau",
-  "search_results.total": "{count, number} {count, plural, one {resultat} other {resultats}}",
-  "search.status_by": "Estatuts de {name}",
-  "upload_button.label": "Apondre un mèdia",
-  "upload_form.undo": "Anullar",
-  "upload_progress.label": "Mandadís…",
-  "upload_area.title": "Lisatz e depausatz per mandar",
-  "notification.follow": "{name} vos sèc.",
+  "navigation_bar.mutes": "Muted users",
+  "navigation_bar.preferences": "Preferéncias",
+  "navigation_bar.public_timeline": "Fil public global",
   "notification.favourite": "{name} a apondut a sos favorits :",
+  "notification.follow": "{name} vos sèc.",
   "notification.reblog": "{name} a partejat vòstre estatut :",
-  "notification.mention": "{name} vos a mencionat :",
+  "notifications.clear": "Levar",
+  "notifications.clear_confirmation": "Volètz vertadièrament levar totas vòstras las notificacions ?",
   "notifications.column_settings.alert": "Notificacions localas",
-  "notifications.column_settings.show": "Mostrar dins la colomna",
-  "notifications.column_settings.sound": "Emetre un son",
-  "notifications.column_settings.follow": "Nòus abonats :",
   "notifications.column_settings.favourite": "Favorits :",
+  "notifications.column_settings.follow": "Nòus abonats :",
   "notifications.column_settings.mention": "Mencions :",
   "notifications.column_settings.reblog": "Partatges :",
-  "notifications.clear": "Levar",
-  "notifications.clear_confirmation": "Volètz vertadièrament levar totas vòstras las notificacions ?",
+  "notifications.column_settings.show": "Mostrar dins la colomna",
+  "notifications.column_settings.sound": "Emetre un son",
   "notifications.settings": "Paramètres de la colomna",
-  "privacy.public.short": "Public",
+  "onboarding.done": "Done",
+  "onboarding.next": "Next",
+  "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+  "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
+  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to Mastodon!",
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
+  "onboarding.page_six.almost_done": "Almost done...",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
+  "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "community guidelines",
+  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
+  "onboarding.page_six.various_app": "mobile apps",
+  "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+  "onboarding.skip": "Skip",
+  "privacy.change": "Ajustar la confidencialitat del messatge",
+  "privacy.direct.long": "Mostrar pas qu'a las personas mencionadas",
+  "privacy.direct.short": "Dirècte",
+  "privacy.private.long": "Mostrar pas qu'a vòstres abonats",
+  "privacy.private.short": "Privat",
   "privacy.public.long": "Mostrar dins los fils publics",
-  "privacy.unlisted.short": "Pas-listat",
+  "privacy.public.short": "Public",
   "privacy.unlisted.long": "Mostrar pas dins los fils publics",
-  "privacy.private.short": "Privat",
-  "privacy.private.long": "Mostrar pas qu'a vòstres abonats",
-  "privacy.direct.short": "Dirècte",
-  "privacy.direct.long": "Mostrar pas qu'a las personas mencionadas",
-  "privacy.change": "Ajustar la confidencialitat del messatge",
-  "media_gallery.toggle_visible": "Modificar la visibilitat",
-  "missing_indicator.label": "Pas trobat",
-  "follow_request.authorize": "Autorizar",
-  "follow_request.reject": "Regetar",
-  "home.settings": "Paramètres de la colomna",
-  "home.column_settings.basic": "Basic",
-  "home.column_settings.show_reblogs": "Mostrar los partatges",
-  "home.column_settings.show_replies": "Mostrar las responsas",
-  "home.column_settings.advanced": "Avançat",
-  "home.column_settings.filter_regex": "Filtrar amb una expression racionala",
+  "privacy.unlisted.short": "Pas-listat",
+  "reply_indicator.cancel": "Anullar",
   "report.heading": "Nòu senhalament",
   "report.placeholder": "Comentaris addicionals",
   "report.submit": "Mandat",
-  "report.target": "Senhalament"
-};
-
-export default oc;
+  "report.target": "Senhalament",
+  "search.placeholder": "Recercar",
+  "search_results.total": "{count, number} {count, plural, one {resultat} other {resultats}}",
+  "status.cannot_reblog": "This post cannot be boosted",
+  "status.delete": "Escafar",
+  "status.favourite": "Apondre als favorits",
+  "status.load_more": "Cargar mai",
+  "status.media_hidden": "Mèdia rescondut",
+  "status.mention": "Mencionar",
+  "status.open": "Desplegar aqueste estatut",
+  "status.reblog": "Partejar",
+  "status.reblogged_by": "{name} a partejat :",
+  "status.reply": "Respondre",
+  "status.replyAll": "Reply to thread",
+  "status.report": "Senhalar @{name}",
+  "status.sensitive_toggle": "Clicar per mostrar",
+  "status.sensitive_warning": "Contengut embarrassant",
+  "status.show_less": "Tornar plegar",
+  "status.show_more": "Desplegar",
+  "tabs_bar.compose": "Compausar",
+  "tabs_bar.federated_timeline": "Fil public global",
+  "tabs_bar.home": "Acuèlh",
+  "tabs_bar.local_timeline": "Fil public local",
+  "tabs_bar.notifications": "Notifications",
+  "upload_area.title": "Lisatz e depausatz per mandar",
+  "upload_button.label": "Apondre un mèdia",
+  "upload_form.undo": "Anullar",
+  "upload_progress.label": "Mandadís…",
+  "video_player.expand": "Expand video",
+  "video_player.toggle_sound": "Activar/Desactivar lo son",
+  "video_player.toggle_visible": "Mostrar/Rescondre la vidèo",
+  "video_player.video_error": "Video could not be played"
+}
\ No newline at end of file
diff --git a/app/assets/javascripts/components/locales/pt-br.jsx b/app/javascript/mastodon/locales/pt-BR.json
index 91c74bb19..12e9f6b5f 100644
--- a/app/assets/javascripts/components/locales/pt-br.jsx
+++ b/app/javascript/mastodon/locales/pt-BR.json
@@ -1,11 +1,11 @@
-const pt_br = {
+{
   "account.block": "Bloquear @{name}",
   "account.disclaimer": "Essa conta está localizado em outra instância. Os nomes podem ser maiores.",
   "account.edit_profile": "Editar perfil",
   "account.follow": "Seguir",
   "account.followers": "Seguidores",
-  "account.follows_you": "É teu seguidor",
   "account.follows": "Segue",
+  "account.follows_you": "É teu seguidor",
   "account.mention": "Mencionar @{name}",
   "account.mute": "Silenciar @{name}",
   "account.posts": "Posts",
@@ -15,7 +15,6 @@ const pt_br = {
   "account.unfollow": "Deixar de seguir",
   "account.unmute": "Não silenciar @{name}",
   "boost_modal.combo": "Pode clicar {combo} para não voltar a ver",
-  "column_back_button.label": "Voltar",
   "column.blocks": "Utilizadores Bloqueados",
   "column.community": "Local",
   "column.favourites": "Favoritos",
@@ -24,17 +23,38 @@ const pt_br = {
   "column.mutes": "Utilizadores silenciados",
   "column.notifications": "Notificações",
   "column.public": "Global",
+  "column_back_button.label": "Voltar",
+  "column_subheading.navigation": "Navigation",
+  "column_subheading.settings": "Settings",
+  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+  "compose_form.lock_disclaimer.lock": "locked",
   "compose_form.placeholder": "Em que estás a pensar?",
   "compose_form.privacy_disclaimer": "O teu conteúdo privado vai ser partilhado com os utilizadores do {domains}. Confias {domainsCount, plural, one {neste servidor} other {nestes servidores}}? A privacidade só funciona em instâncias do Mastodon. Se {domains} {domainsCount, plural, one {não é uma instância} other {não são instâncias}}, não existem indicadores da privacidade da tua partilha, e podem ser partilhados com outros.",
   "compose_form.publish": "Publicar",
   "compose_form.sensitive": "Marcar media como conteúdo sensível",
-  "compose_form.spoiler_placeholder": "Aviso de conteúdo",
   "compose_form.spoiler": "Esconder texto com aviso",
+  "compose_form.spoiler_placeholder": "Aviso de conteúdo",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.confirm": "Block",
+  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.message": "Are you sure you want to mute {name}?",
+  "emoji_button.activity": "Activity",
+  "emoji_button.flags": "Flags",
+  "emoji_button.food": "Food & Drink",
   "emoji_button.label": "Inserir Emoji",
+  "emoji_button.nature": "Nature",
+  "emoji_button.objects": "Objects",
+  "emoji_button.people": "People",
+  "emoji_button.search": "Search...",
+  "emoji_button.symbols": "Symbols",
+  "emoji_button.travel": "Travel & Places",
   "empty_column.community": "Ainda não existem conteúdo local para mostrar!",
   "empty_column.hashtag": "Ainda não existe qualquer conteúdo com essa hashtag",
-  "empty_column.home.public_timeline": "global",
   "empty_column.home": "Ainda não segues qualquer utilizador. Visita {public} ou utiliza a pesquisa para procurar outros utilizadores.",
+  "empty_column.home.public_timeline": "global",
   "empty_column.notifications": "Não tens notificações. Interage com outros utilizadores para iniciar uma conversa.",
   "empty_column.public": "Não há nada aqui! Escreve algo publicamente ou segue outros utilizadores para ver aqui os conteúdos públicos.",
   "follow_request.authorize": "Autorizar",
@@ -64,10 +84,9 @@ const pt_br = {
   "navigation_bar.public_timeline": "Global",
   "notification.favourite": "{name} adicionou o teu post aos favoritos",
   "notification.follow": "{name} seguiu-te",
-  "notification.mention": "{name} mencionou-te",
   "notification.reblog": "{name} partilhou o teu post",
-  "notifications.clear_confirmation": "Queres mesmo limpar todas as notificações?",
   "notifications.clear": "Limpar notificações",
+  "notifications.clear_confirmation": "Queres mesmo limpar todas as notificações?",
   "notifications.column_settings.alert": "Notificações no computador",
   "notifications.column_settings.favourite": "Favoritos:",
   "notifications.column_settings.follow": "Novos seguidores:",
@@ -76,6 +95,26 @@ const pt_br = {
   "notifications.column_settings.show": "Mostrar nas colunas",
   "notifications.column_settings.sound": "Reproduzir som",
   "notifications.settings": "Parâmetros da listagem de Notificações",
+  "onboarding.done": "Done",
+  "onboarding.next": "Next",
+  "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+  "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
+  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to Mastodon!",
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
+  "onboarding.page_six.almost_done": "Almost done...",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
+  "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "community guidelines",
+  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
+  "onboarding.page_six.various_app": "mobile apps",
+  "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+  "onboarding.skip": "Skip",
   "privacy.change": "Ajustar a privacidade da mensagem",
   "privacy.direct.long": "Apenas para utilizadores mencionados",
   "privacy.direct.short": "Directo",
@@ -90,9 +129,9 @@ const pt_br = {
   "report.placeholder": "Comentários adicionais",
   "report.submit": "Enviar",
   "report.target": "Denunciar",
-  "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}",
   "search.placeholder": "Pesquisar",
-  "search.status_by": "Post de {name}",
+  "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}",
+  "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Eliminar",
   "status.favourite": "Adicionar aos favoritos",
   "status.load_more": "Carregar mais",
@@ -102,6 +141,7 @@ const pt_br = {
   "status.reblog": "Partilhar",
   "status.reblogged_by": "{name} partilhou",
   "status.reply": "Responder",
+  "status.replyAll": "Reply to thread",
   "status.report": "Denúnciar @{name}",
   "status.sensitive_toggle": "Clique para ver",
   "status.sensitive_warning": "Conteúdo sensível",
@@ -116,10 +156,8 @@ const pt_br = {
   "upload_button.label": "Adicionar media",
   "upload_form.undo": "Anular",
   "upload_progress.label": "A gravar...",
+  "video_player.expand": "Expandir vídeo",
   "video_player.toggle_sound": "Ligar/Desligar som",
   "video_player.toggle_visible": "Ligar/Desligar vídeo",
-  "video_player.expand": "Expandir vídeo",
-  "video_player.video_error": "Não é possível ver o vídeo",
-};
-
-export default pt_br;
+  "video_player.video_error": "Não é possível ver o vídeo"
+}
\ No newline at end of file
diff --git a/app/assets/javascripts/components/locales/pt.jsx b/app/javascript/mastodon/locales/pt.json
index 03095d20d..12e9f6b5f 100644
--- a/app/assets/javascripts/components/locales/pt.jsx
+++ b/app/javascript/mastodon/locales/pt.json
@@ -1,11 +1,11 @@
-const pt = {
+{
   "account.block": "Bloquear @{name}",
   "account.disclaimer": "Essa conta está localizado em outra instância. Os nomes podem ser maiores.",
   "account.edit_profile": "Editar perfil",
   "account.follow": "Seguir",
   "account.followers": "Seguidores",
-  "account.follows_you": "É teu seguidor",
   "account.follows": "Segue",
+  "account.follows_you": "É teu seguidor",
   "account.mention": "Mencionar @{name}",
   "account.mute": "Silenciar @{name}",
   "account.posts": "Posts",
@@ -15,7 +15,6 @@ const pt = {
   "account.unfollow": "Deixar de seguir",
   "account.unmute": "Não silenciar @{name}",
   "boost_modal.combo": "Pode clicar {combo} para não voltar a ver",
-  "column_back_button.label": "Voltar",
   "column.blocks": "Utilizadores Bloqueados",
   "column.community": "Local",
   "column.favourites": "Favoritos",
@@ -24,17 +23,38 @@ const pt = {
   "column.mutes": "Utilizadores silenciados",
   "column.notifications": "Notificações",
   "column.public": "Global",
+  "column_back_button.label": "Voltar",
+  "column_subheading.navigation": "Navigation",
+  "column_subheading.settings": "Settings",
+  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+  "compose_form.lock_disclaimer.lock": "locked",
   "compose_form.placeholder": "Em que estás a pensar?",
   "compose_form.privacy_disclaimer": "O teu conteúdo privado vai ser partilhado com os utilizadores do {domains}. Confias {domainsCount, plural, one {neste servidor} other {nestes servidores}}? A privacidade só funciona em instâncias do Mastodon. Se {domains} {domainsCount, plural, one {não é uma instância} other {não são instâncias}}, não existem indicadores da privacidade da tua partilha, e podem ser partilhados com outros.",
   "compose_form.publish": "Publicar",
   "compose_form.sensitive": "Marcar media como conteúdo sensível",
-  "compose_form.spoiler_placeholder": "Aviso de conteúdo",
   "compose_form.spoiler": "Esconder texto com aviso",
+  "compose_form.spoiler_placeholder": "Aviso de conteúdo",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.confirm": "Block",
+  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.message": "Are you sure you want to mute {name}?",
+  "emoji_button.activity": "Activity",
+  "emoji_button.flags": "Flags",
+  "emoji_button.food": "Food & Drink",
   "emoji_button.label": "Inserir Emoji",
+  "emoji_button.nature": "Nature",
+  "emoji_button.objects": "Objects",
+  "emoji_button.people": "People",
+  "emoji_button.search": "Search...",
+  "emoji_button.symbols": "Symbols",
+  "emoji_button.travel": "Travel & Places",
   "empty_column.community": "Ainda não existem conteúdo local para mostrar!",
   "empty_column.hashtag": "Ainda não existe qualquer conteúdo com essa hashtag",
-  "empty_column.home.public_timeline": "global",
   "empty_column.home": "Ainda não segues qualquer utilizador. Visita {public} ou utiliza a pesquisa para procurar outros utilizadores.",
+  "empty_column.home.public_timeline": "global",
   "empty_column.notifications": "Não tens notificações. Interage com outros utilizadores para iniciar uma conversa.",
   "empty_column.public": "Não há nada aqui! Escreve algo publicamente ou segue outros utilizadores para ver aqui os conteúdos públicos.",
   "follow_request.authorize": "Autorizar",
@@ -64,10 +84,9 @@ const pt = {
   "navigation_bar.public_timeline": "Global",
   "notification.favourite": "{name} adicionou o teu post aos favoritos",
   "notification.follow": "{name} seguiu-te",
-  "notification.mention": "{name} mencionou-te",
   "notification.reblog": "{name} partilhou o teu post",
-  "notifications.clear_confirmation": "Queres mesmo limpar todas as notificações?",
   "notifications.clear": "Limpar notificações",
+  "notifications.clear_confirmation": "Queres mesmo limpar todas as notificações?",
   "notifications.column_settings.alert": "Notificações no computador",
   "notifications.column_settings.favourite": "Favoritos:",
   "notifications.column_settings.follow": "Novos seguidores:",
@@ -76,6 +95,26 @@ const pt = {
   "notifications.column_settings.show": "Mostrar nas colunas",
   "notifications.column_settings.sound": "Reproduzir som",
   "notifications.settings": "Parâmetros da listagem de Notificações",
+  "onboarding.done": "Done",
+  "onboarding.next": "Next",
+  "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+  "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
+  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to Mastodon!",
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
+  "onboarding.page_six.almost_done": "Almost done...",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
+  "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "community guidelines",
+  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
+  "onboarding.page_six.various_app": "mobile apps",
+  "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+  "onboarding.skip": "Skip",
   "privacy.change": "Ajustar a privacidade da mensagem",
   "privacy.direct.long": "Apenas para utilizadores mencionados",
   "privacy.direct.short": "Directo",
@@ -90,9 +129,9 @@ const pt = {
   "report.placeholder": "Comentários adicionais",
   "report.submit": "Enviar",
   "report.target": "Denunciar",
-  "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}",
   "search.placeholder": "Pesquisar",
-  "search.status_by": "Post de {name}",
+  "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}",
+  "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "Eliminar",
   "status.favourite": "Adicionar aos favoritos",
   "status.load_more": "Carregar mais",
@@ -102,6 +141,7 @@ const pt = {
   "status.reblog": "Partilhar",
   "status.reblogged_by": "{name} partilhou",
   "status.reply": "Responder",
+  "status.replyAll": "Reply to thread",
   "status.report": "Denúnciar @{name}",
   "status.sensitive_toggle": "Clique para ver",
   "status.sensitive_warning": "Conteúdo sensível",
@@ -116,10 +156,8 @@ const pt = {
   "upload_button.label": "Adicionar media",
   "upload_form.undo": "Anular",
   "upload_progress.label": "A gravar...",
+  "video_player.expand": "Expandir vídeo",
   "video_player.toggle_sound": "Ligar/Desligar som",
   "video_player.toggle_visible": "Ligar/Desligar vídeo",
-  "video_player.expand": "Expandir vídeo",
-  "video_player.video_error": "Não é possível ver o vídeo",
-};
-
-export default pt;
+  "video_player.video_error": "Não é possível ver o vídeo"
+}
\ No newline at end of file
diff --git a/app/assets/javascripts/components/locales/ru.jsx b/app/javascript/mastodon/locales/ru.json
index a1c9044bf..c13c95960 100644
--- a/app/assets/javascripts/components/locales/ru.jsx
+++ b/app/javascript/mastodon/locales/ru.json
@@ -1,4 +1,4 @@
-const ru = {
+{
   "account.block": "Блокировать",
   "account.disclaimer": "Это пользователь с другого узла. Число может быть больше.",
   "account.edit_profile": "Изменить профиль",
@@ -26,12 +26,21 @@ const ru = {
   "column_back_button.label": "Назад",
   "column_subheading.navigation": "Навигация",
   "column_subheading.settings": "Настройки",
+  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+  "compose_form.lock_disclaimer.lock": "locked",
   "compose_form.placeholder": "О чем Вы думаете?",
   "compose_form.privacy_disclaimer": "Ваш приватный статус будет доставлен упомянутым пользователям на доменах {domains}. Доверяете ли вы {domainsCount, plural, one {этому серверу} other {этим серверам}}? Приватность постов работает только на узлах Mastodon. Если {domains} {domainsCount, plural, one {не является узлом Mastodon} other {не являются узлами Mastodon}}, приватность поста не будет указана, и он может оказаться продвинут или иным образом показан не обозначенным Вами пользователям.",
   "compose_form.publish": "Трубить",
   "compose_form.sensitive": "Отметить как чувствительный контент",
   "compose_form.spoiler": "Скрыть текст за предупреждением",
   "compose_form.spoiler_placeholder": "Предупреждение о скрытом тексте",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.confirm": "Block",
+  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.message": "Are you sure you want to mute {name}?",
   "emoji_button.activity": "Занятия",
   "emoji_button.flags": "Флаги",
   "emoji_button.food": "Еда и напитки",
@@ -75,7 +84,6 @@ const ru = {
   "navigation_bar.public_timeline": "Глобальная лента",
   "notification.favourite": "{name} понравился Ваш статус",
   "notification.follow": "{name} подписался(-лась) на Вас",
-  "notification.mention": "{name} упомянул(а) Вас",
   "notification.reblog": "{name} продвинул(а) Ваш статус",
   "notifications.clear": "Очистить уведомления",
   "notifications.clear_confirmation": "Вы уверены, что хотите очистить все уведомления?",
@@ -87,6 +95,26 @@ const ru = {
   "notifications.column_settings.show": "Показывать в колонке",
   "notifications.column_settings.sound": "Проигрывать звук",
   "notifications.settings": "Настройки колонки",
+  "onboarding.done": "Done",
+  "onboarding.next": "Next",
+  "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+  "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
+  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to Mastodon!",
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
+  "onboarding.page_six.almost_done": "Almost done...",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
+  "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "community guidelines",
+  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
+  "onboarding.page_six.various_app": "mobile apps",
+  "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+  "onboarding.skip": "Skip",
   "privacy.change": "Изменить видимость статуса",
   "privacy.direct.long": "Показать только упомянутым",
   "privacy.direct.short": "Направленный",
@@ -102,7 +130,6 @@ const ru = {
   "report.submit": "Отправить",
   "report.target": "Жалуемся на",
   "search.placeholder": "Поиск",
-  "search.status_by": "Статус от {name}",
   "search_results.total": "{count, number} {count, plural, one {результат} few {результата} many {результатов} other {результатов}}",
   "status.cannot_reblog": "Этот статус не может быть продвинут",
   "status.delete": "Удалить",
@@ -132,7 +159,5 @@ const ru = {
   "video_player.expand": "Развернуть видео",
   "video_player.toggle_sound": "Вкл./выкл. звук",
   "video_player.toggle_visible": "Показать/скрыть",
-  "video_player.video_error": "Видео не может быть проиграно",
-};
-
-export default ru;
+  "video_player.video_error": "Видео не может быть проиграно"
+}
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json
new file mode 100644
index 000000000..fde28871d
--- /dev/null
+++ b/app/javascript/mastodon/locales/uk.json
@@ -0,0 +1,163 @@
+{
+  "account.block": "Заблокувати",
+  "account.disclaimer": "This user is from another instance. This number may be larger.",
+  "account.edit_profile": "Налаштування профілю",
+  "account.follow": "Підписатися",
+  "account.followers": "Підписники",
+  "account.follows": "Підписки",
+  "account.follows_you": "Підписаний",
+  "account.mention": "Згадати",
+  "account.mute": "Mute @{name}",
+  "account.posts": "Пости",
+  "account.report": "Report @{name}",
+  "account.requested": "Awaiting approval",
+  "account.unblock": "Розблокувати",
+  "account.unfollow": "Відписатися",
+  "account.unmute": "Unmute @{name}",
+  "boost_modal.combo": "You can press {combo} to skip this next time",
+  "column.blocks": "Blocked users",
+  "column.community": "Local timeline",
+  "column.favourites": "Favourites",
+  "column.follow_requests": "Follow requests",
+  "column.home": "Головна",
+  "column.mutes": "Muted users",
+  "column.notifications": "Сповіщення",
+  "column.public": "Стіна",
+  "column_back_button.label": "Назад",
+  "column_subheading.navigation": "Navigation",
+  "column_subheading.settings": "Settings",
+  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+  "compose_form.lock_disclaimer.lock": "locked",
+  "compose_form.placeholder": "Що у Вас на думці?",
+  "compose_form.privacy_disclaimer": "Your private status will be delivered to mentioned users on {domains}. Do you trust {domainsCount, plural, one {that server} other {those servers}}? Post privacy only works on Mastodon instances. If {domains} {domainsCount, plural, one {is not a Mastodon instance} other {are not Mastodon instances}}, there will be no indication that your post is private, and it may be boosted or otherwise made visible to unintended recipients.",
+  "compose_form.publish": "Дмухнути",
+  "compose_form.sensitive": "Непристойний зміст",
+  "compose_form.spoiler": "Hide text behind warning",
+  "compose_form.spoiler_placeholder": "Content warning",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.confirm": "Block",
+  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.message": "Are you sure you want to mute {name}?",
+  "emoji_button.activity": "Activity",
+  "emoji_button.flags": "Flags",
+  "emoji_button.food": "Food & Drink",
+  "emoji_button.label": "Insert emoji",
+  "emoji_button.nature": "Nature",
+  "emoji_button.objects": "Objects",
+  "emoji_button.people": "People",
+  "emoji_button.search": "Search...",
+  "emoji_button.symbols": "Symbols",
+  "emoji_button.travel": "Travel & Places",
+  "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
+  "empty_column.hashtag": "There is nothing in this hashtag yet.",
+  "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.",
+  "empty_column.home.public_timeline": "the public timeline",
+  "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
+  "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up",
+  "follow_request.authorize": "Authorize",
+  "follow_request.reject": "Reject",
+  "getting_started.apps": "Various apps are available",
+  "getting_started.heading": "Ласкаво просимо",
+  "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}. {apps}.",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.basic": "Basic",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_reblogs": "Show boosts",
+  "home.column_settings.show_replies": "Show replies",
+  "home.settings": "Column settings",
+  "lightbox.close": "Закрити",
+  "loading_indicator.label": "Завантаження...",
+  "media_gallery.toggle_visible": "Toggle visibility",
+  "missing_indicator.label": "Not found",
+  "navigation_bar.blocks": "Blocked users",
+  "navigation_bar.community_timeline": "Local timeline",
+  "navigation_bar.edit_profile": "Редагувати профіль",
+  "navigation_bar.favourites": "Favourites",
+  "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.info": "Extended information",
+  "navigation_bar.logout": "Вийти",
+  "navigation_bar.mutes": "Muted users",
+  "navigation_bar.preferences": "Налаштування",
+  "navigation_bar.public_timeline": "Публічна стіна",
+  "notification.favourite": "{name} сподобався ваш допис",
+  "notification.follow": "{name} підписався(-лась) на Вас",
+  "notification.reblog": "{name} передмухнув(-ла) Ваш статус",
+  "notifications.clear": "Clear notifications",
+  "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
+  "notifications.column_settings.alert": "Desktop notifications",
+  "notifications.column_settings.favourite": "Favourites:",
+  "notifications.column_settings.follow": "New followers:",
+  "notifications.column_settings.mention": "Mentions:",
+  "notifications.column_settings.reblog": "Boosts:",
+  "notifications.column_settings.show": "Show in column",
+  "notifications.column_settings.sound": "Play sound",
+  "notifications.settings": "Column settings",
+  "onboarding.done": "Done",
+  "onboarding.next": "Next",
+  "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+  "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
+  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to Mastodon!",
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
+  "onboarding.page_six.almost_done": "Almost done...",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
+  "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "community guidelines",
+  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
+  "onboarding.page_six.various_app": "mobile apps",
+  "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+  "onboarding.skip": "Skip",
+  "privacy.change": "Adjust status privacy",
+  "privacy.direct.long": "Post to mentioned users only",
+  "privacy.direct.short": "Direct",
+  "privacy.private.long": "Post to followers only",
+  "privacy.private.short": "Followers-only",
+  "privacy.public.long": "Post to public timelines",
+  "privacy.public.short": "Public",
+  "privacy.unlisted.long": "Do not show in public timelines",
+  "privacy.unlisted.short": "Unlisted",
+  "reply_indicator.cancel": "Відмінити",
+  "report.heading": "New report",
+  "report.placeholder": "Additional comments",
+  "report.submit": "Submit",
+  "report.target": "Reporting",
+  "search.placeholder": "Пошук",
+  "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
+  "status.cannot_reblog": "This post cannot be boosted",
+  "status.delete": "Видалити",
+  "status.favourite": "Подобається",
+  "status.load_more": "Load more",
+  "status.media_hidden": "Media hidden",
+  "status.mention": "Згадати",
+  "status.open": "Expand this status",
+  "status.reblog": "Передмухнути",
+  "status.reblogged_by": "{name} передмухнув(-ла)",
+  "status.reply": "Відповісти",
+  "status.replyAll": "Reply to thread",
+  "status.report": "Report @{name}",
+  "status.sensitive_toggle": "Натисніть, щоб подивитися",
+  "status.sensitive_warning": "Непристойний зміст",
+  "status.show_less": "Show less",
+  "status.show_more": "Show more",
+  "tabs_bar.compose": "Написати",
+  "tabs_bar.federated_timeline": "Federated",
+  "tabs_bar.home": "Головна",
+  "tabs_bar.local_timeline": "Local",
+  "tabs_bar.notifications": "Сповіщення",
+  "upload_area.title": "Drag & drop to upload",
+  "upload_button.label": "Додати медіа",
+  "upload_form.undo": "Відмінити",
+  "upload_progress.label": "Uploading...",
+  "video_player.expand": "Expand video",
+  "video_player.toggle_sound": "Увімкнути/вимкнути звук",
+  "video_player.toggle_visible": "Toggle visibility",
+  "video_player.video_error": "Video could not be played"
+}
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_ar.json b/app/javascript/mastodon/locales/whitelist_ar.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_ar.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_bg.json b/app/javascript/mastodon/locales/whitelist_bg.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_bg.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_de.json b/app/javascript/mastodon/locales/whitelist_de.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_de.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_en.json b/app/javascript/mastodon/locales/whitelist_en.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_en.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_eo.json b/app/javascript/mastodon/locales/whitelist_eo.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_eo.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_es.json b/app/javascript/mastodon/locales/whitelist_es.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_es.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_fa.json b/app/javascript/mastodon/locales/whitelist_fa.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_fa.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_fi.json b/app/javascript/mastodon/locales/whitelist_fi.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_fi.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_fr.json b/app/javascript/mastodon/locales/whitelist_fr.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_fr.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_hr.json b/app/javascript/mastodon/locales/whitelist_hr.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_hr.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_hu.json b/app/javascript/mastodon/locales/whitelist_hu.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_hu.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_id.json b/app/javascript/mastodon/locales/whitelist_id.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_id.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_io.json b/app/javascript/mastodon/locales/whitelist_io.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_io.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_it.json b/app/javascript/mastodon/locales/whitelist_it.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_it.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_ja.json b/app/javascript/mastodon/locales/whitelist_ja.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_ja.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_nl.json b/app/javascript/mastodon/locales/whitelist_nl.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_nl.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_no.json b/app/javascript/mastodon/locales/whitelist_no.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_no.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_oc.json b/app/javascript/mastodon/locales/whitelist_oc.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_oc.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_pt-BR.json b/app/javascript/mastodon/locales/whitelist_pt-BR.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_pt-BR.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_pt.json b/app/javascript/mastodon/locales/whitelist_pt.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_pt.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_ru.json b/app/javascript/mastodon/locales/whitelist_ru.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_ru.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_uk.json b/app/javascript/mastodon/locales/whitelist_uk.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_uk.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_zh-CN.json b/app/javascript/mastodon/locales/whitelist_zh-CN.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_zh-CN.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/javascript/mastodon/locales/whitelist_zh-HK.json b/app/javascript/mastodon/locales/whitelist_zh-HK.json
new file mode 100644
index 000000000..32960f8ce
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_zh-HK.json
@@ -0,0 +1,2 @@
+[
+]
\ No newline at end of file
diff --git a/app/assets/javascripts/components/locales/zh-cn.jsx b/app/javascript/mastodon/locales/zh-CN.json
index ee4eee427..1e0d1fa58 100644
--- a/app/assets/javascripts/components/locales/zh-cn.jsx
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -1,30 +1,11 @@
-import zh from 'react-intl/locale-data/zh';
-
-const localeData = zh.reduce(function (acc, localeData) {
-  if (localeData.locale === "zh-Hans-CN") {
-    // rename the locale "zh-Hans-CN" as "zh-CN"
-    // (match the code usually used in Accepted-Language header)
-    acc.push(Object.assign({},
-      localeData,
-      {
-        "locale": "zh-CN",
-        "parentLocale": "zh-Hans-CN",
-      }
-    ));
-  }
-  return acc;
-}, []);
-
-export { localeData as localeData };
-
-const zh_cn = {
+{
   "account.block": "屏蔽 @{name}",
   "account.disclaimer": "由于这个账户处于另一个服务站,实际数字会比这个更多。",
   "account.edit_profile": "修改个人资料",
   "account.follow": "关注",
   "account.followers": "关注者",
-  "account.follows_you": "关注你",
   "account.follows": "正关注",
+  "account.follows_you": "关注你",
   "account.mention": "提及 @{name}",
   "account.mute": "将 @{name} 静音",
   "account.posts": "嘟文",
@@ -34,39 +15,50 @@ const zh_cn = {
   "account.unfollow": "取消关注",
   "account.unmute": "取消 @{name} 的静音",
   "boost_modal.combo": "如你想在下次路过时显示,请按{combo},",
-  "column_back_button.label": "返回",
   "column.blocks": "屏蔽用户",
   "column.community": "本站时间轴",
-  // intentional departure from existing "推文" translation for posts:
-  // "推文" refers to "推特", the official translation for Twitter.
-  // Currently using a semi-phonetic translation "嘟", which refers
-  // to train horn sounds, for "toot".
   "column.favourites": "赞过的嘟文",
   "column.follow_requests": "关注请求",
   "column.home": "主页",
+  "column.mutes": "Muted users",
   "column.notifications": "通知",
   "column.public": "跨站公共时间轴",
+  "column_back_button.label": "返回",
+  "column_subheading.navigation": "Navigation",
+  "column_subheading.settings": "Settings",
+  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+  "compose_form.lock_disclaimer.lock": "locked",
   "compose_form.placeholder": "在想啥?",
   "compose_form.privacy_disclaimer": "你的私人嘟文,将被发送至你所提及的 {domains} 用户。你是否信任{domainsCount, plural, one {这个网站} other {这些网站}}?请留意,嘟文隐私设置只适用于各 Mastodon 服务站,如果 {domains} {domainsCount, plural, one {不是 Mastodon 服务站} other {之中有些不是 Mastodon 服务站}},对方将无法收到这篇嘟文的隐私设置,然后可能被转嘟给不能预知的用户阅读。",
-  "compose_form.private": "标示为“只有关注你的人能看”",
-  // Going "toot-toot" here below.
   "compose_form.publish": "嘟嘟",
   "compose_form.sensitive": "将媒体文件标示为“敏感内容”",
-  "compose_form.spoiler_placeholder": "敏感内容的警告消息",
   "compose_form.spoiler": "将部分文本藏于警告消息之后",
-  "compose_form.unlisted": "请勿在公共时间轴显示",
+  "compose_form.spoiler_placeholder": "敏感内容的警告消息",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.confirm": "Block",
+  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.message": "Are you sure you want to mute {name}?",
+  "emoji_button.activity": "Activity",
+  "emoji_button.flags": "Flags",
+  "emoji_button.food": "Food & Drink",
   "emoji_button.label": "加入表情符号",
+  "emoji_button.nature": "Nature",
+  "emoji_button.objects": "Objects",
+  "emoji_button.people": "People",
+  "emoji_button.search": "Search...",
+  "emoji_button.symbols": "Symbols",
+  "emoji_button.travel": "Travel & Places",
   "empty_column.community": "本站时间轴暂时未有内容,快贴文来抢头香啊!",
   "empty_column.hashtag": "这个标签暂时未有内容。",
   "empty_column.home": "你还没有关注任何用户。快看看{public},向其他用户搭讪吧。",
   "empty_column.home.public_timeline": "公共时间轴",
-  "empty_column.home": "你还没有关注任何用户。快看看{public},向其他用户搭讪吧。",
   "empty_column.notifications": "你没有任何通知纪录,快向其他用户搭讪吧。",
   "empty_column.public": "跨站公共时间轴暂时没有内容!快写一些公共的嘟文,或者关注另一些服务站的用户吧!你和本站、友站的交流,将决定这里出现的内容。",
   "follow_request.authorize": "批准",
   "follow_request.reject": "拒绝",
-  "getting_started.about_addressing": "只要你知道一位用户的用户名称和域名,你可以用“@用户名称@域名”的格式在搜索栏寻找该用户。",
-  "getting_started.about_shortcuts": "只要该用户是在你现在的服务站开立,你就可以直接输入用户名搜索。在嘟文中提及别的用户也是如此。",
   "getting_started.apps": "手机或桌面应用程序",
   "getting_started.heading": "开始使用",
   "getting_started.open_source_notice": "Mastodon 是一个开放源码的软件。你可以在官方 GitHub ({github}) 贡献或者回报问题。你亦可通过{apps}阅读 Mastodon 上的消息。",
@@ -87,16 +79,14 @@ const zh_cn = {
   "navigation_bar.follow_requests": "关注请求",
   "navigation_bar.info": "关于本服务站",
   "navigation_bar.logout": "注销",
-  // intentional departure from https://github.com/tootsuite/mastodon/blob/f864fee1/config/locales/zh-CN.yml#L126:
-  // clashes for settings/preferences
+  "navigation_bar.mutes": "Muted users",
   "navigation_bar.preferences": "首选项",
   "navigation_bar.public_timeline": "跨站公共时间轴",
   "notification.favourite": "{name} 赞你的嘟文",
   "notification.follow": "{name} 开始关注你",
-  "notification.mention": "{name} 提及你",
   "notification.reblog": "{name} 转嘟你的嘟文",
-  "notifications.clear_confirmation": "你确定要清空通知纪录吗?",
   "notifications.clear": "清空通知纪录",
+  "notifications.clear_confirmation": "你确定要清空通知纪录吗?",
   "notifications.column_settings.alert": "显示桌面通知",
   "notifications.column_settings.favourite": "赞你的嘟文:",
   "notifications.column_settings.follow": "关注你:",
@@ -105,6 +95,26 @@ const zh_cn = {
   "notifications.column_settings.show": "在通知栏显示",
   "notifications.column_settings.sound": "播放音效",
   "notifications.settings": "字段设置",
+  "onboarding.done": "Done",
+  "onboarding.next": "Next",
+  "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+  "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
+  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to Mastodon!",
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
+  "onboarding.page_six.almost_done": "Almost done...",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
+  "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "community guidelines",
+  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
+  "onboarding.page_six.various_app": "mobile apps",
+  "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+  "onboarding.skip": "Skip",
   "privacy.change": "调整隐私设置",
   "privacy.direct.long": "只有提及的用户能看到",
   "privacy.direct.short": "私人消息",
@@ -119,11 +129,9 @@ const zh_cn = {
   "report.placeholder": "额外消息",
   "report.submit": "提交",
   "report.target": "Reporting",
-  "search_results.total": "{count, number} 项结果",
-  "search.account": "用户",
-  "search.hashtag": "标签",
   "search.placeholder": "搜索",
-  "search.status_by": "按{name}搜索嘟文",
+  "search_results.total": "{count, number} 项结果",
+  "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "删除",
   "status.favourite": "赞",
   "status.load_more": "加载更多",
@@ -133,6 +141,7 @@ const zh_cn = {
   "status.reblog": "转嘟",
   "status.reblogged_by": "{name} 转嘟",
   "status.reply": "回应",
+  "status.replyAll": "Reply to thread",
   "status.report": "举报 @{name}",
   "status.sensitive_toggle": "点击显示",
   "status.sensitive_warning": "敏感内容",
@@ -142,9 +151,7 @@ const zh_cn = {
   "tabs_bar.federated_timeline": "跨站",
   "tabs_bar.home": "主页",
   "tabs_bar.local_timeline": "本站",
-  "tabs_bar.mentions": "提及",
   "tabs_bar.notifications": "通知",
-  "tabs_bar.public": "跨站公共时间轴",
   "upload_area.title": "将文件拖放至此上传",
   "upload_button.label": "上传媒体文件",
   "upload_form.undo": "还原",
@@ -152,6 +159,5 @@ const zh_cn = {
   "video_player.expand": "展开影片",
   "video_player.toggle_sound": "开关音效",
   "video_player.toggle_visible": "打开或关上",
-};
-
-export default zh_cn;
+  "video_player.video_error": "Video could not be played"
+}
\ No newline at end of file
diff --git a/app/assets/javascripts/components/locales/zh-hk.jsx b/app/javascript/mastodon/locales/zh-HK.json
index 3ecb4737b..772b7f8fb 100644
--- a/app/assets/javascripts/components/locales/zh-hk.jsx
+++ b/app/javascript/mastodon/locales/zh-HK.json
@@ -1,30 +1,11 @@
-import zh from 'react-intl/locale-data/zh';
-
-const localeData = zh.reduce(function (acc, localeData) {
-  if (localeData.locale === "zh-Hant-HK") {
-    // rename the locale "zh-Hant-HK" as "zh-HK"
-    // (match the code usually used in Accepted-Language header)
-    acc.push(Object.assign({},
-      localeData,
-      {
-        "locale": "zh-HK",
-        "parentLocale": "zh-Hant-HK",
-      }
-    ));
-  }
-  return acc;
-}, []);
-
-export { localeData as localeData };
-
-const zh_hk = {
+{
   "account.block": "封鎖 @{name}",
   "account.disclaimer": "由於這個用戶在另一個服務站,實際數字會比這個更多。",
   "account.edit_profile": "修改個人資料",
   "account.follow": "關注",
   "account.followers": "關注的人",
-  "account.follows_you": "關注你",
   "account.follows": "正在關注",
+  "account.follows_you": "關注你",
   "account.mention": "提及 @{name}",
   "account.mute": "將 @{name} 靜音",
   "account.posts": "文章",
@@ -34,34 +15,50 @@ const zh_hk = {
   "account.unfollow": "取消關注",
   "account.unmute": "取消 @{name} 的靜音",
   "boost_modal.combo": "如你想在下次路過這顯示,請按{combo},",
-  "column_back_button.label": "返回",
   "column.blocks": "封鎖用戶",
   "column.community": "本站時間軸",
   "column.favourites": "喜歡的文章",
   "column.follow_requests": "關注請求",
   "column.home": "主頁",
+  "column.mutes": "Muted users",
   "column.notifications": "通知",
   "column.public": "跨站公共時間軸",
+  "column_back_button.label": "返回",
+  "column_subheading.navigation": "Navigation",
+  "column_subheading.settings": "Settings",
+  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+  "compose_form.lock_disclaimer.lock": "locked",
   "compose_form.placeholder": "你在想甚麼?",
   "compose_form.privacy_disclaimer": "你的私人文章,將被遞送至你所提及的 {domains} 用戶。你是否信任{domainsCount, plural, one {這個網站} other {這些網站}}?請留意,文章私隱設定只適用於各 Mastodon 服務站,如果 {domains} {domainsCount, plural, one {不是 Mastodon 服務站} other {之中有些不是 Mastodon 服務站}},對方將無法收到這篇文章的私隱設定,然後可能被轉推給不能預知的用戶閱讀。",
-  "compose_form.private": "標示為「只有關注你的人能看」",
   "compose_form.publish": "發文",
   "compose_form.sensitive": "將媒體檔案標示為「敏感內容」",
-  "compose_form.spoiler_placeholder": "敏感警告訊息",
   "compose_form.spoiler": "將部份文字藏於警告訊息之後",
-  "compose_form.unlisted": "請勿在公共時間軸顯示",
+  "compose_form.spoiler_placeholder": "敏感警告訊息",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.confirm": "Block",
+  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.delete.confirm": "Delete",
+  "confirmations.delete.message": "Are you sure you want to delete this status?",
+  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.message": "Are you sure you want to mute {name}?",
+  "emoji_button.activity": "Activity",
+  "emoji_button.flags": "Flags",
+  "emoji_button.food": "Food & Drink",
   "emoji_button.label": "加入表情符號",
+  "emoji_button.nature": "Nature",
+  "emoji_button.objects": "Objects",
+  "emoji_button.people": "People",
+  "emoji_button.search": "Search...",
+  "emoji_button.symbols": "Symbols",
+  "emoji_button.travel": "Travel & Places",
   "empty_column.community": "本站時間軸暫時未有內容,快貼文來搶頭香啊!",
   "empty_column.hashtag": "這個標籤暫時未有內容。",
   "empty_column.home": "你還沒有關注任何用戶。快看看{public},向其他用戶搭訕吧。",
   "empty_column.home.public_timeline": "公共時間軸",
-  "empty_column.home": "你還沒有關注任何用戶。快看看{public},向其他用戶搭訕吧。",
   "empty_column.notifications": "你沒有任何通知紀錄,快向其他用戶搭訕吧。",
   "empty_column.public": "跨站公共時間軸暫時沒有內容!快寫一些公共的文章,或者關注另一些服務站的用戶吧!你和本站、友站的交流,將決定這裏出現的內容。",
   "follow_request.authorize": "批准",
   "follow_request.reject": "拒絕",
-  "getting_started.about_addressing": "只要你知道一位用戶的用戶名稱和域名,你可以用「@用戶名稱@域名」的格式在搜尋欄尋找該用戶。",
-  "getting_started.about_shortcuts": "只要該用戶是在你現在的服務站開立,你可以直接輸入用戶𠱷搜尋。同樣的規則適用於在文章提及別的用戶。",
   "getting_started.apps": "手機或桌面應用程式",
   "getting_started.heading": "開始使用",
   "getting_started.open_source_notice": "Mastodon 是一個開放源碼的軟件。你可以在官方 GitHub ({github}) 貢獻或者回報問題。你亦可透過{apps}閱讀 Mastodon 上的消息。",
@@ -82,14 +79,14 @@ const zh_hk = {
   "navigation_bar.follow_requests": "關注請求",
   "navigation_bar.info": "關於本服務站",
   "navigation_bar.logout": "登出",
+  "navigation_bar.mutes": "Muted users",
   "navigation_bar.preferences": "偏好設定",
   "navigation_bar.public_timeline": "跨站公共時間軸",
   "notification.favourite": "{name} 喜歡你的文章",
   "notification.follow": "{name} 開始關注你",
-  "notification.mention": "{name} 提及你",
   "notification.reblog": "{name} 轉推你的文章",
-  "notifications.clear_confirmation": "你確定要清空通知紀錄嗎?",
   "notifications.clear": "清空通知紀錄",
+  "notifications.clear_confirmation": "你確定要清空通知紀錄嗎?",
   "notifications.column_settings.alert": "顯示桌面通知",
   "notifications.column_settings.favourite": "喜歡你的文章:",
   "notifications.column_settings.follow": "關注你:",
@@ -98,6 +95,26 @@ const zh_hk = {
   "notifications.column_settings.show": "在通知欄顯示",
   "notifications.column_settings.sound": "播放音效",
   "notifications.settings": "欄位設定",
+  "onboarding.done": "Done",
+  "onboarding.next": "Next",
+  "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
+  "onboarding.page_four.home": "The home timeline shows posts from people you follow.",
+  "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.",
+  "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to Mastodon!",
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
+  "onboarding.page_six.almost_done": "Almost done...",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.",
+  "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "community guidelines",
+  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
+  "onboarding.page_six.various_app": "mobile apps",
+  "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+  "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.",
+  "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+  "onboarding.skip": "Skip",
   "privacy.change": "調整私隱設定",
   "privacy.direct.long": "只有提及的用戶能看到",
   "privacy.direct.short": "私人訊息",
@@ -112,11 +129,9 @@ const zh_hk = {
   "report.placeholder": "額外訊息",
   "report.submit": "提交",
   "report.target": "Reporting",
-  "search_results.total": "{count, number} 項結果",
-  "search.account": "用戶",
-  "search.hashtag": "標籤",
   "search.placeholder": "搜尋",
-  "search.status_by": "按{name}搜尋文章",
+  "search_results.total": "{count, number} 項結果",
+  "status.cannot_reblog": "This post cannot be boosted",
   "status.delete": "刪除",
   "status.favourite": "喜歡",
   "status.load_more": "載入更多",
@@ -126,6 +141,7 @@ const zh_hk = {
   "status.reblog": "轉推",
   "status.reblogged_by": "{name} 轉推",
   "status.reply": "回應",
+  "status.replyAll": "Reply to thread",
   "status.report": "舉報 @{name}",
   "status.sensitive_toggle": "點擊顯示",
   "status.sensitive_warning": "敏感內容",
@@ -135,9 +151,7 @@ const zh_hk = {
   "tabs_bar.federated_timeline": "跨站",
   "tabs_bar.home": "主頁",
   "tabs_bar.local_timeline": "本站",
-  "tabs_bar.mentions": "提及",
   "tabs_bar.notifications": "通知",
-  "tabs_bar.public": "跨站公共時間軸",
   "upload_area.title": "將檔案拖放至此上載",
   "upload_button.label": "上載媒體檔案",
   "upload_form.undo": "還原",
@@ -145,6 +159,5 @@ const zh_hk = {
   "video_player.expand": "展開影片",
   "video_player.toggle_sound": "開關音效",
   "video_player.toggle_visible": "打開或關上",
-};
-
-export default zh_hk;
+  "video_player.video_error": "Video could not be played"
+}
\ No newline at end of file
diff --git a/app/assets/javascripts/components/middleware/errors.jsx b/app/javascript/mastodon/middleware/errors.js
index 9a51257cb..9a51257cb 100644
--- a/app/assets/javascripts/components/middleware/errors.jsx
+++ b/app/javascript/mastodon/middleware/errors.js
diff --git a/app/assets/javascripts/components/middleware/loading_bar.jsx b/app/javascript/mastodon/middleware/loading_bar.js
index a98f1bb2b..a98f1bb2b 100644
--- a/app/assets/javascripts/components/middleware/loading_bar.jsx
+++ b/app/javascript/mastodon/middleware/loading_bar.js
diff --git a/app/assets/javascripts/components/middleware/sounds.jsx b/app/javascript/mastodon/middleware/sounds.js
index 200efa3d7..200efa3d7 100644
--- a/app/assets/javascripts/components/middleware/sounds.jsx
+++ b/app/javascript/mastodon/middleware/sounds.js
diff --git a/app/javascript/mastodon/reducers/accounts.js b/app/javascript/mastodon/reducers/accounts.js
new file mode 100644
index 000000000..b3c2b6d88
--- /dev/null
+++ b/app/javascript/mastodon/reducers/accounts.js
@@ -0,0 +1,133 @@
+import {
+  ACCOUNT_FETCH_SUCCESS,
+  FOLLOWERS_FETCH_SUCCESS,
+  FOLLOWERS_EXPAND_SUCCESS,
+  FOLLOWING_FETCH_SUCCESS,
+  FOLLOWING_EXPAND_SUCCESS,
+  ACCOUNT_TIMELINE_FETCH_SUCCESS,
+  ACCOUNT_TIMELINE_EXPAND_SUCCESS,
+  FOLLOW_REQUESTS_FETCH_SUCCESS,
+  FOLLOW_REQUESTS_EXPAND_SUCCESS
+} from '../actions/accounts';
+import {
+  BLOCKS_FETCH_SUCCESS,
+  BLOCKS_EXPAND_SUCCESS
+} from '../actions/blocks';
+import {
+  MUTES_FETCH_SUCCESS,
+  MUTES_EXPAND_SUCCESS
+} from '../actions/mutes';
+import { COMPOSE_SUGGESTIONS_READY } from '../actions/compose';
+import {
+  REBLOG_SUCCESS,
+  UNREBLOG_SUCCESS,
+  FAVOURITE_SUCCESS,
+  UNFAVOURITE_SUCCESS,
+  REBLOGS_FETCH_SUCCESS,
+  FAVOURITES_FETCH_SUCCESS
+} from '../actions/interactions';
+import {
+  TIMELINE_REFRESH_SUCCESS,
+  TIMELINE_UPDATE,
+  TIMELINE_EXPAND_SUCCESS
+} from '../actions/timelines';
+import {
+  STATUS_FETCH_SUCCESS,
+  CONTEXT_FETCH_SUCCESS
+} from '../actions/statuses';
+import { SEARCH_FETCH_SUCCESS } from '../actions/search';
+import {
+  NOTIFICATIONS_UPDATE,
+  NOTIFICATIONS_REFRESH_SUCCESS,
+  NOTIFICATIONS_EXPAND_SUCCESS
+} from '../actions/notifications';
+import {
+  FAVOURITED_STATUSES_FETCH_SUCCESS,
+  FAVOURITED_STATUSES_EXPAND_SUCCESS
+} from '../actions/favourites';
+import { STORE_HYDRATE } from '../actions/store';
+import Immutable from 'immutable';
+
+const normalizeAccount = (state, account) => {
+  account = { ...account };
+
+  delete account.followers_count;
+  delete account.following_count;
+  delete account.statuses_count;
+
+  return state.set(account.id, Immutable.fromJS(account))
+};
+
+const normalizeAccounts = (state, accounts) => {
+  accounts.forEach(account => {
+    state = normalizeAccount(state, account);
+  });
+
+  return state;
+};
+
+const normalizeAccountFromStatus = (state, status) => {
+  state = normalizeAccount(state, status.account);
+
+  if (status.reblog && status.reblog.account) {
+    state = normalizeAccount(state, status.reblog.account);
+  }
+
+  return state;
+};
+
+const normalizeAccountsFromStatuses = (state, statuses) => {
+  statuses.forEach(status => {
+    state = normalizeAccountFromStatus(state, status);
+  });
+
+  return state;
+};
+
+const initialState = Immutable.Map();
+
+export default function accounts(state = initialState, action) {
+  switch(action.type) {
+  case STORE_HYDRATE:
+    return state.merge(action.state.get('accounts'));
+  case ACCOUNT_FETCH_SUCCESS:
+  case NOTIFICATIONS_UPDATE:
+    return normalizeAccount(state, action.account);
+  case FOLLOWERS_FETCH_SUCCESS:
+  case FOLLOWERS_EXPAND_SUCCESS:
+  case FOLLOWING_FETCH_SUCCESS:
+  case FOLLOWING_EXPAND_SUCCESS:
+  case REBLOGS_FETCH_SUCCESS:
+  case FAVOURITES_FETCH_SUCCESS:
+  case COMPOSE_SUGGESTIONS_READY:
+  case FOLLOW_REQUESTS_FETCH_SUCCESS:
+  case FOLLOW_REQUESTS_EXPAND_SUCCESS:
+  case BLOCKS_FETCH_SUCCESS:
+  case BLOCKS_EXPAND_SUCCESS:
+  case MUTES_FETCH_SUCCESS:
+  case MUTES_EXPAND_SUCCESS:
+    return normalizeAccounts(state, action.accounts);
+  case NOTIFICATIONS_REFRESH_SUCCESS:
+  case NOTIFICATIONS_EXPAND_SUCCESS:
+  case SEARCH_FETCH_SUCCESS:
+    return normalizeAccountsFromStatuses(normalizeAccounts(state, action.accounts), action.statuses);
+  case TIMELINE_REFRESH_SUCCESS:
+  case TIMELINE_EXPAND_SUCCESS:
+  case ACCOUNT_TIMELINE_FETCH_SUCCESS:
+  case ACCOUNT_TIMELINE_EXPAND_SUCCESS:
+  case CONTEXT_FETCH_SUCCESS:
+  case FAVOURITED_STATUSES_FETCH_SUCCESS:
+  case FAVOURITED_STATUSES_EXPAND_SUCCESS:
+    return normalizeAccountsFromStatuses(state, action.statuses);
+  case REBLOG_SUCCESS:
+  case FAVOURITE_SUCCESS:
+  case UNREBLOG_SUCCESS:
+  case UNFAVOURITE_SUCCESS:
+    return normalizeAccountFromStatus(state, action.response);
+  case TIMELINE_UPDATE:
+  case STATUS_FETCH_SUCCESS:
+    return normalizeAccountFromStatus(state, action.status);
+  default:
+    return state;
+  }
+};
diff --git a/app/assets/javascripts/components/reducers/accounts.jsx b/app/javascript/mastodon/reducers/accounts_counters.js
index 60d283b0f..2afc6c3d9 100644
--- a/app/assets/javascripts/components/reducers/accounts.jsx
+++ b/app/javascript/mastodon/reducers/accounts_counters.js
@@ -50,7 +50,11 @@ import {
 import { STORE_HYDRATE } from '../actions/store';
 import Immutable from 'immutable';
 
-const normalizeAccount = (state, account) => state.set(account.id, Immutable.fromJS(account));
+const normalizeAccount = (state, account) => state.set(account.id, Immutable.fromJS({
+  followers_count: account.followers_count,
+  following_count: account.following_count,
+  statuses_count: account.statuses_count,
+}));
 
 const normalizeAccounts = (state, accounts) => {
   accounts.forEach(account => {
@@ -80,10 +84,10 @@ const normalizeAccountsFromStatuses = (state, statuses) => {
 
 const initialState = Immutable.Map();
 
-export default function accounts(state = initialState, action) {
+export default function accountsCounters(state = initialState, action) {
   switch(action.type) {
   case STORE_HYDRATE:
-    return state.merge(action.state.get('accounts'));
+    return state.merge(action.state.get('accounts_counters'));
   case ACCOUNT_FETCH_SUCCESS:
   case NOTIFICATIONS_UPDATE:
     return normalizeAccount(state, action.account);
diff --git a/app/assets/javascripts/components/reducers/alerts.jsx b/app/javascript/mastodon/reducers/alerts.js
index dc0145824..dc0145824 100644
--- a/app/assets/javascripts/components/reducers/alerts.jsx
+++ b/app/javascript/mastodon/reducers/alerts.js
diff --git a/app/assets/javascripts/components/reducers/cards.jsx b/app/javascript/mastodon/reducers/cards.js
index 3c9395011..3c9395011 100644
--- a/app/assets/javascripts/components/reducers/cards.jsx
+++ b/app/javascript/mastodon/reducers/cards.js
diff --git a/app/assets/javascripts/components/reducers/compose.jsx b/app/javascript/mastodon/reducers/compose.js
index c87384780..c87384780 100644
--- a/app/assets/javascripts/components/reducers/compose.jsx
+++ b/app/javascript/mastodon/reducers/compose.js
diff --git a/app/assets/javascripts/components/reducers/index.jsx b/app/javascript/mastodon/reducers/index.js
index 147030cca..f05067c47 100644
--- a/app/assets/javascripts/components/reducers/index.jsx
+++ b/app/javascript/mastodon/reducers/index.js
@@ -7,6 +7,7 @@ import { loadingBarReducer } from 'react-redux-loading-bar';
 import modal from './modal';
 import user_lists from './user_lists';
 import accounts from './accounts';
+import accounts_counters from './accounts_counters';
 import statuses from './statuses';
 import relationships from './relationships';
 import search from './search';
@@ -26,6 +27,7 @@ export default combineReducers({
   user_lists,
   status_lists,
   accounts,
+  accounts_counters,
   statuses,
   relationships,
   search,
diff --git a/app/assets/javascripts/components/reducers/meta.jsx b/app/javascript/mastodon/reducers/meta.js
index acf6d4be1..acf6d4be1 100644
--- a/app/assets/javascripts/components/reducers/meta.jsx
+++ b/app/javascript/mastodon/reducers/meta.js
diff --git a/app/assets/javascripts/components/reducers/modal.jsx b/app/javascript/mastodon/reducers/modal.js
index 3566820ef..3566820ef 100644
--- a/app/assets/javascripts/components/reducers/modal.jsx
+++ b/app/javascript/mastodon/reducers/modal.js
diff --git a/app/assets/javascripts/components/reducers/notifications.jsx b/app/javascript/mastodon/reducers/notifications.js
index 1406a388a..c567a3a59 100644
--- a/app/assets/javascripts/components/reducers/notifications.jsx
+++ b/app/javascript/mastodon/reducers/notifications.js
@@ -49,7 +49,7 @@ const normalizeNotifications = (state, notifications, next) => {
   }
 
   return state
-    .update('items', list => loaded ? list.unshift(...items) : list.push(...items))
+    .update('items', list => loaded ? items.concat(list) : list.concat(items))
     .set('loaded', true)
     .set('isLoading', false);
 };
@@ -62,7 +62,7 @@ const appendNormalizedNotifications = (state, notifications, next) => {
   });
 
   return state
-    .update('items', list => list.push(...items))
+    .update('items', list => list.concat(items))
     .set('next', next)
     .set('isLoading', false);
 };
diff --git a/app/assets/javascripts/components/reducers/relationships.jsx b/app/javascript/mastodon/reducers/relationships.js
index c65c48b43..c65c48b43 100644
--- a/app/assets/javascripts/components/reducers/relationships.jsx
+++ b/app/javascript/mastodon/reducers/relationships.js
diff --git a/app/assets/javascripts/components/reducers/reports.jsx b/app/javascript/mastodon/reducers/reports.js
index eab004377..eab004377 100644
--- a/app/assets/javascripts/components/reducers/reports.jsx
+++ b/app/javascript/mastodon/reducers/reports.js
diff --git a/app/assets/javascripts/components/reducers/search.jsx b/app/javascript/mastodon/reducers/search.js
index b3fe6c7be..b3fe6c7be 100644
--- a/app/assets/javascripts/components/reducers/search.jsx
+++ b/app/javascript/mastodon/reducers/search.js
diff --git a/app/assets/javascripts/components/reducers/settings.jsx b/app/javascript/mastodon/reducers/settings.js
index 820af99ed..b255aabc4 100644
--- a/app/assets/javascripts/components/reducers/settings.jsx
+++ b/app/javascript/mastodon/reducers/settings.js
@@ -9,6 +9,10 @@ const initialState = Immutable.Map({
     shows: Immutable.Map({
       reblog: true,
       reply: true
+    }),
+
+    regex: Immutable.Map({
+      body: ''
     })
   }),
 
diff --git a/app/assets/javascripts/components/reducers/status_lists.jsx b/app/javascript/mastodon/reducers/status_lists.js
index b883b1c58..fd463cd63 100644
--- a/app/assets/javascripts/components/reducers/status_lists.jsx
+++ b/app/javascript/mastodon/reducers/status_lists.js
@@ -23,7 +23,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('items', map.get('items').push(...statuses.map(item => item.id)));
+    map.set('items', map.get('items').concat(statuses.map(item => item.id)));
   }));
 };
 
diff --git a/app/assets/javascripts/components/reducers/statuses.jsx b/app/javascript/mastodon/reducers/statuses.js
index 2002d2223..2002d2223 100644
--- a/app/assets/javascripts/components/reducers/statuses.jsx
+++ b/app/javascript/mastodon/reducers/statuses.js
diff --git a/app/assets/javascripts/components/reducers/timelines.jsx b/app/javascript/mastodon/reducers/timelines.js
index fa9863250..31e79f9f6 100644
--- a/app/assets/javascripts/components/reducers/timelines.jsx
+++ b/app/javascript/mastodon/reducers/timelines.js
@@ -116,7 +116,7 @@ const normalizeTimeline = (state, timeline, statuses, next) => {
     state = state.setIn([timeline, 'next'], next);
   }
 
-  return state.updateIn([timeline, 'items'], Immutable.List(), list => (loaded ? list.unshift(...ids) : ids));
+  return state.updateIn([timeline, 'items'], Immutable.List(), list => (loaded ? ids.concat(list) : ids));
 };
 
 const appendNormalizedTimeline = (state, timeline, statuses, next) => {
@@ -130,7 +130,7 @@ const appendNormalizedTimeline = (state, timeline, statuses, next) => {
   state = state.setIn([timeline, 'isLoading'], false);
   state = state.setIn([timeline, 'next'], next);
 
-  return state.updateIn([timeline, 'items'], Immutable.List(), list => list.push(...moreIds));
+  return state.updateIn([timeline, 'items'], Immutable.List(), list => list.concat(moreIds));
 };
 
 const normalizeAccountTimeline = (state, accountId, statuses, replace = false) => {
@@ -145,7 +145,7 @@ const normalizeAccountTimeline = (state, accountId, statuses, replace = false) =
     .set('isLoading', false)
     .set('loaded', true)
     .set('next', true)
-    .update('items', Immutable.List(), list => (replace ? ids : list.unshift(...ids))));
+    .update('items', Immutable.List(), list => (replace ? ids : ids.concat(list))));
 };
 
 const appendNormalizedAccountTimeline = (state, accountId, statuses, next) => {
@@ -159,7 +159,7 @@ const appendNormalizedAccountTimeline = (state, accountId, statuses, next) => {
   return state.updateIn(['accounts_timelines', accountId], Immutable.Map(), map => map
     .set('isLoading', false)
     .set('next', next)
-    .update('items', list => list.push(...moreIds)));
+    .update('items', list => list.concat(moreIds)));
 };
 
 const updateTimeline = (state, timeline, status, references) => {
diff --git a/app/assets/javascripts/components/reducers/user_lists.jsx b/app/javascript/mastodon/reducers/user_lists.js
index 7f55c3641..af9492119 100644
--- a/app/assets/javascripts/components/reducers/user_lists.jsx
+++ b/app/javascript/mastodon/reducers/user_lists.js
@@ -41,7 +41,7 @@ const normalizeList = (state, type, id, accounts, next) => {
 
 const appendToList = (state, type, id, accounts, next) => {
   return state.updateIn([type, id], map => {
-    return map.set('next', next).update('items', list => list.push(...accounts.map(item => item.id)));
+    return map.set('next', next).update('items', list => list.concat(accounts.map(item => item.id)));
   });
 };
 
@@ -62,18 +62,18 @@ export default function userLists(state = initialState, action) {
   case FOLLOW_REQUESTS_FETCH_SUCCESS:
     return state.setIn(['follow_requests', 'items'], Immutable.List(action.accounts.map(item => item.id))).setIn(['follow_requests', 'next'], action.next);
   case FOLLOW_REQUESTS_EXPAND_SUCCESS:
-    return state.updateIn(['follow_requests', 'items'], list => list.push(...action.accounts.map(item => item.id))).setIn(['follow_requests', 'next'], action.next);
+    return state.updateIn(['follow_requests', 'items'], list => list.concat(action.accounts.map(item => item.id))).setIn(['follow_requests', 'next'], action.next);
   case FOLLOW_REQUEST_AUTHORIZE_SUCCESS:
   case FOLLOW_REQUEST_REJECT_SUCCESS:
     return state.updateIn(['follow_requests', 'items'], list => list.filterNot(item => item === action.id));
   case BLOCKS_FETCH_SUCCESS:
     return state.setIn(['blocks', 'items'], Immutable.List(action.accounts.map(item => item.id))).setIn(['blocks', 'next'], action.next);
   case BLOCKS_EXPAND_SUCCESS:
-    return state.updateIn(['blocks', 'items'], list => list.push(...action.accounts.map(item => item.id))).setIn(['blocks', 'next'], action.next);
+    return state.updateIn(['blocks', 'items'], list => list.concat(action.accounts.map(item => item.id))).setIn(['blocks', 'next'], action.next);
   case MUTES_FETCH_SUCCESS:
     return state.setIn(['mutes', 'items'], Immutable.List(action.accounts.map(item => item.id))).setIn(['mutes', 'next'], action.next);
   case MUTES_EXPAND_SUCCESS:
-    return state.updateIn(['mutes', 'items'], list => list.push(...action.accounts.map(item => item.id))).setIn(['mutes', 'next'], action.next);
+    return state.updateIn(['mutes', 'items'], list => list.concat(action.accounts.map(item => item.id))).setIn(['mutes', 'next'], action.next);
   default:
     return state;
   }
diff --git a/app/assets/javascripts/components/rtl.jsx b/app/javascript/mastodon/rtl.js
index 8f14bb338..8f14bb338 100644
--- a/app/assets/javascripts/components/rtl.jsx
+++ b/app/javascript/mastodon/rtl.js
diff --git a/app/assets/javascripts/components/selectors/index.jsx b/app/javascript/mastodon/selectors/index.js
index 01a6cb264..7a75e2660 100644
--- a/app/assets/javascripts/components/selectors/index.jsx
+++ b/app/javascript/mastodon/selectors/index.js
@@ -5,15 +5,16 @@ const getStatuses = state => state.get('statuses');
 const getAccounts = state => state.get('accounts');
 
 const getAccountBase         = (state, id) => state.getIn(['accounts', id], null);
+const getAccountCounters     = (state, id) => state.getIn(['accounts_counters', id], null);
 const getAccountRelationship = (state, id) => state.getIn(['relationships', id], null);
 
 export const makeGetAccount = () => {
-  return createSelector([getAccountBase, getAccountRelationship], (base, relationship) => {
+  return createSelector([getAccountBase, getAccountCounters, getAccountRelationship], (base, counters, relationship) => {
     if (base === null) {
       return null;
     }
 
-    return base.set('relationship', relationship);
+    return base.merge(counters).set('relationship', relationship);
   });
 };
 
diff --git a/app/assets/javascripts/components/store/configureStore.jsx b/app/javascript/mastodon/store/configureStore.js
index a92d756f5..a92d756f5 100644
--- a/app/assets/javascripts/components/store/configureStore.jsx
+++ b/app/javascript/mastodon/store/configureStore.js
diff --git a/app/assets/javascripts/components/stream.jsx b/app/javascript/mastodon/stream.js
index 08da71607..08da71607 100644
--- a/app/assets/javascripts/components/stream.jsx
+++ b/app/javascript/mastodon/stream.js
diff --git a/app/assets/javascripts/components/uuid.jsx b/app/javascript/mastodon/uuid.js
index be1899305..be1899305 100644
--- a/app/assets/javascripts/components/uuid.jsx
+++ b/app/javascript/mastodon/uuid.js
diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js
new file mode 100644
index 000000000..cfd134a9b
--- /dev/null
+++ b/app/javascript/packs/application.js
@@ -0,0 +1,29 @@
+import Mastodon from 'mastodon/containers/mastodon';
+import React from 'react';
+import ReactDOM from 'react-dom';
+import 'font-awesome/css/font-awesome.css';
+import '../styles/application.scss';
+
+if (!window.Intl) {
+  require('intl');
+  require('intl/locale-data/jsonp/en.js');
+}
+
+window.jQuery = window.$ = require('jquery');
+window.Perf = require('react-addons-perf');
+
+require('jquery-ujs');
+require.context('../images/', true);
+
+const customContext = require.context('../../assets/stylesheets/', false);
+
+if (customContext.keys().indexOf('./custom.scss') !== -1) {
+  customContext('./custom.scss');
+}
+
+document.addEventListener('DOMContentLoaded', () => {
+  const mountNode = document.getElementById('mastodon');
+  const props = JSON.parse(mountNode.getAttribute('data-props'));
+
+  ReactDOM.render(<Mastodon {...props} />, mountNode);
+});
diff --git a/app/assets/javascripts/extras.jsx b/app/javascript/packs/public.js
index 7e3b7a256..24e7d0189 100644
--- a/app/assets/javascripts/extras.jsx
+++ b/app/javascript/packs/public.js
@@ -1,6 +1,10 @@
-import emojify from './components/emoji';
+import emojify from 'mastodon/emoji';
 import { length } from 'stringz';
 
+window.jQuery = window.$ = require('jquery');
+require('jquery-ujs');
+require.context('../images/', true);
+
 $(() => {
   $.each($('.emojify'), (_, content) => {
     const $content = $(content);
@@ -39,10 +43,10 @@ $(() => {
     }
   });
 
-  // used on /settings/profile
   $('.account_display_name').on('input', e => {
     $('.name-counter').text(30 - length($(e.target).val()));
   });
+
   $('.account_note').on('input', e => {
     $('.note-counter').text(160 - length($(e.target).val()));
   });
diff --git a/app/assets/stylesheets/about.scss b/app/javascript/styles/about.scss
index b1139b86f..b1139b86f 100644
--- a/app/assets/stylesheets/about.scss
+++ b/app/javascript/styles/about.scss
diff --git a/app/assets/stylesheets/accounts.scss b/app/javascript/styles/accounts.scss
index 99af9c982..99af9c982 100644
--- a/app/assets/stylesheets/accounts.scss
+++ b/app/javascript/styles/accounts.scss
diff --git a/app/assets/stylesheets/admin.scss b/app/javascript/styles/admin.scss
index 6d246ded2..6d246ded2 100644
--- a/app/assets/stylesheets/admin.scss
+++ b/app/javascript/styles/admin.scss
diff --git a/app/assets/stylesheets/application.scss b/app/javascript/styles/application.scss
index fb1767954..5895dd81f 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/javascript/styles/application.scss
@@ -2,7 +2,6 @@
 @import 'fonts/roboto';
 @import 'fonts/roboto-mono';
 @import 'fonts/montserrat';
-@import 'font-awesome';
 
 @import 'reset';
 @import 'basics';
diff --git a/app/assets/stylesheets/basics.scss b/app/javascript/styles/basics.scss
index e2485e695..78c142860 100644
--- a/app/assets/stylesheets/basics.scss
+++ b/app/javascript/styles/basics.scss
@@ -1,6 +1,6 @@
 body {
   font-family: 'Roboto', sans-serif;
-  background: $color1 image-url('background-photo.jpg');
+  background: $color1 url('../images/background-photo.jpg');
   background-size: cover;
   background-attachment: fixed;
   font-size: 13px;
diff --git a/app/assets/stylesheets/boost.scss b/app/javascript/styles/boost.scss
index 90511c88c..90511c88c 100644
--- a/app/assets/stylesheets/boost.scss
+++ b/app/javascript/styles/boost.scss
diff --git a/app/assets/stylesheets/compact_header.scss b/app/javascript/styles/compact_header.scss
index 8fef05f0f..8fef05f0f 100644
--- a/app/assets/stylesheets/compact_header.scss
+++ b/app/javascript/styles/compact_header.scss
diff --git a/app/assets/stylesheets/components.scss b/app/javascript/styles/components.scss
index fdff6eb9f..178ac05de 100644
--- a/app/assets/stylesheets/components.scss
+++ b/app/javascript/styles/components.scss
@@ -985,7 +985,7 @@ a.status__content__spoiler-link {
 }
 
 .transparent-background, .imageloader {
-  background: image-url('void.png');
+  background: url('../images/void.png');
 }
 
 .imageloader {
@@ -1684,7 +1684,7 @@ a.status__content__spoiler-link {
 .getting-started {
   box-sizing: border-box;
   padding-bottom: 235px;
-  background: image-url('mastodon-getting-started.png') no-repeat 0 100%/contain local;
+  background: url('../images/mastodon-getting-started.png') no-repeat 0 100%/contain local;
   flex: 1 0 auto;
 
   p {
@@ -1826,9 +1826,18 @@ button.icon-button.active i.fa-retweet {
   font-size: 16px;
   font-weight: 500;
   color: lighten($color1, 16%);
-  padding-top: 210px;
-  background: image-url('mastodon-not-found.png') no-repeat center -50px;
+  background: $color1;
   cursor: default;
+  display: flex;
+  flex: 1 1 auto;
+  align-items: center;
+  justify-content: center;
+
+  & > div {
+    background: url('../images/mastodon-not-found.png') no-repeat center -50px;
+    padding-top: 210px;
+    width: 100%;
+  }
 }
 
 .column-header {
@@ -2790,7 +2799,7 @@ button.icon-button.active i.fa-retweet {
 }
 
 .onboarding-modal__page-one__elephant-friend {
-  background: image-url('elephant-friend.png') no-repeat center center/contain;
+  background: url('../images/elephant-friend.png') no-repeat center center/contain;
   width: 147px;
   height: 160px;
   margin-right: 10px;
diff --git a/app/assets/stylesheets/containers.scss b/app/javascript/styles/containers.scss
index 6f339f998..6f339f998 100644
--- a/app/assets/stylesheets/containers.scss
+++ b/app/javascript/styles/containers.scss
diff --git a/app/javascript/styles/fonts/montserrat.scss b/app/javascript/styles/fonts/montserrat.scss
new file mode 100644
index 000000000..7496e0800
--- /dev/null
+++ b/app/javascript/styles/fonts/montserrat.scss
@@ -0,0 +1,11 @@
+@font-face {
+  font-family: 'Montserrat';
+  src: local('Montserrat');
+  src: url('../fonts/montserrat/Montserrat-Regular.eot');
+  src: url('../fonts/montserrat/Montserrat-Regular.eot?#iefix') format('embedded-opentype'),
+  url('../fonts/montserrat/Montserrat-Regular.woff2') format('woff2'),
+  url('../fonts/montserrat/Montserrat-Regular.woff') format('woff'),
+  url('../fonts/montserrat/Montserrat-Regular.ttf') format('truetype');
+  font-weight: 400;
+  font-style: normal;
+}
diff --git a/app/javascript/styles/fonts/roboto-mono.scss b/app/javascript/styles/fonts/roboto-mono.scss
new file mode 100644
index 000000000..846519d9f
--- /dev/null
+++ b/app/javascript/styles/fonts/roboto-mono.scss
@@ -0,0 +1,12 @@
+@font-face {
+  font-family: 'Roboto Mono';
+  src: local('Roboto Mono');
+  src: url('../fonts/roboto-mono/robotomono-regular-webfont.eot');
+  src: url('../fonts/roboto-mono/robotomono-regular-webfont.eot?#iefix') format('embedded-opentype'),
+  url('../fonts/roboto-mono/robotomono-regular-webfont.woff2') format('woff2'),
+  url('../fonts/roboto-mono/robotomono-regular-webfont.woff') format('woff'),
+  url('../fonts/roboto-mono/robotomono-regular-webfont.ttf') format('truetype'),
+  url('../fonts/roboto-mono/robotomono-regular-webfont.svg#roboto_monoregular') format('svg');
+  font-weight: 400;
+  font-style: normal;
+}
diff --git a/app/javascript/styles/fonts/roboto.scss b/app/javascript/styles/fonts/roboto.scss
new file mode 100644
index 000000000..6198a580f
--- /dev/null
+++ b/app/javascript/styles/fonts/roboto.scss
@@ -0,0 +1,52 @@
+@font-face {
+  font-family: 'Roboto';
+  src: local('Roboto');
+  src: url('../fonts/roboto/roboto-italic-webfont.eot');
+  src: url('../fonts/roboto/roboto-italic-webfont.eot?#iefix') format('embedded-opentype'),
+    url('../fonts/roboto/roboto-italic-webfont.woff2') format('woff2'),
+    url('../fonts/roboto/roboto-italic-webfont.woff') format('woff'),
+    url('../fonts/roboto/roboto-italic-webfont.ttf') format('truetype'),
+    url('../fonts/roboto/roboto-italic-webfont.svg#roboto-italic-webfont') format('svg');
+  font-weight: normal;
+  font-style: italic;
+}
+
+@font-face {
+  font-family: 'Roboto';
+  src: local('Roboto');
+  src: url('../fonts/roboto/roboto-bold-webfont.eot');
+  src: local('Roboto bold'), local('roboto-bold'),
+    url('../fonts/roboto/roboto-bold-webfont.eot?#iefix') format('embedded-opentype'),
+    url('../fonts/roboto/roboto-bold-webfont.woff2') format('woff2'),
+    url('../fonts/roboto/roboto-bold-webfont.woff') format('woff'),
+    url('../fonts/roboto/roboto-bold-webfont.ttf') format('truetype'),
+    url('../fonts/roboto/roboto-bold-webfont.svg#roboto-bold-webfont') format('svg');
+  font-weight: bold;
+  font-style: normal;
+}
+
+@font-face {
+  font-family: 'Roboto';
+  src: local('Roboto');
+  src: url('../fonts/roboto/roboto-medium-webfont.eot');
+  src: url('../fonts/roboto/roboto-medium-webfont.eot?#iefix') format('embedded-opentype'),
+    url('../fonts/roboto/roboto-medium-webfont.woff2') format('woff2'),
+    url('../fonts/roboto/roboto-medium-webfont.woff') format('woff'),
+    url('../fonts/roboto/roboto-medium-webfont.ttf') format('truetype'),
+    url('../fonts/roboto/roboto-medium-webfont.svg#roboto-medium-webfont') format('svg');
+  font-weight: 500;
+  font-style: normal;
+}
+
+@font-face {
+  font-family: 'Roboto';
+  src: local('Roboto');
+  src: url('../fonts/roboto/roboto-regular-webfont.eot');
+  src: url('../fonts/roboto/roboto-regular-webfont.eot?#iefix') format('embedded-opentype'),
+    url('../fonts/roboto/roboto-regular-webfont.woff2') format('woff2'),
+    url('../fonts/roboto/roboto-regular-webfont.woff') format('woff'),
+    url('../fonts/roboto/roboto-regular-webfont.ttf') format('truetype'),
+    url('../fonts/roboto/roboto-regular-webfont.svg#roboto-regular-webfont') format('svg');
+  font-weight: normal;
+  font-style: normal;
+}
diff --git a/app/assets/stylesheets/footer.scss b/app/javascript/styles/footer.scss
index 07ecb5e75..07ecb5e75 100644
--- a/app/assets/stylesheets/footer.scss
+++ b/app/javascript/styles/footer.scss
diff --git a/app/assets/stylesheets/forms.scss b/app/javascript/styles/forms.scss
index 18258099b..18258099b 100644
--- a/app/assets/stylesheets/forms.scss
+++ b/app/javascript/styles/forms.scss
diff --git a/app/assets/stylesheets/landing_strip.scss b/app/javascript/styles/landing_strip.scss
index 4c2403160..4c2403160 100644
--- a/app/assets/stylesheets/landing_strip.scss
+++ b/app/javascript/styles/landing_strip.scss
diff --git a/app/assets/stylesheets/lists.scss b/app/javascript/styles/lists.scss
index 47805663f..47805663f 100644
--- a/app/assets/stylesheets/lists.scss
+++ b/app/javascript/styles/lists.scss
diff --git a/app/assets/stylesheets/reset.scss b/app/javascript/styles/reset.scss
index 71064cc31..71064cc31 100644
--- a/app/assets/stylesheets/reset.scss
+++ b/app/javascript/styles/reset.scss
diff --git a/app/assets/stylesheets/rtl.scss b/app/javascript/styles/rtl.scss
index 6abb2d25c..6abb2d25c 100644
--- a/app/assets/stylesheets/rtl.scss
+++ b/app/javascript/styles/rtl.scss
diff --git a/app/assets/stylesheets/stream_entries.scss b/app/javascript/styles/stream_entries.scss
index fac4703b9..fac4703b9 100644
--- a/app/assets/stylesheets/stream_entries.scss
+++ b/app/javascript/styles/stream_entries.scss
diff --git a/app/assets/stylesheets/tables.scss b/app/javascript/styles/tables.scss
index ad8050580..ad8050580 100644
--- a/app/assets/stylesheets/tables.scss
+++ b/app/javascript/styles/tables.scss
diff --git a/app/assets/stylesheets/variables.scss b/app/javascript/styles/variables.scss
index 0dded90b0..0dded90b0 100755
--- a/app/assets/stylesheets/variables.scss
+++ b/app/javascript/styles/variables.scss
diff --git a/app/views/about/show.html.haml b/app/views/about/show.html.haml
index 4c008a06a..7bdfb1d2a 100644
--- a/app/views/about/show.html.haml
+++ b/app/views/about/show.html.haml
@@ -1,5 +1,5 @@
 - content_for :header_tags do
-  = javascript_include_tag 'application_public', integrity: true, crossorigin: 'anonymous'
+  = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous'
 
 - content_for :page_title do
   = site_hostname
@@ -10,20 +10,20 @@
   %meta{ property: 'og:type', content: 'website' }/
   %meta{ property: 'og:title', content: site_hostname }/
   %meta{ property: 'og:description', content: strip_tags(@instance_presenter.site_description.presence || t('about.about_mastodon')) }/
-  %meta{ property: 'og:image', content: asset_url('mastodon_small.jpg') }/
+  %meta{ property: 'og:image', content: asset_pack_path('mastodon_small.jpg') }/
   %meta{ property: 'og:image:width', content: '400' }/
   %meta{ property: 'og:image:height', content: '400' }/
   %meta{ property: 'twitter:card', content: 'summary' }/
 
 .wrapper
   %h1
-    = image_tag 'logo.png'
+    = image_tag asset_pack_path('logo.png')
     = Setting.site_title
 
   %p= t('about.about_mastodon').html_safe
 
   .screenshot-with-signup
-    .mascot= image_tag 'fluffy-elephant-friend.png'
+    .mascot= image_tag asset_pack_path('fluffy-elephant-friend.png')
 
     - if @instance_presenter.open_registrations
       = render 'registration'
diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml
index df4223ed2..55fbfe60e 100644
--- a/app/views/home/index.html.haml
+++ b/app/views/home/index.html.haml
@@ -1,6 +1,6 @@
 - content_for :header_tags do
   %script#initial-state{:type => 'application/json'}!= json_escape(render(file: 'home/initial_state', formats: :json))
 
-  = javascript_include_tag 'application', integrity: true, crossorigin: 'anonymous'
+  = javascript_pack_tag 'application', integrity: true, crossorigin: 'anonymous'
 
-= react_component 'Mastodon', default_props, class: 'app-holder', prerender: false
+.app-holder#mastodon{ data: { props: Oj.dump(default_props) }}
diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml
index d3d97e3a0..f7cea41e2 100644
--- a/app/views/layouts/admin.html.haml
+++ b/app/views/layouts/admin.html.haml
@@ -1,12 +1,12 @@
 - content_for :header_tags do
-  = javascript_include_tag 'application_public', integrity: true, crossorigin: 'anonymous'
+  = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous'
 
 - content_for :content do
   .admin-wrapper
     .sidebar-wrapper
       .sidebar
         = link_to root_path do
-          = image_tag 'logo.png', class: 'logo'
+          = image_tag asset_pack_path('logo.png'), class: 'logo'
 
         = render_navigation
     .content-wrapper
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index 602d502a9..683d0272f 100755
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -19,7 +19,9 @@
         = ' - '
       = title
 
-    = stylesheet_link_tag stylesheet_for_layout, media: 'all'
+    = stylesheet_pack_tag 'vendor', media: 'all'
+    = stylesheet_pack_tag 'application', media: 'all'
+    = javascript_pack_tag 'vendor', integrity: true, crossorigin: 'anonymous'
     = csrf_meta_tags
 
     = yield :header_tags
diff --git a/app/views/layouts/auth.html.haml b/app/views/layouts/auth.html.haml
index b2ceed12a..2478776ab 100644
--- a/app/views/layouts/auth.html.haml
+++ b/app/views/layouts/auth.html.haml
@@ -1,12 +1,12 @@
 - content_for :header_tags do
-  = javascript_include_tag 'application_public', integrity: true, crossorigin: 'anonymous'
+  = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous'
 
 - content_for :content do
   .container
     .logo-container
       %h1
         = link_to root_path do
-          = image_tag 'logo.png'
+          = image_tag asset_pack_path('logo.png')
 
     .form-container
       = render 'flashes'
diff --git a/app/views/layouts/embedded.html.haml b/app/views/layouts/embedded.html.haml
index 596f3c36e..fcc474ff6 100644
--- a/app/views/layouts/embedded.html.haml
+++ b/app/views/layouts/embedded.html.haml
@@ -3,6 +3,6 @@
   %head
     %meta{:charset => 'utf-8'}/
     = stylesheet_link_tag 'application', media: 'all'
-    = javascript_include_tag 'application_public', integrity: true, crossorigin: 'anonymous'
+    = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous'
   %body.embed
     = yield
diff --git a/app/views/layouts/public.html.haml b/app/views/layouts/public.html.haml
index 7bb0f71ff..9b94ad9a4 100644
--- a/app/views/layouts/public.html.haml
+++ b/app/views/layouts/public.html.haml
@@ -1,5 +1,5 @@
 - content_for :header_tags do
-  = javascript_include_tag 'application_public', integrity: true, crossorigin: 'anonymous'
+  = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous'
 
 - content_for :content do
   .container= yield