about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.babelrc22
-rw-r--r--.foreman1
-rw-r--r--.gitignore4
-rw-r--r--.postcssrc.yml4
-rw-r--r--.travis.yml7
-rw-r--r--Dockerfile15
-rw-r--r--Gemfile28
-rw-r--r--Gemfile.lock43
-rw-r--r--Procfile.dev3
-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
-rwxr-xr-xbin/webpack33
-rwxr-xr-xbin/webpack-dev-server33
-rwxr-xr-xbin/yarn11
-rw-r--r--config/application.rb4
-rw-r--r--config/environments/development.rb2
-rw-r--r--config/environments/production.rb2
-rw-r--r--config/initializers/assets.rb2
-rw-r--r--config/webpack/configuration.js26
-rw-r--r--config/webpack/development.js16
-rw-r--r--config/webpack/development.server.js18
-rw-r--r--config/webpack/development.server.yml17
-rw-r--r--config/webpack/loaders/assets.js12
-rw-r--r--config/webpack/loaders/babel.js5
-rw-r--r--config/webpack/loaders/coffee.js4
-rw-r--r--config/webpack/loaders/erb.js9
-rw-r--r--config/webpack/loaders/sass.js14
-rw-r--r--config/webpack/paths.yml33
-rw-r--r--config/webpack/production.js44
-rw-r--r--config/webpack/shared.js59
-rw-r--r--config/webpack/test.js6
-rw-r--r--config/webpack/translationRunner.js34
-rw-r--r--docker-compose.yml17
-rw-r--r--package.json58
-rw-r--r--spec/controllers/api/v1/accounts_controller_spec.rb7
-rw-r--r--spec/features/log_in_spec.rb2
-rw-r--r--spec/javascript/components/avatar.test.jsx2
-rw-r--r--spec/javascript/components/button.test.jsx2
-rw-r--r--spec/javascript/components/display_name.test.jsx2
-rw-r--r--spec/javascript/components/dropdown_menu.test.jsx2
-rw-r--r--spec/javascript/components/features/ui/components/column.test.jsx4
-rw-r--r--spec/javascript/components/loading_indicator.test.jsx2
-rw-r--r--yarn.lock1758
343 files changed, 5280 insertions, 2062 deletions
diff --git a/.babelrc b/.babelrc
index 928d32e3b..2c4d9aab3 100644
--- a/.babelrc
+++ b/.babelrc
@@ -1,7 +1,25 @@
 {
-  "presets": ["es2015", "react"],
+  "presets": [
+    "es2015",
+    "react",
+    [
+      "env",
+      {
+        "loose": true,
+        "modules": false
+      }
+    ]
+  ],
   "plugins": [
+    "transform-react-jsx-source",
+    "transform-react-jsx-self",
     "transform-decorators-legacy",
-    "transform-object-rest-spread"
+    "transform-object-rest-spread",
+    [
+      "react-intl",
+      {
+        "messagesDir": "./build/messages"
+      }
+    ]
   ]
 }
diff --git a/.foreman b/.foreman
new file mode 100644
index 000000000..722b491f7
--- /dev/null
+++ b/.foreman
@@ -0,0 +1 @@
+procfile: Procfile.dev
diff --git a/.gitignore b/.gitignore
index c6c468cc7..a0a7eac9c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,7 +22,7 @@ public/assets
 .env
 .env.production
 node_modules/
-neo4j/
+build/
 
 # Ignore Vagrant files
 .vagrant/
@@ -43,3 +43,5 @@ redis
 # Ignore vim files
 *~
 *.swp
+/public/packs
+/node_modules
diff --git a/.postcssrc.yml b/.postcssrc.yml
new file mode 100644
index 000000000..bc4f02ab3
--- /dev/null
+++ b/.postcssrc.yml
@@ -0,0 +1,4 @@
+plugins:
+  postcss-smart-import: {}
+  precss: {}
+  autoprefixer: {}
diff --git a/.travis.yml b/.travis.yml
index 90a33f51d..58be0908f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,9 +1,7 @@
 language: ruby
 cache:
   bundler: true
-  yarn: true
-  directories:
-    - node_modules
+  yarn: false
 dist: trusty
 sudo: false
 
@@ -42,7 +40,8 @@ install:
   - yarn install
 
 before_script:
-  - bundle exec rails db:create db:migrate
+  - bundle exec rails db:create db:schema:load
+  - bundle exec rails assets:precompile
 
 script:
   - bundle exec rspec
diff --git a/Dockerfile b/Dockerfile
index 9968e8227..3e4727258 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -10,8 +10,6 @@ EXPOSE 3000 4000
 
 WORKDIR /mastodon
 
-COPY Gemfile Gemfile.lock package.json yarn.lock /mastodon/
-
 RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories \
  && BUILD_DEPS=" \
     postgresql-dev \
@@ -23,6 +21,7 @@ RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/reposit
     $BUILD_DEPS \
     nodejs@edge \
     nodejs-npm@edge \
+    git \
     libpq \
     libxml2 \
     libxslt \
@@ -31,14 +30,14 @@ RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/reposit
     imagemagick@edge \
     ca-certificates \
  && npm install -g npm@3 && npm install -g yarn \
- && bundle install --deployment --without test development \
- && yarn --ignore-optional \
- && yarn cache clean \
- && npm -g cache clean \
  && update-ca-certificates \
- && apk del $BUILD_DEPS \
  && rm -rf /tmp/* /var/cache/apk/*
 
+COPY Gemfile Gemfile.lock package.json yarn.lock /mastodon/
+
+RUN bundle install --deployment --without test development \
+ && yarn --ignore-optional --pure-lockfile
+
 COPY . /mastodon
 
-VOLUME /mastodon/public/system /mastodon/public/assets
+VOLUME /mastodon/public/system /mastodon/public/assets /mastodon/public/packs
diff --git a/Gemfile b/Gemfile
index f946440c6..8009e2cbe 100644
--- a/Gemfile
+++ b/Gemfile
@@ -5,22 +5,19 @@ ruby '>= 2.3.0', '< 2.5.0'
 
 gem 'pkg-config'
 
+gem 'puma'
 gem 'rails', '~> 5.0.2'
-gem 'sass-rails', '~> 5.0'
 gem 'uglifier', '>= 1.3.0'
-gem 'jquery-rails'
-gem 'puma'
 
 gem 'hamlit-rails'
 gem 'pg'
 gem 'pghero'
 gem 'dotenv-rails'
-gem 'font-awesome-rails'
 gem 'best_in_place', '~> 3.0.1'
 
+gem 'aws-sdk', '>= 2.0'
 gem 'paperclip', '~> 5.1'
 gem 'paperclip-av-transcoder'
-gem 'aws-sdk', '>= 2.0'
 
 gem 'addressable'
 gem 'devise'
@@ -58,18 +55,18 @@ gem 'sprockets-rails', require: 'sprockets/railtie'
 gem 'statsd-instrument'
 gem 'twitter-text'
 gem 'tzinfo-data'
+gem 'webpacker', '~>1.2'
 gem 'whatlanguage'
 
+# For some reason the view specs start failing without this
 gem 'react-rails'
-gem 'browserify-rails'
-gem 'autoprefixer-rails'
 
 group :development, :test do
-  gem 'rspec-rails'
-  gem 'pry-rails'
-  gem 'fuubar'
   gem 'fabrication'
+  gem 'fuubar'
   gem 'i18n-tasks', '~> 0.9.6'
+  gem 'pry-rails'
+  gem 'rspec-rails'
 end
 
 group :test do
@@ -83,24 +80,23 @@ group :test do
 end
 
 group :development do
-  gem 'rubocop', '0.46.0', require: false
+  gem 'active_record_query_trace'
+  gem 'annotate'
   gem 'better_errors'
   gem 'binding_of_caller'
+  gem 'bullet'
   gem 'letter_opener'
   gem 'letter_opener_web'
-  gem 'bullet'
-  gem 'active_record_query_trace'
-  gem 'annotate'
+  gem 'rubocop', '0.46.0', require: false
 
   gem 'capistrano', '3.8.0'
   gem 'capistrano-rails'
   gem 'capistrano-rbenv'
   gem 'capistrano-yarn'
-  gem 'capistrano-faster-assets', '~> 1.0'
 end
 
 group :production do
+  gem 'lograge'
   gem 'rails_12factor'
   gem 'redis-rails'
-  gem 'lograge'
 end
diff --git a/Gemfile.lock b/Gemfile.lock
index acda0d387..ab811bcfd 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -43,15 +43,13 @@ GEM
       public_suffix (~> 2.0, >= 2.0.2)
     airbrussh (1.2.0)
       sshkit (>= 1.6.1, != 1.7.0)
-    annotate (2.7.1)
-      activerecord (>= 3.2, < 6.0)
-      rake (>= 10.4, < 12.0)
+    annotate (2.6.5)
+      activerecord (>= 2.3.0)
+      rake (>= 0.8.7)
     arel (7.1.4)
     ast (2.3.0)
     attr_encrypted (3.0.3)
       encryptor (~> 3.0.0)
-    autoprefixer-rails (6.7.7.2)
-      execjs
     av (0.9.0)
       cocaine (~> 0.5.3)
     aws-sdk (2.9.12)
@@ -76,10 +74,6 @@ GEM
       rack (>= 0.9.0)
     binding_of_caller (0.7.2)
       debug_inspector (>= 0.0.1)
-    browserify-rails (4.1.0)
-      addressable (>= 2.4.0)
-      railties (>= 4.0.0, < 5.1)
-      sprockets (>= 3.6.0)
     builder (3.2.3)
     bullet (5.5.1)
       activesupport (>= 3.0.0)
@@ -92,8 +86,6 @@ GEM
     capistrano-bundler (1.2.0)
       capistrano (~> 3.1)
       sshkit (~> 1.2)
-    capistrano-faster-assets (1.0.2)
-      capistrano (>= 3.1)
     capistrano-rails (1.2.3)
       capistrano (~> 3.1)
       capistrano-bundler (~> 1.1)
@@ -161,8 +153,6 @@ GEM
     faker (1.7.3)
       i18n (~> 0.5)
     fast_blank (1.0.0)
-    font-awesome-rails (4.7.0.1)
-      railties (>= 3.2, < 5.1)
     fuubar (2.2.0)
       rspec-core (~> 3.0)
       ruby-progressbar (~> 1.4)
@@ -210,10 +200,6 @@ GEM
       rainbow (~> 2.2)
       terminal-table (>= 1.5.1)
     jmespath (1.3.1)
-    jquery-rails (4.3.1)
-      rails-dom-testing (>= 1, < 3)
-      railties (>= 4.2.0)
-      thor (>= 0.14, < 2.0)
     json (2.1.0)
     kaminari (1.0.1)
       activesupport (>= 4.1.0)
@@ -257,6 +243,7 @@ GEM
     mimemagic (0.3.2)
     mini_portile2 (2.1.0)
     minitest (5.10.1)
+    multi_json (1.12.1)
     net-scp (1.2.1)
       net-ssh (>= 2.6.5)
     net-ssh (4.1.0)
@@ -348,8 +335,8 @@ GEM
       thor (>= 0.18.1, < 2.0)
     rainbow (2.2.2)
       rake
-    rake (11.3.0)
-    react-rails (1.11.0)
+    rake (12.0.0)
+    react-rails (2.1.0)
       babel-transpiler (>= 0.7.0)
       connection_pool
       execjs
@@ -410,13 +397,6 @@ GEM
       crass (~> 1.0.2)
       nokogiri (>= 1.4.4)
       nokogumbo (~> 1.4.1)
-    sass (3.4.23)
-    sass-rails (5.0.6)
-      railties (>= 4.0.0, < 6)
-      sass (~> 3.1)
-      sprockets (>= 2.8, < 4.0)
-      sprockets-rails (>= 2.0, < 4.0)
-      tilt (>= 1.1, < 3)
     sidekiq (4.2.10)
       concurrent-ruby (~> 1.0)
       connection_pool (~> 2.2, >= 2.2.0)
@@ -473,6 +453,10 @@ GEM
       addressable (>= 2.3.6)
       crack (>= 0.3.2)
       hashdiff
+    webpacker (1.2)
+      activesupport (>= 4.2)
+      multi_json (~> 1.2)
+      railties (>= 4.2)
     websocket-driver (0.6.5)
       websocket-extensions (>= 0.1.0)
     websocket-extensions (0.1.2)
@@ -487,15 +471,12 @@ DEPENDENCIES
   active_record_query_trace
   addressable
   annotate
-  autoprefixer-rails
   aws-sdk (>= 2.0)
   best_in_place (~> 3.0.1)
   better_errors
   binding_of_caller
-  browserify-rails
   bullet
   capistrano (= 3.8.0)
-  capistrano-faster-assets (~> 1.0)
   capistrano-rails
   capistrano-rbenv
   capistrano-yarn
@@ -507,7 +488,6 @@ DEPENDENCIES
   fabrication
   faker
   fast_blank
-  font-awesome-rails
   fuubar
   goldfinger
   hamlit-rails
@@ -517,7 +497,6 @@ DEPENDENCIES
   http_accept_language
   httplog
   i18n-tasks (~> 0.9.6)
-  jquery-rails
   kaminari
   letter_opener
   letter_opener_web
@@ -554,7 +533,6 @@ DEPENDENCIES
   rubocop (= 0.46.0)
   ruby-oembed
   sanitize
-  sass-rails (~> 5.0)
   sidekiq
   sidekiq-unique-jobs
   simple-navigation
@@ -566,6 +544,7 @@ DEPENDENCIES
   tzinfo-data
   uglifier (>= 1.3.0)
   webmock
+  webpacker (~> 1.2)
   whatlanguage
 
 RUBY VERSION
diff --git a/Procfile.dev b/Procfile.dev
new file mode 100644
index 000000000..4ecea287c
--- /dev/null
+++ b/Procfile.dev
@@ -0,0 +1,3 @@
+web: bundle exec rails s -p 3000
+stream: yarn run start
+webpack: ./bin/webpack-dev-server
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
diff --git a/bin/webpack b/bin/webpack
new file mode 100755
index 000000000..a871ce77b
--- /dev/null
+++ b/bin/webpack
@@ -0,0 +1,33 @@
+#!/usr/bin/env ruby
+$stdout.sync = true
+
+require "shellwords"
+require "yaml"
+
+ENV["RAILS_ENV"] ||= "development"
+RAILS_ENV   = ENV["RAILS_ENV"]
+
+ENV["NODE_ENV"] ||= RAILS_ENV
+NODE_ENV    = ENV["NODE_ENV"]
+
+APP_PATH               = File.expand_path("../", __dir__)
+CONFIG_PATH            = File.join(APP_PATH, "config/webpack/paths.yml")
+
+begin
+  paths            = YAML.load(File.read(CONFIG_PATH))[NODE_ENV]
+
+  NODE_MODULES_PATH   = File.join(APP_PATH.shellescape, paths["node_modules"])
+  WEBPACK_CONFIG_PATH = File.join(APP_PATH.shellescape, paths["config"])
+rescue Errno::ENOENT, NoMethodError
+  puts "Configuration not found in config/webpack/paths.yml"
+  puts "Please run bundle exec rails webpacker:install to install webpacker"
+  exit!
+end
+
+WEBPACK_BIN    = "#{NODE_MODULES_PATH}/.bin/webpack"
+WEBPACK_CONFIG = "#{WEBPACK_CONFIG_PATH}/#{NODE_ENV}.js"
+
+Dir.chdir(APP_PATH) do
+  exec "NODE_PATH=#{NODE_MODULES_PATH} #{WEBPACK_BIN} --config #{WEBPACK_CONFIG}" \
+    " #{ARGV.join(" ")}"
+end
diff --git a/bin/webpack-dev-server b/bin/webpack-dev-server
new file mode 100755
index 000000000..c2a61ff5e
--- /dev/null
+++ b/bin/webpack-dev-server
@@ -0,0 +1,33 @@
+#!/usr/bin/env ruby
+$stdout.sync = true
+
+require "shellwords"
+require "yaml"
+
+ENV["RAILS_ENV"] ||= "development"
+RAILS_ENV = ENV["RAILS_ENV"]
+
+ENV["NODE_ENV"] ||= RAILS_ENV
+NODE_ENV = ENV["NODE_ENV"]
+
+APP_PATH    = File.expand_path("../", __dir__)
+CONFIG_PATH = File.join(APP_PATH, "config/webpack/paths.yml")
+
+begin
+  paths = YAML.load(File.read(CONFIG_PATH))[NODE_ENV]
+
+  NODE_MODULES_PATH   = File.join(APP_PATH.shellescape, paths["node_modules"])
+  WEBPACK_CONFIG_PATH = File.join(APP_PATH.shellescape, paths["config"])
+
+  WEBPACK_BIN       = "#{NODE_MODULES_PATH}/.bin/webpack-dev-server"
+  DEV_SERVER_CONFIG = "#{WEBPACK_CONFIG_PATH}/development.server.js"
+rescue Errno::ENOENT, NoMethodError
+  puts "Configuration not found in config/webpacker/paths.yml."
+  puts "Please run bundle exec rails webpacker:install to install webpacker"
+  exit!
+end
+
+Dir.chdir(APP_PATH) do
+  exec "NODE_PATH=#{NODE_MODULES_PATH} #{WEBPACK_BIN} --progress --color " \
+    "--config #{DEV_SERVER_CONFIG} #{ARGV.join(" ")}"
+end
diff --git a/bin/yarn b/bin/yarn
new file mode 100755
index 000000000..c2bacef83
--- /dev/null
+++ b/bin/yarn
@@ -0,0 +1,11 @@
+#!/usr/bin/env ruby
+VENDOR_PATH = File.expand_path('..', __dir__)
+Dir.chdir(VENDOR_PATH) do
+  begin
+    exec "yarnpkg #{ARGV.join(" ")}"
+  rescue Errno::ENOENT
+    $stderr.puts "Yarn executable was not detected in the system."
+    $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
+    exit 1
+  end
+end
diff --git a/config/application.rb b/config/application.rb
index 2e7e5cd49..1b5820fa3 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -74,10 +74,6 @@ module Mastodon
     config.middleware.use Rack::Attack
     config.middleware.use Rack::Deflater
 
-    # babel config can be found in .babelrc
-    config.browserify_rails.commandline_options   = '--transform babelify --extension=".jsx"'
-    config.browserify_rails.evaluate_node_modules = true
-
     config.to_prepare do
       Doorkeeper::AuthorizationsController.layout 'public'
       Doorkeeper::AuthorizedApplicationsController.layout 'admin'
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 6157f20d3..4b25ab1a8 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -75,8 +75,6 @@ Rails.application.configure do
 
     Bullet.add_whitelist type: :n_plus_one_query, class_name: 'User', association: :account
   end
-
-  config.react.variant = :development
 end
 
 require 'sidekiq/testing'
diff --git a/config/environments/production.rb b/config/environments/production.rb
index a1cd0fb35..1f2b5e05d 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -106,8 +106,6 @@ Rails.application.configure do
 
   config.action_mailer.delivery_method = ENV.fetch('SMTP_DELIVERY_METHOD', 'smtp').to_sym
 
-  config.react.variant = :production
-
   config.to_prepare do
     StatsD.backend = StatsD::Instrument::Backends::NullBackend.new if ENV['STATSD_ADDR'].blank?
   end
diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb
index e6bd8a015..f2bf17364 100644
--- a/config/initializers/assets.rb
+++ b/config/initializers/assets.rb
@@ -8,6 +8,6 @@ Rails.application.config.assets.version = '1.0'
 
 # Precompile additional assets.
 # application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
-Rails.application.config.assets.precompile += %w(application_public.js custom.css)
+# Rails.application.config.assets.precompile += %w(application_public.js custom.css)
 
 Rails.application.config.assets.initialize_on_precompile = true
diff --git a/config/webpack/configuration.js b/config/webpack/configuration.js
new file mode 100644
index 000000000..61c5b821f
--- /dev/null
+++ b/config/webpack/configuration.js
@@ -0,0 +1,26 @@
+// Common configuration for webpacker loaded from config/webpack/paths.yml
+
+const { join, resolve } = require('path')
+const { env } = require('process')
+const { safeLoad } = require('js-yaml')
+const { readFileSync } = require('fs')
+
+const configPath = resolve('config', 'webpack')
+const loadersDir = join(__dirname, 'loaders')
+const paths = safeLoad(readFileSync(join(configPath, 'paths.yml'), 'utf8'))[env.NODE_ENV]
+const devServer = safeLoad(readFileSync(join(configPath, 'development.server.yml'), 'utf8'))[env.NODE_ENV]
+
+// Compute public path based on environment and CDN_HOST in production
+const ifHasCDN = env.CDN_HOST !== undefined && env.NODE_ENV === 'production'
+const devServerUrl = `http://${devServer.host}:${devServer.port}/${paths.entry}/`
+const publicUrl = ifHasCDN ? `${env.CDN_HOST}/${paths.entry}/` : `/${paths.entry}/`
+const publicPath = env.NODE_ENV !== 'production' ? devServerUrl : publicUrl
+
+module.exports = {
+  devServer,
+  env,
+  paths,
+  loadersDir,
+  publicUrl,
+  publicPath
+}
diff --git a/config/webpack/development.js b/config/webpack/development.js
new file mode 100644
index 000000000..7dfa2df11
--- /dev/null
+++ b/config/webpack/development.js
@@ -0,0 +1,16 @@
+// Note: You must restart bin/webpack-dev-server for changes to take effect
+
+const merge = require('webpack-merge')
+const sharedConfig = require('./shared.js')
+
+module.exports = merge(sharedConfig, {
+  devtool: 'sourcemap',
+
+  stats: {
+    errorDetails: true
+  },
+
+  output: {
+    pathinfo: true
+  }
+})
diff --git a/config/webpack/development.server.js b/config/webpack/development.server.js
new file mode 100644
index 000000000..5c5631a4e
--- /dev/null
+++ b/config/webpack/development.server.js
@@ -0,0 +1,18 @@
+// Note: You must restart bin/webpack-dev-server for changes to take effect
+
+const { resolve } = require('path')
+const merge = require('webpack-merge')
+const devConfig = require('./development.js')
+const { devServer, publicPath, paths } = require('./configuration.js')
+
+module.exports = merge(devConfig, {
+  devServer: {
+    host: devServer.host,
+    port: devServer.port,
+    headers: { "Access-Control-Allow-Origin": "*" },
+    compress: true,
+    historyApiFallback: true,
+    contentBase: resolve(paths.output, paths.entry),
+    publicPath
+  }
+})
diff --git a/config/webpack/development.server.yml b/config/webpack/development.server.yml
new file mode 100644
index 000000000..ee588a888
--- /dev/null
+++ b/config/webpack/development.server.yml
@@ -0,0 +1,17 @@
+# Note: You must restart bin/webpack-dev-server for changes to take effect
+
+default: &default
+  enabled: true
+  host: localhost
+  port: 8080
+
+development:
+  <<: *default
+
+test:
+  <<: *default
+  enabled: false
+
+production:
+  <<: *default
+  enabled: false
diff --git a/config/webpack/loaders/assets.js b/config/webpack/loaders/assets.js
new file mode 100644
index 000000000..595f073fc
--- /dev/null
+++ b/config/webpack/loaders/assets.js
@@ -0,0 +1,12 @@
+const { env, publicPath } = require('../configuration.js')
+
+module.exports = {
+  test: /\.(jpg|jpeg|png|gif|svg|eot|ttf|woff|woff2)$/i,
+  use: [{
+    loader: 'file-loader',
+    options: {
+      publicPath,
+      name: env.NODE_ENV === 'production' ? '[name]-[hash].[ext]' : '[name].[ext]'
+    }
+  }]
+}
diff --git a/config/webpack/loaders/babel.js b/config/webpack/loaders/babel.js
new file mode 100644
index 000000000..c608e708f
--- /dev/null
+++ b/config/webpack/loaders/babel.js
@@ -0,0 +1,5 @@
+module.exports = {
+  test: /\.js(\.erb)?$/,
+  exclude: /node_modules/,
+  loader: 'babel-loader'
+}
diff --git a/config/webpack/loaders/coffee.js b/config/webpack/loaders/coffee.js
new file mode 100644
index 000000000..dae874249
--- /dev/null
+++ b/config/webpack/loaders/coffee.js
@@ -0,0 +1,4 @@
+module.exports = {
+  test: /\.coffee(\.erb)?$/,
+  loader: 'coffee-loader'
+}
diff --git a/config/webpack/loaders/erb.js b/config/webpack/loaders/erb.js
new file mode 100644
index 000000000..4cd7d6849
--- /dev/null
+++ b/config/webpack/loaders/erb.js
@@ -0,0 +1,9 @@
+module.exports = {
+  test: /\.erb$/,
+  enforce: 'pre',
+  exclude: /node_modules/,
+  loader: 'rails-erb-loader',
+  options: {
+    runner: 'bin/rails runner'
+  }
+}
diff --git a/config/webpack/loaders/sass.js b/config/webpack/loaders/sass.js
new file mode 100644
index 000000000..2cb0e759a
--- /dev/null
+++ b/config/webpack/loaders/sass.js
@@ -0,0 +1,14 @@
+const ExtractTextPlugin = require('extract-text-webpack-plugin')
+const { env } = require('../configuration.js')
+
+module.exports = {
+  test: /\.(scss|sass|css)$/i,
+  use: ExtractTextPlugin.extract({
+    fallback: 'style-loader',
+    use: [
+      { loader: 'css-loader', options: { minimize: env.NODE_ENV === 'production' } },
+      'postcss-loader',
+      'sass-loader'
+    ]
+  })
+}
diff --git a/config/webpack/paths.yml b/config/webpack/paths.yml
new file mode 100644
index 000000000..26ab8facc
--- /dev/null
+++ b/config/webpack/paths.yml
@@ -0,0 +1,33 @@
+# Note: You must restart bin/webpack-dev-server for changes to take effect
+
+default: &default
+  config: config/webpack
+  entry: packs
+  output: public
+  manifest: manifest.json
+  node_modules: node_modules
+  source: app/javascript
+  extensions:
+    - .coffee
+    - .js
+    - .jsx
+    - .ts
+    - .vue
+    - .sass
+    - .scss
+    - .css
+    - .png
+    - .svg
+    - .gif
+    - .jpeg
+    - .jpg
+
+development:
+  <<: *default
+
+test:
+  <<: *default
+  manifest: manifest-test.json
+
+production:
+  <<: *default
diff --git a/config/webpack/production.js b/config/webpack/production.js
new file mode 100644
index 000000000..2e4baa424
--- /dev/null
+++ b/config/webpack/production.js
@@ -0,0 +1,44 @@
+// Note: You must restart bin/webpack-dev-server for changes to take effect
+
+/* eslint global-require: 0 */
+
+const webpack = require('webpack')
+const merge = require('webpack-merge')
+const CompressionPlugin = require('compression-webpack-plugin')
+const sharedConfig = require('./shared.js')
+
+module.exports = merge(sharedConfig, {
+  output: { filename: '[name]-[chunkhash].js' },
+
+  plugins: [
+    new webpack.optimize.UglifyJsPlugin({
+      compress: {
+        unused: true,
+        evaluate: true,
+        booleans: true,
+        drop_debugger: true,
+        dead_code: true,
+        pure_getters: true,
+        negate_iife: true,
+        conditionals: true,
+        loops: true,
+        cascade: true,
+        keep_fargs: false,
+        warnings: true
+      },
+
+      mangle: false,
+
+      output: {
+        comments: false
+      },
+
+      sourceMap: false
+    }),
+    new CompressionPlugin({
+      asset: '[path].gz[query]',
+      algorithm: 'gzip',
+      test: /\.(js|css|svg|eot|ttf|woff|woff2)$/
+    })
+  ]
+})
diff --git a/config/webpack/shared.js b/config/webpack/shared.js
new file mode 100644
index 000000000..3d1b14e15
--- /dev/null
+++ b/config/webpack/shared.js
@@ -0,0 +1,59 @@
+// Note: You must restart bin/webpack-dev-server for changes to take effect
+
+/* eslint global-require: 0 */
+/* eslint import/no-dynamic-require: 0 */
+
+const webpack = require('webpack')
+const { basename, dirname, join, relative, resolve } = require('path')
+const { sync } = require('glob')
+const ExtractTextPlugin = require('extract-text-webpack-plugin')
+const ManifestPlugin = require('webpack-manifest-plugin')
+const extname = require('path-complete-extname')
+const { env, paths, publicPath, loadersDir } = require('./configuration.js')
+
+const extensionGlob = `**/*{${paths.extensions.join(',')}}*`
+const packPaths = sync(join(paths.source, paths.entry, extensionGlob))
+
+module.exports = {
+  entry: packPaths.reduce(
+    (map, entry) => {
+      const localMap = map
+      const namespace = relative(join(paths.source, paths.entry), dirname(entry))
+      localMap[join(namespace, basename(entry, extname(entry)))] = resolve(entry)
+      return localMap
+    }, {}
+  ),
+
+  output: {
+    filename: '[name].js',
+    chunkFilename: '[name]-[chunkhash].js',
+    path: resolve(paths.output, paths.entry),
+    publicPath
+  },
+
+  module: {
+    rules: sync(join(loadersDir, '*.js')).map(loader => require(loader))
+  },
+
+  plugins: [
+    new webpack.EnvironmentPlugin(JSON.parse(JSON.stringify(env))),
+    new ExtractTextPlugin(env.NODE_ENV === 'production' ? '[name]-[hash].css' : '[name].css'),
+    new ManifestPlugin({ fileName: paths.manifest, publicPath, writeToFileEmit: true }),
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'vendor',
+      minChunks: ({ resource }) => /node_modules/.test(resource)
+    })
+  ],
+
+  resolve: {
+    extensions: paths.extensions,
+    modules: [
+      resolve(paths.source),
+      resolve(paths.node_modules)
+    ]
+  },
+
+  resolveLoader: {
+    modules: [paths.node_modules]
+  }
+}
diff --git a/config/webpack/test.js b/config/webpack/test.js
new file mode 100644
index 000000000..e002d0cdc
--- /dev/null
+++ b/config/webpack/test.js
@@ -0,0 +1,6 @@
+// Note: You must restart bin/webpack-dev-server for changes to take effect
+
+const merge = require('webpack-merge')
+const sharedConfig = require('./shared.js')
+
+module.exports = merge(sharedConfig, {})
diff --git a/config/webpack/translationRunner.js b/config/webpack/translationRunner.js
new file mode 100644
index 000000000..c636170b9
--- /dev/null
+++ b/config/webpack/translationRunner.js
@@ -0,0 +1,34 @@
+const manageTranslations = require('react-intl-translations-manager').default;
+
+manageTranslations({
+  messagesDirectory: 'build/messages',
+  translationsDirectory: 'app/javascript/mastodon/locales/',
+  detectDuplicateIds: false,
+  singleMessagesFile: true,
+  languages: [
+    'ar',
+    'en',
+    'de',
+    'es',
+    'fa',
+    'hr',
+    'hu',
+    'io',
+    'it',
+    'fr',
+    'nl',
+    'no',
+    'oc',
+    'pt',
+    'pt-BR',
+    'uk',
+    'fi',
+    'eo',
+    'ru',
+    'ja',
+    'zh-HK',
+    'zh-CN',
+    'bg',
+    'id',
+  ],
+})
diff --git a/docker-compose.yml b/docker-compose.yml
index 81c6fe981..1a8debb46 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -15,10 +15,14 @@ services:
 #    volumes:
 #      - ./redis:/data
 
-  web:
-    restart: always
+  app:
     build: .
     image: gargron/mastodon
+
+  web:
+    extends:
+      service: app
+    restart: always
     env_file: .env.production
     command: bundle exec rails s -p 3000 -b '0.0.0.0'
     ports:
@@ -28,12 +32,13 @@ services:
       - redis
     volumes:
       - ./public/assets:/mastodon/public/assets
+      - ./public/packs:/mastodon/public/packs
       - ./public/system:/mastodon/public/system
 
   streaming:
+    extends:
+      service: app
     restart: always
-    build: .
-    image: gargron/mastodon
     env_file: .env.production
     command: npm run start
     ports:
@@ -43,9 +48,9 @@ services:
       - redis
 
   sidekiq:
+    extends:
+      service: app
     restart: always
-    build: .
-    image: gargron/mastodon
     env_file: .env.production
     command: bundle exec sidekiq -q default -q mailers -q pull -q push
     depends_on:
diff --git a/package.json b/package.json
index 70304c529..a19c61ee2 100644
--- a/package.json
+++ b/package.json
@@ -3,10 +3,11 @@
   "license": "AGPL-3.0",
   "scripts": {
     "postversion": "git push --tags",
+    "manage:translations": "node ./config/webpack/translationRunner.js",
     "start": "babel-node ./streaming/index.js --presets es2015,stage-2",
     "storybook": "start-storybook -p 9001 -c storybook",
     "test": "npm run test:lint && npm run test:mocha",
-    "test:lint": "eslint -c .eslintrc.json --ext=js --ext=jsx app/assets/javascripts/",
+    "test:lint": "eslint -c .eslintrc.json --ext=js --ext=jsx app/javascript/",
     "test:mocha": "mocha --require ./spec/javascript/setup.js --compilers js:babel-register ./spec/javascript/components/*.test.jsx"
   },
   "repository": {
@@ -14,39 +15,55 @@
     "url": "https://github.com/tootsuite/mastodon.git"
   },
   "dependencies": {
-    "@kadira/storybook": "^2.35.3",
+    "autoprefixer": "^6.7.7",
     "axios": "^0.15.3",
     "babel-cli": "^6.23.0",
+    "babel-core": "^6.24.1",
+    "babel-loader": "7.x",
+    "babel-plugin-react-intl": "^2.3.1",
     "babel-plugin-react-transform": "^2.0.2",
     "babel-plugin-transform-decorators-legacy": "^1.3.4",
     "babel-plugin-transform-object-rest-spread": "^6.23.0",
+    "babel-plugin-transform-react-constant-elements": "^6.23.0",
+    "babel-plugin-transform-react-inline-elements": "^6.22.0",
+    "babel-plugin-transform-react-jsx-self": "^6.22.0",
+    "babel-plugin-transform-react-jsx-source": "^6.22.0",
+    "babel-plugin-transform-react-pure-class-to-function": "^1.0.1",
+    "babel-plugin-transform-react-remove-prop-types": "^0.4.3",
+    "babel-preset-env": "^1.4.0",
     "babel-preset-es2015": "^6.22.0",
     "babel-preset-react": "^6.11.1",
     "babel-preset-stage-2": "^6.22.0",
-    "babelify": "^7.3.0",
-    "browserify": "^14.1.0",
-    "browserify-incremental": "^3.1.1",
     "bufferutil": "^2.0.1",
-    "chai": "^3.5.0",
-    "chai-enzyme": "^0.6.1",
-    "css-loader": "^0.26.2",
+    "coffee-loader": "^0.7.3",
+    "coffee-script": "^1.12.5",
+    "compression-webpack-plugin": "^0.4.0",
+    "css-loader": "^0.28.0",
     "dotenv": "^4.0.0",
     "emojione": "^2.2.7",
     "emojione-picker": "^2.1.2",
-    "enzyme": "^2.8.2",
     "es6-promise": "^3.2.1",
     "escape-html": "^1.0.3",
     "eventsource": "^0.2.1",
     "express": "^4.14.1",
+    "extract-text-webpack-plugin": "^2.1.0",
+    "file-loader": "^0.11.1",
+    "font-awesome": "^4.7.0",
+    "glob": "^7.1.1",
     "http-link-header": "^0.8.0",
     "immutable": "^3.8.1",
     "intl": "^1.2.5",
-    "jsdom": "^9.11.0",
-    "mocha": "^3.2.0",
+    "jquery-ujs": "^1.2.2",
+    "js-yaml": "^3.8.3",
     "node-sass": "^4.5.2",
     "npmlog": "^4.0.2",
+    "path-complete-extname": "^0.1.0",
     "pg": "^6.1.2",
+    "postcss-loader": "^1.3.3",
+    "postcss-smart-import": "^0.6.12",
+    "precss": "^1.4.0",
     "prop-types": "^15.5.8",
+    "rails-erb-loader": "^5.0.0",
     "react": "^15.5.4",
     "react-addons-perf": "^15.4.2",
     "react-addons-shallow-compare": "^15.5.2",
@@ -55,6 +72,7 @@
     "react-dom": "^15.5.4",
     "react-imageloader": "^2.1.0",
     "react-immutable-proptypes": "^2.1.0",
+    "react-immutable-pure-component": "^0.0.4",
     "react-intl": "^2.1.5",
     "react-motion": "^0.4.5",
     "react-notification": "^6.6.0",
@@ -72,21 +90,31 @@
     "redux-immutable": "^3.1.0",
     "redux-thunk": "^2.2.0",
     "reselect": "^2.5.4",
-    "sass-loader": "^6.0.2",
+    "sass-loader": "^6.0.3",
     "sinon": "^1.17.6",
     "stringz": "^0.1.2",
-    "style-loader": "^0.13.2",
+    "style-loader": "^0.16.1",
     "utf-8-validate": "^3.0.1",
     "uuid": "^3.0.1",
-    "webpack": "^2.2.1",
+    "webpack": "^2.4.1",
+    "webpack-manifest-plugin": "^1.1.0",
+    "webpack-merge": "^4.1.0",
     "websocket.js": "^0.1.7",
     "ws": "^2.1.0"
   },
   "devDependencies": {
+    "@kadira/storybook": "^2.35.3",
     "babel-eslint": "^7.2.2",
+    "chai": "^3.5.0",
+    "chai-enzyme": "^0.6.1",
+    "enzyme": "^2.8.2",
     "eslint": "^3.19.0",
     "eslint-plugin-jsx-a11y": "^4.0.0",
-    "eslint-plugin-react": "^6.10.3"
+    "eslint-plugin-react": "^6.10.3",
+    "jsdom": "^9.11.0",
+    "mocha": "^3.2.0",
+    "react-intl-translations-manager": "^5.0.0",
+    "webpack-dev-server": "^2.4.5"
   },
   "optionalDependencies": {
     "fsevents": "*"
diff --git a/spec/controllers/api/v1/accounts_controller_spec.rb b/spec/controllers/api/v1/accounts_controller_spec.rb
index ed49779b4..d69d9c4c2 100644
--- a/spec/controllers/api/v1/accounts_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts_controller_spec.rb
@@ -27,14 +27,11 @@ RSpec.describe Api::V1::AccountsController, type: :controller do
   describe 'PATCH #update_credentials' do
     describe 'with valid data' do
       before do
-        avatar = File.read(Rails.root.join('app', 'assets', 'images', 'logo.png'))
-        header = File.read(Rails.root.join('app', 'assets', 'images', 'mastodon-getting-started.png'))
-
         patch :update_credentials, params: {
           display_name: "Alice Isn't Dead",
           note: "Hi!\n\nToot toot!",
-          avatar: "data:image/png;base64,#{Base64.encode64(avatar)}",
-          header: "data:image/png;base64,#{Base64.encode64(header)}",
+          avatar: fixture_file_upload('files/avatar.gif', 'image/gif'),
+          header: fixture_file_upload('files/attachment.jpg', 'image/jpeg'),
         }
       end
 
diff --git a/spec/features/log_in_spec.rb b/spec/features/log_in_spec.rb
index 4a634f6b8..f9c222b60 100644
--- a/spec/features/log_in_spec.rb
+++ b/spec/features/log_in_spec.rb
@@ -11,6 +11,6 @@ feature "Log in" do
     fill_in "user_password", with: password
     click_on "Log in"
 
-    expect(page).to have_css "div.app-holder[data-react-class=Mastodon]"
+    expect(page).to have_css "div.app-holder"
   end
 end
diff --git a/spec/javascript/components/avatar.test.jsx b/spec/javascript/components/avatar.test.jsx
index 7131bbec7..f70173880 100644
--- a/spec/javascript/components/avatar.test.jsx
+++ b/spec/javascript/components/avatar.test.jsx
@@ -1,7 +1,7 @@
 import { expect } from 'chai';
 import { render } from 'enzyme';
 
-import Avatar from '../../../app/assets/javascripts/components/components/avatar'
+import Avatar from '../../../app/javascript/mastodon/components/avatar'
 
 describe('<Avatar />', () => {
   const src = '/path/to/image.jpg';
diff --git a/spec/javascript/components/button.test.jsx b/spec/javascript/components/button.test.jsx
index 13bbe95a2..c32fae32a 100644
--- a/spec/javascript/components/button.test.jsx
+++ b/spec/javascript/components/button.test.jsx
@@ -2,7 +2,7 @@ import { expect } from 'chai';
 import { shallow } from 'enzyme';
 import sinon from 'sinon';
 
-import Button from '../../../app/assets/javascripts/components/components/button';
+import Button from '../../../app/javascript/mastodon/components/button';
 
 describe('<Button />', () => {
   it('renders a button element', () => {
diff --git a/spec/javascript/components/display_name.test.jsx b/spec/javascript/components/display_name.test.jsx
index e5a932f4b..7072e653e 100644
--- a/spec/javascript/components/display_name.test.jsx
+++ b/spec/javascript/components/display_name.test.jsx
@@ -2,7 +2,7 @@ import { expect } from 'chai';
 import { render } from 'enzyme';
 import Immutable  from 'immutable';
 
-import DisplayName from '../../../app/assets/javascripts/components/components/display_name'
+import DisplayName from '../../../app/javascript/mastodon/components/display_name'
 
 describe('<DisplayName />', () => {
   it('renders display name + account name', () => {
diff --git a/spec/javascript/components/dropdown_menu.test.jsx b/spec/javascript/components/dropdown_menu.test.jsx
index ef3b25a60..717bd51ac 100644
--- a/spec/javascript/components/dropdown_menu.test.jsx
+++ b/spec/javascript/components/dropdown_menu.test.jsx
@@ -2,7 +2,7 @@ import { expect } from 'chai';
 import { shallow, mount } from 'enzyme';
 import sinon from 'sinon';
 
-import DropdownMenu from '../../../app/assets/javascripts/components/components/dropdown_menu';
+import DropdownMenu from '../../../app/javascript/mastodon/components/dropdown_menu';
 import Dropdown, { DropdownTrigger, DropdownContent } from 'react-simple-dropdown';
 
 describe('<DropdownMenu />', () => {
diff --git a/spec/javascript/components/features/ui/components/column.test.jsx b/spec/javascript/components/features/ui/components/column.test.jsx
index b7e257757..6359905e6 100644
--- a/spec/javascript/components/features/ui/components/column.test.jsx
+++ b/spec/javascript/components/features/ui/components/column.test.jsx
@@ -2,8 +2,8 @@ import { expect } from 'chai';
 import { mount } from 'enzyme';
 import sinon from 'sinon';
 
-import Column from '../../../../../../app/assets/javascripts/components/features/ui/components/column';
-import ColumnHeader from '../../../../../../app/assets/javascripts/components/features/ui/components/column_header';
+import Column from '../../../../../../app/javascript/mastodon/features/ui/components/column';
+import ColumnHeader from '../../../../../../app/javascript/mastodon/features/ui/components/column_header';
 
 describe('<Column />', () => {
   describe('<ColumnHeader /> click handler', () => {
diff --git a/spec/javascript/components/loading_indicator.test.jsx b/spec/javascript/components/loading_indicator.test.jsx
index 677ff4aaf..a372640be 100644
--- a/spec/javascript/components/loading_indicator.test.jsx
+++ b/spec/javascript/components/loading_indicator.test.jsx
@@ -1,7 +1,7 @@
 import { expect } from 'chai';
 import { shallow } from 'enzyme';
 
-import LoadingIndicator from '../../../app/assets/javascripts/components/components/loading_indicator'
+import LoadingIndicator from '../../../app/javascript/mastodon/components/loading_indicator'
 
 describe('<LoadingIndicator />', () => {
 
diff --git a/yarn.lock b/yarn.lock
index 0e100f0bd..58e373de9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -99,20 +99,6 @@
     webpack-dev-middleware "^1.6.0"
     webpack-hot-middleware "^2.13.2"
 
-JSONStream@^0.10.0:
-  version "0.10.0"
-  resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-0.10.0.tgz#74349d0d89522b71f30f0a03ff9bd20ca6f12ac0"
-  dependencies:
-    jsonparse "0.0.5"
-    through ">=2.2.7 <3"
-
-JSONStream@^1.0.3:
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.2.1.tgz#32aa5790e799481083b49b4b7fa94e23bae69bf9"
-  dependencies:
-    jsonparse "^1.2.0"
-    through ">=2.2.7 <3"
-
 abab@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.3.tgz#b81de5f7274ec4e756d797cd834f303642724e5d"
@@ -146,14 +132,6 @@ acorn-jsx@^3.0.0:
   dependencies:
     acorn "^3.0.4"
 
-acorn@^1.0.3:
-  version "1.2.2"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-1.2.2.tgz#c8ce27de0acc76d896d2b1fad3df588d9e82f014"
-
-acorn@^2.7.0:
-  version "2.7.0"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-2.7.0.tgz#ab6e7d9d886aaca8b085bc3312b79a198433f0e7"
-
 acorn@^3.0.0, acorn@^3.0.4:
   version "3.3.0"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
@@ -162,7 +140,7 @@ acorn@^4.0.3, acorn@^4.0.4:
   version "4.0.11"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.11.tgz#edcda3bd937e7556410d42ed5860f67399c794c0"
 
-acorn@^5.0.1:
+acorn@^5.0.0, acorn@^5.0.1:
   version "5.0.3"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.0.3.tgz#c460df08491463f028ccb82eab3730bf01087b3d"
 
@@ -183,7 +161,7 @@ ajv-keywords@^1.0.0, ajv-keywords@^1.1.1:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c"
 
-ajv@^4.7.0:
+ajv@^4.11.2, ajv@^4.7.0:
   version "4.11.3"
   resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.3.tgz#ce30bdb90d1254f762c75af915fb3a63e7183d22"
   dependencies:
@@ -222,6 +200,10 @@ ansi-styles@^2.2.1:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
 
+any-promise@^0.1.0, any-promise@~0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-0.1.0.tgz#830b680aa7e56f33451d4b049f3bd8044498ee27"
+
 anymatch@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507"
@@ -270,10 +252,6 @@ array-equal@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
 
-array-filter@~0.0.0:
-  version "0.0.1"
-  resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec"
-
 array-find-index@^1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
@@ -289,14 +267,6 @@ array-includes@^3.0.2:
     define-properties "^1.1.2"
     es-abstract "^1.5.0"
 
-array-map@~0.0.0:
-  version "0.0.0"
-  resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662"
-
-array-reduce@~0.0.0:
-  version "0.0.0"
-  resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b"
-
 array-union@^1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
@@ -346,7 +316,7 @@ assert-plus@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
 
-assert@^1.1.1, assert@^1.4.0:
+assert@^1.1.1:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91"
   dependencies:
@@ -364,12 +334,6 @@ ast-types@0.9.5:
   version "0.9.5"
   resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.5.tgz#1a660a09945dbceb1f9c9cbb715002617424e04a"
 
-astw@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/astw/-/astw-2.0.0.tgz#08121ac8288d35611c0ceec663f6cd545604897d"
-  dependencies:
-    acorn "^1.0.3"
-
 async-each@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
@@ -378,6 +342,10 @@ async-foreach@^0.1.3:
   version "0.1.3"
   resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542"
 
+async@0.2.x, async@~0.2.6:
+  version "0.2.10"
+  resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1"
+
 async@^0.9.0:
   version "0.9.2"
   resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d"
@@ -392,10 +360,6 @@ async@^2.1.2, async@^2.1.5:
   dependencies:
     lodash "^4.14.0"
 
-async@~0.2.6:
-  version "0.2.10"
-  resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1"
-
 asynckit@^0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@@ -404,15 +368,15 @@ autobind-decorator@1.3.3:
   version "1.3.3"
   resolved "https://registry.yarnpkg.com/autobind-decorator/-/autobind-decorator-1.3.3.tgz#41b1915ee742859c872b5d1743d10745254b83b4"
 
-autoprefixer@^6.3.1, autoprefixer@^6.3.7:
-  version "6.5.0"
-  resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.5.0.tgz#910de0aa0f22af4c7d50367cbc9d4d412945162f"
+autoprefixer@^6.3.1, autoprefixer@^6.3.7, autoprefixer@^6.7.7:
+  version "6.7.7"
+  resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014"
   dependencies:
-    browserslist "~1.4.0"
-    caniuse-db "^1.0.30000540"
+    browserslist "^1.7.6"
+    caniuse-db "^1.0.30000634"
     normalize-range "^0.1.2"
     num2fraction "^1.2.2"
-    postcss "^5.2.2"
+    postcss "^5.2.16"
     postcss-value-parser "^3.2.3"
 
 aws-sign2@~0.6.0:
@@ -450,15 +414,7 @@ babel-cli@^6.23.0:
   optionalDependencies:
     chokidar "^1.6.1"
 
-babel-code-frame@^6.11.0:
-  version "6.16.0"
-  resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.16.0.tgz#f90e60da0862909d3ce098733b5d3987c97cb8de"
-  dependencies:
-    chalk "^1.1.0"
-    esutils "^2.0.2"
-    js-tokens "^2.0.0"
-
-babel-code-frame@^6.16.0, babel-code-frame@^6.22.0:
+babel-code-frame@^6.11.0, babel-code-frame@^6.16.0, babel-code-frame@^6.22.0:
   version "6.22.0"
   resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4"
   dependencies:
@@ -466,43 +422,19 @@ babel-code-frame@^6.16.0, babel-code-frame@^6.22.0:
     esutils "^2.0.2"
     js-tokens "^3.0.0"
 
-babel-core@^6.0.14, babel-core@^6.23.0:
-  version "6.23.1"
-  resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.23.1.tgz#c143cb621bb2f621710c220c5d579d15b8a442df"
+babel-core@^6.11.4, babel-core@^6.23.0, babel-core@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.24.1.tgz#8c428564dce1e1f41fb337ec34f4c3b022b5ad83"
   dependencies:
     babel-code-frame "^6.22.0"
-    babel-generator "^6.23.0"
-    babel-helpers "^6.23.0"
+    babel-generator "^6.24.1"
+    babel-helpers "^6.24.1"
     babel-messages "^6.23.0"
-    babel-register "^6.23.0"
-    babel-runtime "^6.22.0"
-    babel-template "^6.23.0"
-    babel-traverse "^6.23.1"
-    babel-types "^6.23.0"
-    babylon "^6.11.0"
-    convert-source-map "^1.1.0"
-    debug "^2.1.1"
-    json5 "^0.5.0"
-    lodash "^4.2.0"
-    minimatch "^3.0.2"
-    path-is-absolute "^1.0.0"
-    private "^0.1.6"
-    slash "^1.0.0"
-    source-map "^0.5.0"
-
-babel-core@^6.11.4:
-  version "6.22.1"
-  resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.22.1.tgz#9c5fd658ba1772d28d721f6d25d968fc7ae21648"
-  dependencies:
-    babel-code-frame "^6.22.0"
-    babel-generator "^6.22.0"
-    babel-helpers "^6.22.0"
-    babel-messages "^6.22.0"
-    babel-register "^6.22.0"
+    babel-register "^6.24.1"
     babel-runtime "^6.22.0"
-    babel-template "^6.22.0"
-    babel-traverse "^6.22.1"
-    babel-types "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
     babylon "^6.11.0"
     convert-source-map "^1.1.0"
     debug "^2.1.1"
@@ -523,25 +455,13 @@ babel-eslint@^7.2.2:
     babel-types "^6.23.0"
     babylon "^6.16.1"
 
-babel-generator@^6.22.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.22.0.tgz#d642bf4961911a8adc7c692b0c9297f325cda805"
-  dependencies:
-    babel-messages "^6.22.0"
-    babel-runtime "^6.22.0"
-    babel-types "^6.22.0"
-    detect-indent "^4.0.0"
-    jsesc "^1.3.0"
-    lodash "^4.2.0"
-    source-map "^0.5.0"
-
-babel-generator@^6.23.0:
-  version "6.23.0"
-  resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.23.0.tgz#6b8edab956ef3116f79d8c84c5a3c05f32a74bc5"
+babel-generator@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.24.1.tgz#e715f486c58ded25649d888944d52aa07c5d9497"
   dependencies:
     babel-messages "^6.23.0"
     babel-runtime "^6.22.0"
-    babel-types "^6.23.0"
+    babel-types "^6.24.1"
     detect-indent "^4.0.0"
     jsesc "^1.3.0"
     lodash "^4.2.0"
@@ -573,22 +493,22 @@ babel-helper-builder-react-jsx@^6.8.0:
     esutils "^2.0.0"
     lodash "^4.2.0"
 
-babel-helper-call-delegate@^6.22.0, babel-helper-call-delegate@^6.8.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.22.0.tgz#119921b56120f17e9dae3f74b4f5cc7bcc1b37ef"
+babel-helper-call-delegate@^6.24.1, babel-helper-call-delegate@^6.8.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d"
   dependencies:
-    babel-helper-hoist-variables "^6.22.0"
+    babel-helper-hoist-variables "^6.24.1"
     babel-runtime "^6.22.0"
-    babel-traverse "^6.22.0"
-    babel-types "^6.22.0"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
 
-babel-helper-define-map@^6.22.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.22.0.tgz#9544e9502b2d6dfe7d00ff60e82bd5a7a89e95b7"
+babel-helper-define-map@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.24.1.tgz#7a9747f258d8947d32d515f6aa1c7bd02204a080"
   dependencies:
-    babel-helper-function-name "^6.22.0"
+    babel-helper-function-name "^6.24.1"
     babel-runtime "^6.22.0"
-    babel-types "^6.22.0"
+    babel-types "^6.24.1"
     lodash "^4.2.0"
 
 babel-helper-explode-assignable-expression@^6.22.0:
@@ -608,36 +528,40 @@ babel-helper-explode-class@^6.22.0:
     babel-traverse "^6.22.0"
     babel-types "^6.22.0"
 
-babel-helper-function-name@^6.22.0, babel-helper-function-name@^6.8.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.22.0.tgz#51f1bdc4bb89b15f57a9b249f33d742816dcbefc"
+babel-helper-function-name@^6.22.0, babel-helper-function-name@^6.24.1, babel-helper-function-name@^6.8.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9"
   dependencies:
-    babel-helper-get-function-arity "^6.22.0"
+    babel-helper-get-function-arity "^6.24.1"
     babel-runtime "^6.22.0"
-    babel-template "^6.22.0"
-    babel-traverse "^6.22.0"
-    babel-types "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
 
-babel-helper-get-function-arity@^6.22.0, babel-helper-get-function-arity@^6.8.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.22.0.tgz#0beb464ad69dc7347410ac6ade9f03a50634f5ce"
+babel-helper-get-function-arity@^6.24.1, babel-helper-get-function-arity@^6.8.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d"
   dependencies:
     babel-runtime "^6.22.0"
-    babel-types "^6.22.0"
+    babel-types "^6.24.1"
 
-babel-helper-hoist-variables@^6.22.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.22.0.tgz#3eacbf731d80705845dd2e9718f600cfb9b4ba72"
+babel-helper-hoist-variables@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76"
   dependencies:
     babel-runtime "^6.22.0"
-    babel-types "^6.22.0"
+    babel-types "^6.24.1"
 
-babel-helper-optimise-call-expression@^6.22.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.22.0.tgz#f8d5d4b40a6e2605a6a7f9d537b581bea3756d15"
+babel-helper-is-react-class@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/babel-helper-is-react-class/-/babel-helper-is-react-class-1.0.0.tgz#ef6f3678b05c76dbdeedadead7af98c2724d8431"
+
+babel-helper-optimise-call-expression@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257"
   dependencies:
     babel-runtime "^6.22.0"
-    babel-types "^6.22.0"
+    babel-types "^6.24.1"
 
 babel-helper-regex@^6.22.0:
   version "6.22.0"
@@ -657,30 +581,31 @@ babel-helper-remap-async-to-generator@^6.22.0:
     babel-traverse "^6.22.0"
     babel-types "^6.22.0"
 
-babel-helper-replace-supers@^6.22.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.22.0.tgz#1fcee2270657548908c34db16bcc345f9850cf42"
+babel-helper-replace-supers@^6.22.0, babel-helper-replace-supers@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a"
   dependencies:
-    babel-helper-optimise-call-expression "^6.22.0"
-    babel-messages "^6.22.0"
+    babel-helper-optimise-call-expression "^6.24.1"
+    babel-messages "^6.23.0"
     babel-runtime "^6.22.0"
-    babel-template "^6.22.0"
-    babel-traverse "^6.22.0"
-    babel-types "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
 
-babel-helpers@^6.22.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.22.0.tgz#d275f55f2252b8101bff07bc0c556deda657392c"
+babel-helpers@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2"
   dependencies:
     babel-runtime "^6.22.0"
-    babel-template "^6.22.0"
+    babel-template "^6.24.1"
 
-babel-helpers@^6.23.0:
-  version "6.23.0"
-  resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.23.0.tgz#4f8f2e092d0b6a8808a4bde79c27f1e2ecf0d992"
+babel-loader@7.x:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.0.0.tgz#2e43a66bee1fff4470533d0402c8a4532fafbaf7"
   dependencies:
-    babel-runtime "^6.22.0"
-    babel-template "^6.23.0"
+    find-cache-dir "^0.1.1"
+    loader-utils "^1.0.2"
+    mkdirp "^0.5.1"
 
 babel-loader@^6.2.4:
   version "6.2.5"
@@ -690,12 +615,6 @@ babel-loader@^6.2.4:
     mkdirp "^0.5.1"
     object-assign "^4.0.1"
 
-babel-messages@^6.22.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.22.0.tgz#36066a214f1217e4ed4164867669ecb39e3ea575"
-  dependencies:
-    babel-runtime "^6.22.0"
-
 babel-messages@^6.23.0:
   version "6.23.0"
   resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e"
@@ -716,6 +635,14 @@ babel-plugin-react-docgen@^1.4.2:
     lodash "4.x.x"
     react-docgen "^2.12.1"
 
+babel-plugin-react-intl@^2.3.1:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-react-intl/-/babel-plugin-react-intl-2.3.1.tgz#3d43912e824da005e08e8e8239d5ba784374bb00"
+  dependencies:
+    babel-runtime "^6.2.0"
+    intl-messageformat-parser "^1.2.0"
+    mkdirp "^0.5.1"
+
 babel-plugin-react-transform@^2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/babel-plugin-react-transform/-/babel-plugin-react-transform-2.0.2.tgz#515bbfa996893981142d90b1f9b1635de2995109"
@@ -825,29 +752,29 @@ babel-plugin-transform-es2015-block-scoped-functions@^6.22.0, babel-plugin-trans
   dependencies:
     babel-runtime "^6.22.0"
 
-babel-plugin-transform-es2015-block-scoping@^6.22.0, babel-plugin-transform-es2015-block-scoping@^6.6.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.22.0.tgz#00d6e3a0bebdcfe7536b9d653b44a9141e63e47e"
+babel-plugin-transform-es2015-block-scoping@^6.22.0, babel-plugin-transform-es2015-block-scoping@^6.23.0, babel-plugin-transform-es2015-block-scoping@^6.6.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.24.1.tgz#76c295dc3a4741b1665adfd3167215dcff32a576"
   dependencies:
     babel-runtime "^6.22.0"
-    babel-template "^6.22.0"
-    babel-traverse "^6.22.0"
-    babel-types "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
     lodash "^4.2.0"
 
-babel-plugin-transform-es2015-classes@^6.22.0, babel-plugin-transform-es2015-classes@^6.6.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.22.0.tgz#54d44998fd823d9dca15292324161c331c1b6f14"
+babel-plugin-transform-es2015-classes@^6.22.0, babel-plugin-transform-es2015-classes@^6.23.0, babel-plugin-transform-es2015-classes@^6.6.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db"
   dependencies:
-    babel-helper-define-map "^6.22.0"
-    babel-helper-function-name "^6.22.0"
-    babel-helper-optimise-call-expression "^6.22.0"
-    babel-helper-replace-supers "^6.22.0"
-    babel-messages "^6.22.0"
+    babel-helper-define-map "^6.24.1"
+    babel-helper-function-name "^6.24.1"
+    babel-helper-optimise-call-expression "^6.24.1"
+    babel-helper-replace-supers "^6.24.1"
+    babel-messages "^6.23.0"
     babel-runtime "^6.22.0"
-    babel-template "^6.22.0"
-    babel-traverse "^6.22.0"
-    babel-types "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
 
 babel-plugin-transform-es2015-computed-properties@^6.22.0, babel-plugin-transform-es2015-computed-properties@^6.3.13:
   version "6.22.0"
@@ -862,9 +789,9 @@ babel-plugin-transform-es2015-destructuring@6.16.0:
   dependencies:
     babel-runtime "^6.9.0"
 
-babel-plugin-transform-es2015-destructuring@^6.22.0, babel-plugin-transform-es2015-destructuring@^6.6.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.22.0.tgz#8e0af2f885a0b2cf999d47c4c1dd23ce88cfa4c6"
+babel-plugin-transform-es2015-destructuring@^6.22.0, babel-plugin-transform-es2015-destructuring@^6.23.0, babel-plugin-transform-es2015-destructuring@^6.6.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d"
   dependencies:
     babel-runtime "^6.22.0"
 
@@ -875,9 +802,9 @@ babel-plugin-transform-es2015-duplicate-keys@^6.22.0, babel-plugin-transform-es2
     babel-runtime "^6.22.0"
     babel-types "^6.22.0"
 
-babel-plugin-transform-es2015-for-of@^6.22.0, babel-plugin-transform-es2015-for-of@^6.6.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.22.0.tgz#180467ad63aeea592a1caeee4bf1c8b3e2616265"
+babel-plugin-transform-es2015-for-of@^6.22.0, babel-plugin-transform-es2015-for-of@^6.23.0, babel-plugin-transform-es2015-for-of@^6.6.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691"
   dependencies:
     babel-runtime "^6.22.0"
 
@@ -895,7 +822,7 @@ babel-plugin-transform-es2015-literals@^6.22.0, babel-plugin-transform-es2015-li
   dependencies:
     babel-runtime "^6.22.0"
 
-babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015-modules-amd@^6.8.0:
+babel-plugin-transform-es2015-modules-amd@^6.22.0:
   version "6.22.0"
   resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.22.0.tgz#bf69cd34889a41c33d90dfb740e0091ccff52f21"
   dependencies:
@@ -903,30 +830,38 @@ babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015
     babel-runtime "^6.22.0"
     babel-template "^6.22.0"
 
-babel-plugin-transform-es2015-modules-commonjs@^6.22.0, babel-plugin-transform-es2015-modules-commonjs@^6.6.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.22.0.tgz#6ca04e22b8e214fb50169730657e7a07dc941145"
+babel-plugin-transform-es2015-modules-amd@^6.24.1, babel-plugin-transform-es2015-modules-amd@^6.8.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154"
   dependencies:
-    babel-plugin-transform-strict-mode "^6.22.0"
+    babel-plugin-transform-es2015-modules-commonjs "^6.24.1"
     babel-runtime "^6.22.0"
-    babel-template "^6.22.0"
-    babel-types "^6.22.0"
+    babel-template "^6.24.1"
 
-babel-plugin-transform-es2015-modules-systemjs@^6.12.0, babel-plugin-transform-es2015-modules-systemjs@^6.22.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.22.0.tgz#810cd0cd025a08383b84236b92c6e31f88e644ad"
+babel-plugin-transform-es2015-modules-commonjs@^6.22.0, babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1, babel-plugin-transform-es2015-modules-commonjs@^6.6.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.1.tgz#d3e310b40ef664a36622200097c6d440298f2bfe"
   dependencies:
-    babel-helper-hoist-variables "^6.22.0"
+    babel-plugin-transform-strict-mode "^6.24.1"
     babel-runtime "^6.22.0"
-    babel-template "^6.22.0"
+    babel-template "^6.24.1"
+    babel-types "^6.24.1"
 
-babel-plugin-transform-es2015-modules-umd@^6.12.0, babel-plugin-transform-es2015-modules-umd@^6.22.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.22.0.tgz#60d0ba3bd23258719c64391d9bf492d648dc0fae"
+babel-plugin-transform-es2015-modules-systemjs@^6.12.0, babel-plugin-transform-es2015-modules-systemjs@^6.22.0, babel-plugin-transform-es2015-modules-systemjs@^6.23.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23"
   dependencies:
-    babel-plugin-transform-es2015-modules-amd "^6.22.0"
+    babel-helper-hoist-variables "^6.24.1"
     babel-runtime "^6.22.0"
-    babel-template "^6.22.0"
+    babel-template "^6.24.1"
+
+babel-plugin-transform-es2015-modules-umd@^6.12.0, babel-plugin-transform-es2015-modules-umd@^6.22.0, babel-plugin-transform-es2015-modules-umd@^6.23.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468"
+  dependencies:
+    babel-plugin-transform-es2015-modules-amd "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
 
 babel-plugin-transform-es2015-object-super@^6.22.0, babel-plugin-transform-es2015-object-super@^6.3.13:
   version "6.22.0"
@@ -946,16 +881,16 @@ babel-plugin-transform-es2015-parameters@6.17.0:
     babel-traverse "^6.16.0"
     babel-types "^6.16.0"
 
-babel-plugin-transform-es2015-parameters@^6.22.0, babel-plugin-transform-es2015-parameters@^6.6.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.22.0.tgz#57076069232019094f27da8c68bb7162fe208dbb"
+babel-plugin-transform-es2015-parameters@^6.22.0, babel-plugin-transform-es2015-parameters@^6.23.0, babel-plugin-transform-es2015-parameters@^6.6.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b"
   dependencies:
-    babel-helper-call-delegate "^6.22.0"
-    babel-helper-get-function-arity "^6.22.0"
+    babel-helper-call-delegate "^6.24.1"
+    babel-helper-get-function-arity "^6.24.1"
     babel-runtime "^6.22.0"
-    babel-template "^6.22.0"
-    babel-traverse "^6.22.0"
-    babel-types "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
 
 babel-plugin-transform-es2015-shorthand-properties@^6.22.0, babel-plugin-transform-es2015-shorthand-properties@^6.3.13:
   version "6.22.0"
@@ -984,9 +919,9 @@ babel-plugin-transform-es2015-template-literals@^6.22.0, babel-plugin-transform-
   dependencies:
     babel-runtime "^6.22.0"
 
-babel-plugin-transform-es2015-typeof-symbol@^6.22.0, babel-plugin-transform-es2015-typeof-symbol@^6.6.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.22.0.tgz#87faf2336d3b6a97f68c4d906b0cd0edeae676e1"
+babel-plugin-transform-es2015-typeof-symbol@^6.22.0, babel-plugin-transform-es2015-typeof-symbol@^6.23.0, babel-plugin-transform-es2015-typeof-symbol@^6.6.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372"
   dependencies:
     babel-runtime "^6.22.0"
 
@@ -1033,26 +968,52 @@ babel-plugin-transform-react-constant-elements@6.9.1:
   dependencies:
     babel-runtime "^6.9.1"
 
+babel-plugin-transform-react-constant-elements@^6.23.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-constant-elements/-/babel-plugin-transform-react-constant-elements-6.23.0.tgz#2f119bf4d2cdd45eb9baaae574053c604f6147dd"
+  dependencies:
+    babel-runtime "^6.22.0"
+
 babel-plugin-transform-react-display-name@^6.3.13:
   version "6.8.0"
   resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.8.0.tgz#f7a084977383d728bdbdc2835bba0159577f660e"
   dependencies:
     babel-runtime "^6.0.0"
 
-babel-plugin-transform-react-jsx-self@6.11.0, babel-plugin-transform-react-jsx-self@^6.11.0:
+babel-plugin-transform-react-inline-elements@^6.22.0:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-inline-elements/-/babel-plugin-transform-react-inline-elements-6.22.0.tgz#6687211a32b49a52f22c573a2b5504a25ef17c53"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-react-jsx-self@6.11.0:
   version "6.11.0"
   resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.11.0.tgz#605c9450c1429f97a930f7e1dfe3f0d9d0dbd0f4"
   dependencies:
     babel-plugin-syntax-jsx "^6.8.0"
     babel-runtime "^6.9.0"
 
-babel-plugin-transform-react-jsx-source@6.9.0, babel-plugin-transform-react-jsx-source@^6.3.13:
+babel-plugin-transform-react-jsx-self@^6.11.0, babel-plugin-transform-react-jsx-self@^6.22.0:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz#df6d80a9da2612a121e6ddd7558bcbecf06e636e"
+  dependencies:
+    babel-plugin-syntax-jsx "^6.8.0"
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-react-jsx-source@6.9.0:
   version "6.9.0"
   resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.9.0.tgz#af684a05c2067a86e0957d4f343295ccf5dccf00"
   dependencies:
     babel-plugin-syntax-jsx "^6.8.0"
     babel-runtime "^6.9.0"
 
+babel-plugin-transform-react-jsx-source@^6.22.0, babel-plugin-transform-react-jsx-source@^6.3.13:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz#66ac12153f5cd2d17b3c19268f4bf0197f44ecd6"
+  dependencies:
+    babel-plugin-syntax-jsx "^6.8.0"
+    babel-runtime "^6.22.0"
+
 babel-plugin-transform-react-jsx@^6.3.13:
   version "6.8.0"
   resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.8.0.tgz#94759942f70af18c617189aa7f3593f1644a71ab"
@@ -1061,6 +1022,16 @@ babel-plugin-transform-react-jsx@^6.3.13:
     babel-plugin-syntax-jsx "^6.8.0"
     babel-runtime "^6.0.0"
 
+babel-plugin-transform-react-pure-class-to-function@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-pure-class-to-function/-/babel-plugin-transform-react-pure-class-to-function-1.0.1.tgz#32a649c97d653250b419cfd1489331b0290d9ee4"
+  dependencies:
+    babel-helper-is-react-class "^1.0.0"
+
+babel-plugin-transform-react-remove-prop-types@^0.4.3:
+  version "0.4.3"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.3.tgz#fdff5c12933efc3ac979817adcdc0f612c5e3563"
+
 babel-plugin-transform-regenerator@6.16.1:
   version "6.16.1"
   resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.16.1.tgz#a75de6b048a14154aae14b0122756c5bed392f59"
@@ -1081,12 +1052,12 @@ babel-plugin-transform-runtime@6.15.0:
   dependencies:
     babel-runtime "^6.9.0"
 
-babel-plugin-transform-strict-mode@^6.22.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.22.0.tgz#e008df01340fdc87e959da65991b7e05970c8c7c"
+babel-plugin-transform-strict-mode@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758"
   dependencies:
     babel-runtime "^6.22.0"
-    babel-types "^6.22.0"
+    babel-types "^6.24.1"
 
 babel-polyfill@^6.23.0:
   version "6.23.0"
@@ -1129,6 +1100,40 @@ babel-preset-env@0.0.6:
     babel-plugin-transform-regenerator "^6.6.0"
     browserslist "^1.4.0"
 
+babel-preset-env@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.4.0.tgz#c8e02a3bcc7792f23cded68e0355b9d4c28f0f7a"
+  dependencies:
+    babel-plugin-check-es2015-constants "^6.22.0"
+    babel-plugin-syntax-trailing-function-commas "^6.22.0"
+    babel-plugin-transform-async-to-generator "^6.22.0"
+    babel-plugin-transform-es2015-arrow-functions "^6.22.0"
+    babel-plugin-transform-es2015-block-scoped-functions "^6.22.0"
+    babel-plugin-transform-es2015-block-scoping "^6.23.0"
+    babel-plugin-transform-es2015-classes "^6.23.0"
+    babel-plugin-transform-es2015-computed-properties "^6.22.0"
+    babel-plugin-transform-es2015-destructuring "^6.23.0"
+    babel-plugin-transform-es2015-duplicate-keys "^6.22.0"
+    babel-plugin-transform-es2015-for-of "^6.23.0"
+    babel-plugin-transform-es2015-function-name "^6.22.0"
+    babel-plugin-transform-es2015-literals "^6.22.0"
+    babel-plugin-transform-es2015-modules-amd "^6.22.0"
+    babel-plugin-transform-es2015-modules-commonjs "^6.23.0"
+    babel-plugin-transform-es2015-modules-systemjs "^6.23.0"
+    babel-plugin-transform-es2015-modules-umd "^6.23.0"
+    babel-plugin-transform-es2015-object-super "^6.22.0"
+    babel-plugin-transform-es2015-parameters "^6.23.0"
+    babel-plugin-transform-es2015-shorthand-properties "^6.22.0"
+    babel-plugin-transform-es2015-spread "^6.22.0"
+    babel-plugin-transform-es2015-sticky-regex "^6.22.0"
+    babel-plugin-transform-es2015-template-literals "^6.22.0"
+    babel-plugin-transform-es2015-typeof-symbol "^6.23.0"
+    babel-plugin-transform-es2015-unicode-regex "^6.22.0"
+    babel-plugin-transform-exponentiation-operator "^6.22.0"
+    babel-plugin-transform-regenerator "^6.22.0"
+    browserslist "^1.4.0"
+    invariant "^2.2.2"
+
 babel-preset-es2015@^6.16.0, babel-preset-es2015@^6.22.0:
   version "6.22.0"
   resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.22.0.tgz#af5a98ecb35eb8af764ad8a5a05eb36dc4386835"
@@ -1228,7 +1233,7 @@ babel-preset-stage-3@^6.22.0:
     babel-plugin-transform-exponentiation-operator "^6.22.0"
     babel-plugin-transform-object-rest-spread "^6.22.0"
 
-babel-register@^6.22.0, babel-register@^6.23.0:
+babel-register@^6.23.0:
   version "6.23.0"
   resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.23.0.tgz#c9aa3d4cca94b51da34826c4a0f9e08145d74ff3"
   dependencies:
@@ -1240,6 +1245,18 @@ babel-register@^6.22.0, babel-register@^6.23.0:
     mkdirp "^0.5.1"
     source-map-support "^0.4.2"
 
+babel-register@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.24.1.tgz#7e10e13a2f71065bdfad5a1787ba45bca6ded75f"
+  dependencies:
+    babel-core "^6.24.1"
+    babel-runtime "^6.22.0"
+    core-js "^2.4.0"
+    home-or-tmp "^2.0.0"
+    lodash "^4.2.0"
+    mkdirp "^0.5.1"
+    source-map-support "^0.4.2"
+
 babel-runtime@6.11.6:
   version "6.11.6"
   resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.11.6.tgz#6db707fef2d49c49bfa3cb64efdb436b518b8222"
@@ -1247,53 +1264,53 @@ babel-runtime@6.11.6:
     core-js "^2.4.0"
     regenerator-runtime "^0.9.5"
 
-babel-runtime@6.x.x, babel-runtime@^6.0.0, babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.2.0, babel-runtime@^6.22.0, babel-runtime@^6.5.0, babel-runtime@^6.9.0, babel-runtime@^6.9.1, babel-runtime@^6.9.2:
+babel-runtime@6.x.x, babel-runtime@^6.0.0, babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.23.0, babel-runtime@^6.5.0, babel-runtime@^6.9.0, babel-runtime@^6.9.1, babel-runtime@^6.9.2:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b"
+  dependencies:
+    core-js "^2.4.0"
+    regenerator-runtime "^0.10.0"
+
+babel-runtime@^6.2.0, babel-runtime@^6.22.0:
   version "6.22.0"
   resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.22.0.tgz#1cf8b4ac67c77a4ddb0db2ae1f74de52ac4ca611"
   dependencies:
     core-js "^2.4.0"
     regenerator-runtime "^0.10.0"
 
-babel-template@^6.16.0, babel-template@^6.22.0, babel-template@^6.23.0, babel-template@^6.3.0:
-  version "6.23.0"
-  resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.23.0.tgz#04d4f270adbb3aa704a8143ae26faa529238e638"
+babel-template@^6.16.0, babel-template@^6.22.0, babel-template@^6.24.1, babel-template@^6.3.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.24.1.tgz#04ae514f1f93b3a2537f2a0f60a5a45fb8308333"
   dependencies:
     babel-runtime "^6.22.0"
-    babel-traverse "^6.23.0"
-    babel-types "^6.23.0"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
     babylon "^6.11.0"
     lodash "^4.2.0"
 
-babel-traverse@^6.16.0, babel-traverse@^6.22.0, babel-traverse@^6.22.1, babel-traverse@^6.23.0, babel-traverse@^6.23.1:
-  version "6.23.1"
-  resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.23.1.tgz#d3cb59010ecd06a97d81310065f966b699e14f48"
+babel-traverse@^6.16.0, babel-traverse@^6.22.0, babel-traverse@^6.23.1, babel-traverse@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.24.1.tgz#ab36673fd356f9a0948659e7b338d5feadb31695"
   dependencies:
     babel-code-frame "^6.22.0"
     babel-messages "^6.23.0"
     babel-runtime "^6.22.0"
-    babel-types "^6.23.0"
+    babel-types "^6.24.1"
     babylon "^6.15.0"
     debug "^2.2.0"
     globals "^9.0.0"
     invariant "^2.2.0"
     lodash "^4.2.0"
 
-babel-types@^6.16.0, babel-types@^6.19.0, babel-types@^6.22.0, babel-types@^6.23.0, babel-types@^6.9.0:
-  version "6.23.0"
-  resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.23.0.tgz#bb17179d7538bad38cd0c9e115d340f77e7e9acf"
+babel-types@^6.16.0, babel-types@^6.19.0, babel-types@^6.22.0, babel-types@^6.23.0, babel-types@^6.24.1, babel-types@^6.9.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.24.1.tgz#a136879dc15b3606bda0d90c1fc74304c2ff0975"
   dependencies:
     babel-runtime "^6.22.0"
     esutils "^2.0.2"
     lodash "^4.2.0"
     to-fast-properties "^1.0.1"
 
-babelify@^7.3.0:
-  version "7.3.0"
-  resolved "https://registry.yarnpkg.com/babelify/-/babelify-7.3.0.tgz#aa56aede7067fd7bd549666ee16dc285087e88e5"
-  dependencies:
-    babel-core "^6.0.14"
-    object-assign "^4.0.0"
-
 babylon@^6.11.0, babylon@^6.15.0, babylon@^6.16.1:
   version "6.16.1"
   resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.16.1.tgz#30c5a22f481978a9e7f8cdfdf496b11d94b404d3"
@@ -1308,18 +1325,26 @@ backoff@^2.4.1:
   dependencies:
     precond "0.2"
 
+balanced-match@0.1.0, balanced-match@~0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.1.0.tgz#b504bd05869b39259dd0c5efc35d843176dccc4a"
+
+balanced-match@^0.2.0:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.2.1.tgz#7bc658b4bed61eee424ad74f75f5c3e2c4df3cc7"
+
 balanced-match@^0.4.1, balanced-match@^0.4.2:
   version "0.4.2"
   resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
 
-balanced-match@~0.1.0:
-  version "0.1.0"
-  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.1.0.tgz#b504bd05869b39259dd0c5efc35d843176dccc4a"
-
 base64-js@^1.0.2:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1"
 
+batch@0.5.3:
+  version "0.5.3"
+  resolved "https://registry.yarnpkg.com/batch/-/batch-0.5.3.tgz#3f3414f380321743bfc1042f9a83ff1d5824d464"
+
 bcrypt-pbkdf@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.0.tgz#3ca76b85241c7170bf7d9703e7b9aa74630040d4"
@@ -1338,7 +1363,7 @@ bindings@~1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.2.1.tgz#14ad6113812d2d37d72e67b4cacb4bb726505f11"
 
-bl@^1.0.0, bl@~1.1.2:
+bl@^1.0.0:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/bl/-/bl-1.1.2.tgz#fdca871a99713aa00d19e3bbba41c44787a65398"
   dependencies:
@@ -1383,22 +1408,6 @@ brorand@^1.0.1:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.0.6.tgz#4028706b915f91f7b349a2e0bf3c376039d216e5"
 
-browser-pack@^6.0.1:
-  version "6.0.1"
-  resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.0.1.tgz#779887c792eaa1f64a46a22c8f1051cdcd96755f"
-  dependencies:
-    JSONStream "^1.0.3"
-    combine-source-map "~0.7.1"
-    defined "^1.0.0"
-    through2 "^2.0.0"
-    umd "^3.0.0"
-
-browser-resolve@^1.11.0, browser-resolve@^1.7.0:
-  version "1.11.2"
-  resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.2.tgz#8ff09b0a2c421718a1051c260b32e48f442938ce"
-  dependencies:
-    resolve "1.1.7"
-
 browser-stdout@1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f"
@@ -1419,14 +1428,6 @@ browserify-aes@^1.0.0, browserify-aes@^1.0.4:
     evp_bytestokey "^1.0.0"
     inherits "^2.0.1"
 
-browserify-cache-api@^3.0.0:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/browserify-cache-api/-/browserify-cache-api-3.0.1.tgz#96247e853f068fd6e0d45cc73f0bb2cd9778ef02"
-  dependencies:
-    async "^1.5.2"
-    through2 "^2.0.0"
-    xtend "^4.0.0"
-
 browserify-cipher@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.0.tgz#9988244874bf5ed4e28da95666dcd66ac8fc363a"
@@ -1443,15 +1444,6 @@ browserify-des@^1.0.0:
     des.js "^1.0.0"
     inherits "^2.0.1"
 
-browserify-incremental@^3.1.1:
-  version "3.1.1"
-  resolved "https://registry.yarnpkg.com/browserify-incremental/-/browserify-incremental-3.1.1.tgz#0713cb7587247a632a9f08cf1bd169b878b62a8a"
-  dependencies:
-    JSONStream "^0.10.0"
-    browserify-cache-api "^3.0.0"
-    through2 "^2.0.0"
-    xtend "^4.0.0"
-
 browserify-rsa@^4.0.0:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524"
@@ -1471,69 +1463,18 @@ browserify-sign@^4.0.0:
     inherits "^2.0.1"
     parse-asn1 "^5.0.0"
 
-browserify-zlib@^0.1.4, browserify-zlib@~0.1.2:
+browserify-zlib@^0.1.4:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d"
   dependencies:
     pako "~0.2.0"
 
-browserify@^14.1.0:
-  version "14.1.0"
-  resolved "https://registry.yarnpkg.com/browserify/-/browserify-14.1.0.tgz#0508cc1e7bf4c152312c2fa523e676c0b0b92311"
-  dependencies:
-    JSONStream "^1.0.3"
-    assert "^1.4.0"
-    browser-pack "^6.0.1"
-    browser-resolve "^1.11.0"
-    browserify-zlib "~0.1.2"
-    buffer "^5.0.2"
-    cached-path-relative "^1.0.0"
-    concat-stream "~1.5.1"
-    console-browserify "^1.1.0"
-    constants-browserify "~1.0.0"
-    crypto-browserify "^3.0.0"
-    defined "^1.0.0"
-    deps-sort "^2.0.0"
-    domain-browser "~1.1.0"
-    duplexer2 "~0.1.2"
-    events "~1.1.0"
-    glob "^7.1.0"
-    has "^1.0.0"
-    htmlescape "^1.1.0"
-    https-browserify "~0.0.0"
-    inherits "~2.0.1"
-    insert-module-globals "^7.0.0"
-    labeled-stream-splicer "^2.0.0"
-    module-deps "^4.0.8"
-    os-browserify "~0.1.1"
-    parents "^1.0.1"
-    path-browserify "~0.0.0"
-    process "~0.11.0"
-    punycode "^1.3.2"
-    querystring-es3 "~0.2.0"
-    read-only-stream "^2.0.0"
-    readable-stream "^2.0.2"
-    resolve "^1.1.4"
-    shasum "^1.0.0"
-    shell-quote "^1.6.1"
-    stream-browserify "^2.0.0"
-    stream-http "^2.0.0"
-    string_decoder "~0.10.0"
-    subarg "^1.0.0"
-    syntax-error "^1.1.1"
-    through2 "^2.0.0"
-    timers-browserify "^1.0.1"
-    tty-browserify "~0.0.0"
-    url "~0.11.0"
-    util "~0.10.1"
-    vm-browserify "~0.0.1"
-    xtend "^4.0.0"
-
-browserslist@^1.4.0, browserslist@~1.4.0:
-  version "1.4.0"
-  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.4.0.tgz#9cfdcf5384d9158f5b70da2aa00b30e8ff019049"
+browserslist@^1.4.0, browserslist@^1.7.6:
+  version "1.7.7"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9"
   dependencies:
-    caniuse-db "^1.0.30000539"
+    caniuse-db "^1.0.30000639"
+    electron-to-chromium "^1.2.7"
 
 buffer-shims@^1.0.0:
   version "1.0.0"
@@ -1555,13 +1496,6 @@ buffer@^4.3.0, buffer@^4.9.0:
     ieee754 "^1.1.4"
     isarray "^1.0.0"
 
-buffer@^5.0.2:
-  version "5.0.5"
-  resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.0.5.tgz#35c9393244a90aff83581063d16f0882cecc9418"
-  dependencies:
-    base64-js "^1.0.2"
-    ieee754 "^1.1.4"
-
 bufferutil@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-2.0.1.tgz#8de37f5a300730c305fc3edd9f93348ee8a46288"
@@ -1578,9 +1512,9 @@ builtin-status-codes@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-2.0.0.tgz#6f22003baacf003ccd287afe6872151fddc58579"
 
-cached-path-relative@^1.0.0:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.1.tgz#d09c4b52800aa4c078e2dd81a869aac90d2e54e7"
+bytes@2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.3.0.tgz#d5b680a165b6201739acb611542aabc2d8ceb070"
 
 caller-path@^0.1.0:
   version "0.1.0"
@@ -1611,9 +1545,9 @@ camelcase@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
 
-caniuse-db@^1.0.30000539, caniuse-db@^1.0.30000540:
-  version "1.0.30000554"
-  resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000554.tgz#cd1dbe423d00b6203ba93f05973a476428dec919"
+caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
+  version "1.0.30000664"
+  resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000664.tgz#e16316e5fdabb9c7209b2bf0744ffc8a14201f22"
 
 case-sensitive-paths-webpack-plugin@^1.1.2:
   version "1.1.4"
@@ -1676,7 +1610,7 @@ cheerio@^0.22.0:
     lodash.reject "^4.4.0"
     lodash.some "^4.4.0"
 
-chokidar@^1.0.0, chokidar@^1.4.3, chokidar@^1.6.1:
+chokidar@^1.0.0, chokidar@^1.4.3, chokidar@^1.6.0, chokidar@^1.6.1:
   version "1.6.1"
   resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.6.1.tgz#2f4447ab5e96e50fb3d789fd90d4c72e0e4c70c2"
   dependencies:
@@ -1771,6 +1705,16 @@ code-point-at@^1.0.0:
   dependencies:
     number-is-nan "^1.0.0"
 
+coffee-loader@^0.7.3:
+  version "0.7.3"
+  resolved "https://registry.yarnpkg.com/coffee-loader/-/coffee-loader-0.7.3.tgz#fadbc6efd6fc7ecc88c5b3046a2c292066bcb54a"
+  dependencies:
+    loader-utils "^1.0.2"
+
+coffee-script@^1.12.5:
+  version "1.12.5"
+  resolved "https://registry.yarnpkg.com/coffee-script/-/coffee-script-1.12.5.tgz#809f4585419112bbfe46a073ad7543af18c27346"
+
 collapse-white-space@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.2.tgz#9c463fb9c6d190d2dcae21a356a01bcae9eeef6d"
@@ -1809,15 +1753,6 @@ colors@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
 
-combine-source-map@~0.7.1:
-  version "0.7.2"
-  resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.7.2.tgz#0870312856b307a87cc4ac486f3a9a62aeccc09e"
-  dependencies:
-    convert-source-map "~1.1.0"
-    inline-source-map "~0.6.0"
-    lodash.memoize "~3.0.3"
-    source-map "~0.5.3"
-
 combined-stream@^1.0.5, combined-stream@~1.0.5:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009"
@@ -1840,11 +1775,41 @@ commondir@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
 
+complex.js@2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/complex.js/-/complex.js-2.0.1.tgz#ea90c7a05aeceaf3a376d2c0f6a78421727d6879"
+
+compressible@~2.0.8:
+  version "2.0.10"
+  resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.10.tgz#feda1c7f7617912732b29bf8cf26252a20b9eecd"
+  dependencies:
+    mime-db ">= 1.27.0 < 2"
+
+compression-webpack-plugin@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-0.4.0.tgz#811de04215f811ea6a12d4d8aed8457d758f13ac"
+  dependencies:
+    async "0.2.x"
+    webpack-sources "^0.1.0"
+  optionalDependencies:
+    node-zopfli "^2.0.0"
+
+compression@^1.5.2:
+  version "1.6.2"
+  resolved "https://registry.yarnpkg.com/compression/-/compression-1.6.2.tgz#cceb121ecc9d09c52d7ad0c3350ea93ddd402bc3"
+  dependencies:
+    accepts "~1.3.3"
+    bytes "2.3.0"
+    compressible "~2.0.8"
+    debug "~2.2.0"
+    on-headers "~1.0.1"
+    vary "~1.1.0"
+
 concat-map@0.0.1:
   version "0.0.1"
   resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
 
-concat-stream@^1.4.7, concat-stream@^1.5.2, concat-stream@~1.5.0, concat-stream@~1.5.1:
+concat-stream@^1.4.7, concat-stream@^1.5.2:
   version "1.5.2"
   resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266"
   dependencies:
@@ -1866,6 +1831,10 @@ configstore@^2.0.0:
     write-file-atomic "^1.1.2"
     xdg-basedir "^2.0.0"
 
+connect-history-api-fallback@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz#e51d17f8f0ef0db90a64fdb47de3051556e9f169"
+
 console-browserify@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10"
@@ -1876,7 +1845,7 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
 
-constants-browserify@^1.0.0, constants-browserify@~1.0.0:
+constants-browserify@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
 
@@ -1896,10 +1865,6 @@ convert-source-map@^1.1.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.3.0.tgz#e9f3e9c6e2728efc2676696a70eb382f73106a67"
 
-convert-source-map@~1.1.0:
-  version "1.1.3"
-  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860"
-
 cookie-signature@1.0.6:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
@@ -1983,7 +1948,7 @@ crypto-browserify@3.3.0:
     ripemd160 "0.2.0"
     sha.js "2.2.6"
 
-crypto-browserify@^3.0.0, crypto-browserify@^3.11.0:
+crypto-browserify@^3.11.0:
   version "3.11.0"
   resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.11.0.tgz#3652a0906ab9b2a7e0c3ce66a408e957a2485522"
   dependencies:
@@ -1998,11 +1963,20 @@ crypto-browserify@^3.0.0, crypto-browserify@^3.11.0:
     public-encrypt "^4.0.0"
     randombytes "^2.0.0"
 
+css-color-function@^1.2.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/css-color-function/-/css-color-function-1.3.0.tgz#72c767baf978f01b8a8a94f42f17ba5d22a776fc"
+  dependencies:
+    balanced-match "0.1.0"
+    color "^0.11.0"
+    debug "~0.7.4"
+    rgb "~0.1.0"
+
 css-color-names@0.0.4:
   version "0.0.4"
   resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
 
-css-loader@^0.26.1, css-loader@^0.26.2:
+css-loader@^0.26.1:
   version "0.26.2"
   resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.26.2.tgz#a9cd4c2b1a559b45d8efc04fc311ab5d2aaccb9d"
   dependencies:
@@ -2019,6 +1993,23 @@ css-loader@^0.26.1, css-loader@^0.26.2:
     postcss-modules-values "^1.1.0"
     source-list-map "^0.1.7"
 
+css-loader@^0.28.0:
+  version "0.28.0"
+  resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.0.tgz#417cfa9789f8cde59a30ccbf3e4da7a806889bad"
+  dependencies:
+    babel-code-frame "^6.11.0"
+    css-selector-tokenizer "^0.7.0"
+    cssnano ">=2.6.1 <4"
+    loader-utils "^1.0.2"
+    lodash.camelcase "^4.3.0"
+    object-assign "^4.0.1"
+    postcss "^5.0.6"
+    postcss-modules-extract-imports "^1.0.0"
+    postcss-modules-local-by-default "^1.0.1"
+    postcss-modules-scope "^1.0.0"
+    postcss-modules-values "^1.1.0"
+    source-list-map "^0.1.7"
+
 css-select@~1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
@@ -2138,10 +2129,18 @@ debug@2.2.0, debug@^2.1.1, debug@^2.2.0, debug@~2.2.0:
   dependencies:
     ms "0.7.1"
 
+debug@~0.7.4:
+  version "0.7.4"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39"
+
 decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
 
+decimal.js@7.1.1:
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-7.1.1.tgz#1adcad7d70d7a91c426d756f1eb6566c3be6cbcf"
+
 deep-eql@^0.1.3:
   version "0.1.3"
   resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-0.1.3.tgz#ef558acab8de25206cd713906d74e56930eb69f2"
@@ -2160,6 +2159,12 @@ deep-is@~0.1.3:
   version "0.1.3"
   resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
 
+defaults@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d"
+  dependencies:
+    clone "^1.0.2"
+
 define-properties@^1.1.1, define-properties@^1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94"
@@ -2195,15 +2200,6 @@ depd@~1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3"
 
-deps-sort@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/deps-sort/-/deps-sort-2.0.0.tgz#091724902e84658260eb910748cccd1af6e21fb5"
-  dependencies:
-    JSONStream "^1.0.3"
-    shasum "^1.0.0"
-    subarg "^1.0.0"
-    through2 "^2.0.0"
-
 des.js@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc"
@@ -2221,13 +2217,6 @@ detect-indent@^4.0.0:
   dependencies:
     repeating "^2.0.0"
 
-detective@^4.0.0:
-  version "4.3.1"
-  resolved "https://registry.yarnpkg.com/detective/-/detective-4.3.1.tgz#9fb06dd1ee8f0ea4dbcc607cda39d9ce1d4f726f"
-  dependencies:
-    acorn "^1.0.3"
-    defined "^1.0.0"
-
 diff@1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf"
@@ -2265,7 +2254,7 @@ dom-serializer@0, dom-serializer@~0.1.0:
     domelementtype "~1.1.1"
     entities "~1.1.1"
 
-domain-browser@^1.1.1, domain-browser@~1.1.0:
+domain-browser@^1.1.1:
   version "1.1.7"
   resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc"
 
@@ -2304,12 +2293,6 @@ double-ended-queue@^2.1.0-0:
   version "2.1.0-0"
   resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c"
 
-duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2:
-  version "0.1.4"
-  resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"
-  dependencies:
-    readable-stream "^2.0.2"
-
 ecc-jsbn@~0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505"
@@ -2324,6 +2307,10 @@ ee-first@1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
 
+electron-to-chromium@^1.2.7:
+  version "1.3.8"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.8.tgz#b2c8a2c79bb89fbbfd3724d9555e15095b5f5fb6"
+
 element-class@^0.2.0:
   version "0.2.2"
   resolved "https://registry.yarnpkg.com/element-class/-/element-class-0.2.2.tgz#9d3bbd0767f9013ef8e1c8ebe722c1402a60050e"
@@ -2604,7 +2591,7 @@ esprima@^2.6.0, esprima@^2.7.1:
   version "2.7.3"
   resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
 
-esprima@~3.1.0:
+esprima@^3.1.1, esprima@~3.1.0:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
 
@@ -2648,10 +2635,20 @@ event-emitter@~0.3.5:
     d "1"
     es5-ext "~0.10.14"
 
-events@^1.0.0, events@^1.1.1, events@~1.1.0:
+eventemitter3@1.x.x:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508"
+
+events@^1.0.0, events@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
 
+eventsource@0.1.6:
+  version "0.1.6"
+  resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-0.1.6.tgz#0acede849ed7dd1ccc32c811bb11b944d4f29232"
+  dependencies:
+    original ">=0.0.5"
+
 eventsource@^0.2.1:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-0.2.1.tgz#662bf85f376e73b5c34c2ee17db566b8419a6232"
@@ -2729,6 +2726,15 @@ extglob@^0.3.1:
   dependencies:
     is-extglob "^1.0.0"
 
+extract-text-webpack-plugin@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/extract-text-webpack-plugin/-/extract-text-webpack-plugin-2.1.0.tgz#69315b885f876dbf96d3819f6a9f1cca7aebf159"
+  dependencies:
+    ajv "^4.11.2"
+    async "^2.1.2"
+    loader-utils "^1.0.2"
+    webpack-sources "^0.1.0"
+
 extsprintf@1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550"
@@ -2741,6 +2747,18 @@ fastparse@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8"
 
+faye-websocket@^0.10.0:
+  version "0.10.0"
+  resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4"
+  dependencies:
+    websocket-driver ">=0.5.1"
+
+faye-websocket@~0.11.0:
+  version "0.11.1"
+  resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.1.tgz#f0efe18c4f56e4f40afc7e06c719fd5ee6188f38"
+  dependencies:
+    websocket-driver ">=0.5.1"
+
 fbjs@^0.8.4, fbjs@^0.8.9:
   version "0.8.12"
   resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.12.tgz#10b5d92f76d45575fd63a217d4ea02bea2f8ed04"
@@ -2767,6 +2785,12 @@ file-entry-cache@^2.0.0:
     flat-cache "^1.2.1"
     object-assign "^4.0.1"
 
+file-loader@^0.11.1:
+  version "0.11.1"
+  resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-0.11.1.tgz#6b328ee1234a729e4e47d36375dd6d35c0e1db84"
+  dependencies:
+    loader-utils "^1.0.2"
+
 file-loader@^0.9.0:
   version "0.9.0"
   resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-0.9.0.tgz#1d2daddd424ce6d1b07cfe3f79731bed3617ab42"
@@ -2831,6 +2855,10 @@ follow-redirects@1.0.0:
   dependencies:
     debug "^2.2.0"
 
+font-awesome@^4.7.0:
+  version "4.7.0"
+  resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133"
+
 for-in@^0.1.3, for-in@^0.1.5:
   version "0.1.6"
   resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.6.tgz#c9f96e89bfad18a545af5ec3ed352a1d9e5b4dc8"
@@ -2849,14 +2877,6 @@ forever-agent@~0.6.1:
   version "0.6.1"
   resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
 
-form-data@~2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.0.0.tgz#6f0aebadcc5da16c13e1ecc11137d85f9b883b25"
-  dependencies:
-    asynckit "^0.4.0"
-    combined-stream "^1.0.5"
-    mime-types "^2.1.11"
-
 form-data@~2.1.1:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.2.tgz#89c3534008b97eada4cbb157d58f6f5df025eae4"
@@ -2875,10 +2895,39 @@ forwarded@~0.1.0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363"
 
+fraction.js@4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.0.0.tgz#73974e2f8b51ef709536d624cc90782e2bb61274"
+
 fresh@0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.3.0.tgz#651f838e22424e7566de161d8358caa199f83d4f"
 
+fs-extra@^0.24.0:
+  version "0.24.0"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.24.0.tgz#d4e4342a96675cb7846633a6099249332b539952"
+  dependencies:
+    graceful-fs "^4.1.2"
+    jsonfile "^2.1.0"
+    path-is-absolute "^1.0.0"
+    rimraf "^2.2.8"
+
+fs-extra@^0.30.0:
+  version "0.30.0"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0"
+  dependencies:
+    graceful-fs "^4.1.2"
+    jsonfile "^2.1.0"
+    klaw "^1.0.0"
+    path-is-absolute "^1.0.0"
+    rimraf "^2.2.8"
+
+fs-promise@^0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/fs-promise/-/fs-promise-0.3.1.tgz#bf34050368f24d6dc9dfc6688ab5cead8f86842a"
+  dependencies:
+    any-promise "~0.1.0"
+
 fs-readdir-recursive@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.0.0.tgz#8cd1745c8b4f8a29c8caec392476921ba195f560"
@@ -2996,7 +3045,7 @@ glob-parent@^2.0.0:
   dependencies:
     is-glob "^2.0.0"
 
-glob@7.0.5, glob@^7.0.0:
+glob@7.0.5:
   version "7.0.5"
   resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.5.tgz#b4202a69099bbb4d292a7c1b95b6682b67ebdc95"
   dependencies:
@@ -3007,7 +3056,17 @@ glob@7.0.5, glob@^7.0.0:
     once "^1.3.0"
     path-is-absolute "^1.0.0"
 
-glob@^7.0.3, glob@^7.0.5, glob@^7.1.0, glob@~7.1.1:
+glob@^5.0.3:
+  version "5.0.15"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
+  dependencies:
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "2 || 3"
+    once "^1.3.0"
+    path-is-absolute "^1.0.0"
+
+glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@~7.1.1:
   version "7.1.1"
   resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
   dependencies:
@@ -3022,6 +3081,17 @@ globals@^9.0.0, globals@^9.14.0:
   version "9.14.0"
   resolved "https://registry.yarnpkg.com/globals/-/globals-9.14.0.tgz#8859936af0038741263053b39d0e76ca241e4034"
 
+globby@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/globby/-/globby-3.0.1.tgz#2094af8421e19152150d5893eb6416b312d9a22f"
+  dependencies:
+    array-union "^1.0.1"
+    arrify "^1.0.0"
+    glob "^5.0.3"
+    object-assign "^4.0.1"
+    pify "^2.0.0"
+    pinkie-promise "^1.0.0"
+
 globby@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d"
@@ -3041,7 +3111,13 @@ globule@^1.0.0:
     lodash "~4.16.4"
     minimatch "~3.0.2"
 
-graceful-fs@^4.1.2, graceful-fs@^4.1.4:
+gonzales-pe@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/gonzales-pe/-/gonzales-pe-4.0.3.tgz#36148e18e267184fbfdc929af28f29ad9fbf9746"
+  dependencies:
+    minimist "1.1.x"
+
+graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.6, graceful-fs@^4.1.9:
   version "4.1.9"
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.9.tgz#baacba37d19d11f9d146d3578bc99958c3787e29"
 
@@ -3053,6 +3129,10 @@ growl@1.9.2:
   version "1.9.2"
   resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f"
 
+handle-thing@^1.2.4:
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4"
+
 har-validator@~2.0.6:
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d"
@@ -3076,7 +3156,7 @@ has-unicode@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
 
-has@^1.0.0, has@^1.0.1:
+has@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28"
   dependencies:
@@ -3125,6 +3205,15 @@ hosted-git-info@^2.1.4:
   version "2.1.5"
   resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.1.5.tgz#0ba81d90da2e25ab34a332e6ec77936e1598118b"
 
+hpack.js@^2.1.6:
+  version "2.1.6"
+  resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2"
+  dependencies:
+    inherits "^2.0.1"
+    obuf "^1.0.0"
+    readable-stream "^2.0.1"
+    wbuf "^1.1.0"
+
 html-comment-regex@^1.1.0:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e"
@@ -3145,10 +3234,6 @@ html@^1.0.0:
   dependencies:
     concat-stream "^1.4.7"
 
-htmlescape@^1.1.0:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351"
-
 htmlparser2@^3.9.1:
   version "3.9.2"
   resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338"
@@ -3160,7 +3245,11 @@ htmlparser2@^3.9.1:
     inherits "^2.0.1"
     readable-stream "^2.0.2"
 
-http-errors@~1.5.1:
+http-deceiver@^1.2.4:
+  version "1.2.7"
+  resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
+
+http-errors@~1.5.0, http-errors@~1.5.1:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.5.1.tgz#788c0d2c1de2c81b9e6e8c01843b6b97eb920750"
   dependencies:
@@ -3172,6 +3261,22 @@ http-link-header@^0.8.0:
   version "0.8.0"
   resolved "https://registry.yarnpkg.com/http-link-header/-/http-link-header-0.8.0.tgz#a22b41a0c9b1e2d8fac1bf1b697c6bd532d5f5e4"
 
+http-proxy-middleware@~0.17.4:
+  version "0.17.4"
+  resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz#642e8848851d66f09d4f124912846dbaeb41b833"
+  dependencies:
+    http-proxy "^1.16.2"
+    is-glob "^3.1.0"
+    lodash "^4.17.2"
+    micromatch "^2.3.11"
+
+http-proxy@^1.16.2:
+  version "1.16.2"
+  resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.16.2.tgz#06dff292952bf64dbe8471fa9df73066d4f37742"
+  dependencies:
+    eventemitter3 "1.x.x"
+    requires-port "1.x.x"
+
 http-signature@~1.1.0:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf"
@@ -3180,7 +3285,7 @@ http-signature@~1.1.0:
     jsprim "^1.2.2"
     sshpk "^1.7.0"
 
-https-browserify@0.0.1, https-browserify@~0.0.0:
+https-browserify@0.0.1:
   version "0.0.1"
   resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82"
 
@@ -3245,12 +3350,6 @@ ini@~1.3.0:
   version "1.3.4"
   resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
 
-inline-source-map@~0.6.0:
-  version "0.6.2"
-  resolved "https://registry.yarnpkg.com/inline-source-map/-/inline-source-map-0.6.2.tgz#f9393471c18a79d1724f863fa38b586370ade2a5"
-  dependencies:
-    source-map "~0.5.3"
-
 inquirer@^0.12.0:
   version "0.12.0"
   resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e"
@@ -3269,19 +3368,6 @@ inquirer@^0.12.0:
     strip-ansi "^3.0.0"
     through "^2.3.6"
 
-insert-module-globals@^7.0.0:
-  version "7.0.1"
-  resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-7.0.1.tgz#c03bf4e01cb086d5b5e5ace8ad0afe7889d638c3"
-  dependencies:
-    JSONStream "^1.0.3"
-    combine-source-map "~0.7.1"
-    concat-stream "~1.5.1"
-    is-buffer "^1.1.0"
-    lexical-scope "^1.2.0"
-    process "~0.11.0"
-    through2 "^2.0.0"
-    xtend "^4.0.0"
-
 interpret@^0.6.4:
   version "0.6.6"
   resolved "https://registry.yarnpkg.com/interpret/-/interpret-0.6.6.tgz#fecd7a18e7ce5ca6abfb953e1f86213a49f1625b"
@@ -3298,7 +3384,7 @@ intl-format-cache@^2.0.5:
   version "2.0.5"
   resolved "https://registry.yarnpkg.com/intl-format-cache/-/intl-format-cache-2.0.5.tgz#b484cefcb9353f374f25de389a3ceea1af18d7c9"
 
-intl-messageformat-parser@1.2.0:
+intl-messageformat-parser@1.2.0, intl-messageformat-parser@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/intl-messageformat-parser/-/intl-messageformat-parser-1.2.0.tgz#5906b7f953ab7470e0dc8549097b648b991892ff"
 
@@ -3314,7 +3400,7 @@ intl-relativeformat@^1.3.0:
   dependencies:
     intl-messageformat "1.3.0"
 
-invariant@2.x.x, invariant@^2.0.0, invariant@^2.1.1, invariant@^2.2.0, invariant@^2.2.1:
+invariant@2.x.x, invariant@^2.0.0, invariant@^2.1.1, invariant@^2.2.0, invariant@^2.2.1, invariant@^2.2.2:
   version "2.2.2"
   resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360"
   dependencies:
@@ -3342,7 +3428,7 @@ is-binary-path@^1.0.0:
   dependencies:
     binary-extensions "^1.0.0"
 
-is-buffer@^1.0.2, is-buffer@^1.1.0:
+is-buffer@^1.0.2:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.4.tgz#cfc86ccd5dc5a52fa80489111c6920c457e2d98b"
 
@@ -3382,6 +3468,10 @@ is-extglob@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0"
 
+is-extglob@^2.1.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+
 is-finite@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa"
@@ -3404,6 +3494,12 @@ is-glob@^2.0.0, is-glob@^2.0.1:
   dependencies:
     is-extglob "^1.0.0"
 
+is-glob@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a"
+  dependencies:
+    is-extglob "^2.1.0"
+
 is-my-json-valid@^2.10.0, is-my-json-valid@^2.12.4:
   version "2.15.0"
   resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.15.0.tgz#936edda3ca3c211fd98f3b2d3e08da43f7b2915b"
@@ -3505,10 +3601,6 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
 
-isarray@~0.0.1:
-  version "0.0.1"
-  resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
-
 isexe@^1.1.1:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/isexe/-/isexe-1.1.2.tgz#36f3e22e60750920f5e7241a476a8c6a42275ad0"
@@ -3540,6 +3632,16 @@ jodid25519@^1.0.0:
   dependencies:
     jsbn "~0.1.0"
 
+jquery-ujs@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/jquery-ujs/-/jquery-ujs-1.2.2.tgz#6a8ef1020e6b6dda385b90a4bddc128c21c56397"
+  dependencies:
+    jquery ">=1.8.0"
+
+jquery@>=1.8.0:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.2.1.tgz#5c4d9de652af6cd0a770154a631bba12b015c787"
+
 js-base64@^2.1.9:
   version "2.1.9"
   resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.1.9.tgz#f0e80ae039a4bd654b5f281fc93f04a914a7fcce"
@@ -3548,15 +3650,18 @@ js-tokens@^1.0.1:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-1.0.3.tgz#14e56eb68c8f1a92c43d59f5014ec29dc20f2ae1"
 
-js-tokens@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-2.0.0.tgz#79903f5563ee778cc1162e6dcf1a0027c97f9cb5"
-
 js-tokens@^3.0.0:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7"
 
-js-yaml@^3.4.3, js-yaml@^3.5.1, js-yaml@~3.6.1:
+js-yaml@^3.4.3, js-yaml@^3.5.1, js-yaml@^3.8.3:
+  version "3.8.3"
+  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.3.tgz#33a05ec481c850c8875929166fe1beb61c728766"
+  dependencies:
+    argparse "^1.0.7"
+    esprima "^3.1.1"
+
+js-yaml@~3.6.1:
   version "3.6.1"
   resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.6.1.tgz#6e5fe67d8b205ce4d22fad05b7781e8dadcc4b30"
   dependencies:
@@ -3613,17 +3718,11 @@ json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1:
   dependencies:
     jsonify "~0.0.0"
 
-json-stable-stringify@~0.0.0:
-  version "0.0.1"
-  resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz#611c23e814db375527df851193db59dd2af27f45"
-  dependencies:
-    jsonify "~0.0.0"
-
 json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
 
-json3@3.3.2:
+json3@3.3.2, json3@^3.3.2:
   version "3.3.2"
   resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1"
 
@@ -3631,18 +3730,20 @@ json5@^0.5.0:
   version "0.5.0"
   resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.0.tgz#9b20715b026cbe3778fd769edccd822d8332a5b2"
 
+json5@^0.5.1:
+  version "0.5.1"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
+
+jsonfile@^2.1.0:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8"
+  optionalDependencies:
+    graceful-fs "^4.1.6"
+
 jsonify@~0.0.0:
   version "0.0.0"
   resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
 
-jsonparse@0.0.5:
-  version "0.0.5"
-  resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-0.0.5.tgz#330542ad3f0a654665b778f3eb2d9a9fa507ac64"
-
-jsonparse@^1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.2.0.tgz#5c0c5685107160e72fe7489bddea0b44c2bc67bd"
-
 jsonpointer@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.0.tgz#6661e161d2fc445f19f98430231343722e1fcbd5"
@@ -3677,13 +3778,11 @@ kind-of@^3.0.2:
   dependencies:
     is-buffer "^1.0.2"
 
-labeled-stream-splicer@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz#a52e1d138024c00b86b1c0c91f677918b8ae0a59"
-  dependencies:
-    inherits "^2.0.1"
-    isarray "~0.0.1"
-    stream-splicer "^2.0.0"
+klaw@^1.0.0:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439"
+  optionalDependencies:
+    graceful-fs "^4.1.9"
 
 lazy-cache@^0.2.3:
   version "0.2.7"
@@ -3706,12 +3805,6 @@ levn@^0.3.0, levn@~0.3.0:
     prelude-ls "~1.1.2"
     type-check "~0.3.2"
 
-lexical-scope@^1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/lexical-scope/-/lexical-scope-1.2.0.tgz#fcea5edc704a4b3a8796cdca419c3a0afaf22df4"
-  dependencies:
-    astw "^2.0.0"
-
 load-json-file@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
@@ -3735,7 +3828,15 @@ loader-utils@0.2.x, loader-utils@^0.2.11, loader-utils@^0.2.16, loader-utils@^0.
     json5 "^0.5.0"
     object-assign "^4.0.1"
 
-loader-utils@^1.0.1, loader-utils@^1.0.2:
+loader-utils@^1.0.1, loader-utils@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd"
+  dependencies:
+    big.js "^3.1.3"
+    emojis-list "^2.0.0"
+    json5 "^0.5.0"
+
+loader-utils@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.0.2.tgz#a9f923c865a974623391a8602d031137fad74830"
   dependencies:
@@ -3818,7 +3919,7 @@ lodash.create@3.1.1:
     lodash._basecreate "^3.0.0"
     lodash._isiterateecall "^3.0.0"
 
-lodash.defaults@^4.0.1:
+lodash.defaults@^4.0.1, lodash.defaults@^4.2.0:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
 
@@ -3858,10 +3959,6 @@ lodash.map@^4.4.0:
   version "4.6.0"
   resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3"
 
-lodash.memoize@~3.0.3:
-  version "3.0.4"
-  resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f"
-
 lodash.merge@^4.4.0:
   version "4.6.0"
   resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5"
@@ -3898,7 +3995,7 @@ lodash.tail@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/lodash.tail/-/lodash.tail-4.1.1.tgz#d2333a36d9e7717c8ad2f7cacafec7c32b444664"
 
-lodash@4.x.x, lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.1, lodash@^4.3.0:
+lodash@4.x.x, "lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.1, lodash@^4.3.0:
   version "4.17.4"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
 
@@ -3914,18 +4011,18 @@ longest@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
 
-loose-envify@^1.0.0, loose-envify@^1.3.0:
-  version "1.3.1"
-  resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
-  dependencies:
-    js-tokens "^3.0.0"
-
-loose-envify@^1.1.0, loose-envify@^1.2.0:
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.2.0.tgz#69a65aad3de542cf4ee0f4fe74e8e33c709ccb0f"
   dependencies:
     js-tokens "^1.0.1"
 
+loose-envify@^1.3.0:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
+  dependencies:
+    js-tokens "^3.0.0"
+
 loud-rejection@^1.0.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
@@ -3962,6 +4059,17 @@ math-expression-evaluator@^1.2.14:
   dependencies:
     lodash.indexof "^4.0.5"
 
+mathjs@^3.11.5:
+  version "3.12.1"
+  resolved "https://registry.yarnpkg.com/mathjs/-/mathjs-3.12.1.tgz#a165abdbc6b55da1ec600b85311e2962850551b5"
+  dependencies:
+    complex.js "2.0.1"
+    decimal.js "7.1.1"
+    fraction.js "4.0.0"
+    seed-random "2.2.0"
+    tiny-emitter "1.0.2"
+    typed-function "0.10.5"
+
 media-typer@0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@@ -4007,7 +4115,7 @@ methods@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
 
-micromatch@^2.1.5:
+micromatch@^2.1.5, micromatch@^2.3.11:
   version "2.3.11"
   resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565"
   dependencies:
@@ -4032,11 +4140,15 @@ miller-rabin@^4.0.0:
     bn.js "^4.0.0"
     brorand "^1.0.1"
 
+"mime-db@>= 1.27.0 < 2":
+  version "1.27.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1"
+
 mime-db@~1.26.0:
   version "1.26.0"
   resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.26.0.tgz#eaffcd0e4fc6935cf8134da246e2e6c35305adff"
 
-mime-types@^2.1.11, mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.13, mime-types@~2.1.7:
+mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.13, mime-types@~2.1.7:
   version "2.1.14"
   resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.14.tgz#f7ef7d97583fcaf3b7d282b6f8b5679dab1e94ee"
   dependencies:
@@ -4054,7 +4166,7 @@ minimalistic-assert@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3"
 
-minimatch@^3.0.0, minimatch@^3.0.2, minimatch@~3.0.2:
+"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@~3.0.2:
   version "3.0.3"
   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774"
   dependencies:
@@ -4064,7 +4176,11 @@ minimist@0.0.8, minimist@~0.0.1:
   version "0.0.8"
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
 
-minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0:
+minimist@1.1.x:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.1.3.tgz#3bedfd91a92d39016fcfaa1c681e8faa1a1efda8"
+
+minimist@^1.1.3, minimist@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
 
@@ -4075,7 +4191,7 @@ mixin-object@^2.0.1:
     for-in "^0.1.3"
     is-extendable "^0.1.1"
 
-mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
+mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
   version "0.5.1"
   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
   dependencies:
@@ -4101,26 +4217,6 @@ mocha@^3.2.0:
     mkdirp "0.5.1"
     supports-color "3.1.2"
 
-module-deps@^4.0.8:
-  version "4.1.1"
-  resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-4.1.1.tgz#23215833f1da13fd606ccb8087b44852dcb821fd"
-  dependencies:
-    JSONStream "^1.0.3"
-    browser-resolve "^1.7.0"
-    cached-path-relative "^1.0.0"
-    concat-stream "~1.5.0"
-    defined "^1.0.0"
-    detective "^4.0.0"
-    duplexer2 "^0.1.2"
-    inherits "^2.0.1"
-    parents "^1.0.0"
-    readable-stream "^2.0.2"
-    resolve "^1.1.3"
-    stream-combiner2 "^1.1.1"
-    subarg "^1.0.0"
-    through2 "^2.0.0"
-    xtend "^4.0.0"
-
 ms@0.7.1:
   version "0.7.1"
   resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
@@ -4133,7 +4229,7 @@ mute-stream@0.0.5:
   version "0.0.5"
   resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0"
 
-nan@^2.3.0, nan@^2.3.2, nan@~2.5.0:
+nan@^2.0.0, nan@^2.3.0, nan@^2.3.2, nan@~2.5.0:
   version "2.5.1"
   resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.1.tgz#d5b01691253326a97a2bbee9e61c55d8d60351e2"
 
@@ -4236,7 +4332,7 @@ node-libs-browser@^2.0.0:
     util "^0.10.3"
     vm-browserify "0.0.4"
 
-node-pre-gyp@^0.6.29:
+node-pre-gyp@^0.6.29, node-pre-gyp@^0.6.4:
   version "0.6.30"
   resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.30.tgz#64d3073a6f573003717ccfe30c89023297babba1"
   dependencies:
@@ -4273,9 +4369,14 @@ node-sass@^4.5.2:
     sass-graph "^2.1.1"
     stdout-stream "^1.4.0"
 
-node-uuid@~1.4.7:
-  version "1.4.7"
-  resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.7.tgz#6da5a17668c4b3dd59623bda11cf7fa4c1f60a6f"
+node-zopfli@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/node-zopfli/-/node-zopfli-2.0.2.tgz#a7a473ae92aaea85d4c68d45bbf2c944c46116b8"
+  dependencies:
+    commander "^2.8.1"
+    defaults "^1.0.2"
+    nan "^2.0.0"
+    node-pre-gyp "^0.6.4"
 
 noop-logger@^0.1.1:
   version "0.1.1"
@@ -4344,7 +4445,7 @@ oauth-sign@~0.8.1:
   version "0.8.2"
   resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
 
-object-assign@4.1.0, object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0:
+object-assign@4.1.0, object-assign@^4.0.1:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0"
 
@@ -4352,7 +4453,7 @@ object-assign@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2"
 
-object-assign@^4.1.1:
+object-assign@^4.1.0, object-assign@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
 
@@ -4404,12 +4505,20 @@ object.values@^1.0.3:
     function-bind "^1.0.2"
     has "^1.0.1"
 
+obuf@^1.0.0, obuf@^1.1.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.1.tgz#104124b6c602c6796881a042541d36db43a5264e"
+
 on-finished@~2.3.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
   dependencies:
     ee-first "1.1.1"
 
+on-headers@~1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7"
+
 once@^1.3.0, once@^1.3.1:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@@ -4426,6 +4535,13 @@ onetime@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789"
 
+opn@4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95"
+  dependencies:
+    object-assign "^4.0.1"
+    pinkie-promise "^2.0.0"
+
 optimist@~0.6.0:
   version "0.6.1"
   resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
@@ -4444,7 +4560,7 @@ optionator@^0.8.1, optionator@^0.8.2:
     type-check "~0.3.2"
     wordwrap "~1.0.0"
 
-original@^1.0.0:
+original@>=0.0.5, original@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/original/-/original-1.0.0.tgz#9147f93fa1696d04be61e01bd50baeaca656bd3b"
   dependencies:
@@ -4454,10 +4570,6 @@ os-browserify@^0.2.0:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.2.1.tgz#63fc4ccee5d2d7763d26bbf8601078e6c2e0044f"
 
-os-browserify@~0.1.1:
-  version "0.1.2"
-  resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.1.2.tgz#49ca0293e0b19590a5f5de10c7f265a617d8fe54"
-
 os-homedir@^1.0.0, os-homedir@^1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
@@ -4495,12 +4607,6 @@ pako@~0.2.0:
   version "0.2.9"
   resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75"
 
-parents@^1.0.0, parents@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/parents/-/parents-1.0.1.tgz#fedd4d2bf193a77745fe71e371d73c3307d9c751"
-  dependencies:
-    path-platform "~0.11.15"
-
 parse-asn1@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.0.0.tgz#35060f6d5015d37628c770f4e091a0b5a278bc23"
@@ -4534,10 +4640,14 @@ parseurl@~1.3.0, parseurl@~1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56"
 
-path-browserify@0.0.0, path-browserify@~0.0.0:
+path-browserify@0.0.0:
   version "0.0.0"
   resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a"
 
+path-complete-extname@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/path-complete-extname/-/path-complete-extname-0.1.0.tgz#c454702669f31452f8193aa6168915fa31692f4a"
+
 path-exists@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
@@ -4552,9 +4662,9 @@ path-is-inside@^1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
 
-path-platform@~0.11.15:
-  version "0.11.15"
-  resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.11.15.tgz#e864217f74c36850f0852b78dc7bf7d4a5721bf2"
+path-parse@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1"
 
 path-to-regexp@0.1.7:
   version "0.1.7"
@@ -4625,12 +4735,22 @@ pify@^2.0.0, pify@^2.3.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
 
+pinkie-promise@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-1.0.0.tgz#d1da67f5482563bb7cf57f286ae2822ecfbf3670"
+  dependencies:
+    pinkie "^1.0.0"
+
 pinkie-promise@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
   dependencies:
     pinkie "^2.0.0"
 
+pinkie@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-1.0.0.tgz#5a47f28ba1015d0201bda7bf0f358e47bec8c7e4"
+
 pinkie@^2.0.0:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
@@ -4652,6 +4772,26 @@ podda@^1.2.1:
     babel-runtime "^6.11.6"
     immutable "^3.8.1"
 
+portfinder@^1.0.9:
+  version "1.0.13"
+  resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.13.tgz#bb32ecd87c27104ae6ee44b5a3ccbf0ebb1aede9"
+  dependencies:
+    async "^1.5.2"
+    debug "^2.2.0"
+    mkdirp "0.5.x"
+
+postcss-advanced-variables@1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/postcss-advanced-variables/-/postcss-advanced-variables-1.2.2.tgz#90a6213262e66a050a368b4a9c5d4778d72dbd74"
+  dependencies:
+    postcss "^5.0.10"
+
+postcss-atroot@^0.1.2:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/postcss-atroot/-/postcss-atroot-0.1.3.tgz#6752c0230c745140549345b2b0e30ebeda01a405"
+  dependencies:
+    postcss "^5.0.5"
+
 postcss-calc@^5.2.0:
   version "5.3.1"
   resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e"
@@ -4660,6 +4800,15 @@ postcss-calc@^5.2.0:
     postcss-message-helpers "^2.0.0"
     reduce-css-calc "^1.2.6"
 
+postcss-color-function@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-color-function/-/postcss-color-function-2.0.1.tgz#9ad226f550e8a7c7f8b8a77860545b6dd7f55241"
+  dependencies:
+    css-color-function "^1.2.0"
+    postcss "^5.0.4"
+    postcss-message-helpers "^2.0.0"
+    postcss-value-parser "^3.3.0"
+
 postcss-colormin@^2.1.8:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.1.tgz#dc5421b6ae6f779ef6bfd47352b94abe59d0316b"
@@ -4675,6 +4824,27 @@ postcss-convert-values@^2.3.4:
     postcss "^5.0.11"
     postcss-value-parser "^3.1.2"
 
+postcss-custom-media@^5.0.0:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-5.0.1.tgz#138d25a184bf2eb54de12d55a6c01c30a9d8bd81"
+  dependencies:
+    postcss "^5.0.0"
+
+postcss-custom-properties@^5.0.0:
+  version "5.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-5.0.2.tgz#9719d78f2da9cf9f53810aebc23d4656130aceb1"
+  dependencies:
+    balanced-match "^0.4.2"
+    postcss "^5.0.0"
+
+postcss-custom-selectors@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-custom-selectors/-/postcss-custom-selectors-3.0.0.tgz#8f81249f5ed07a8d0917cf6a39fe5b056b7f96ac"
+  dependencies:
+    balanced-match "^0.2.0"
+    postcss "^5.0.0"
+    postcss-selector-matches "^2.0.0"
+
 postcss-discard-comments@^2.0.4:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d"
@@ -4707,6 +4877,12 @@ postcss-discard-unused@^2.2.1:
     postcss "^5.0.14"
     uniqs "^2.0.0"
 
+postcss-extend@^1.0.1:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/postcss-extend/-/postcss-extend-1.0.5.tgz#5ea98bf787ba3cacf4df4609743f80a833b1d0e7"
+  dependencies:
+    postcss "^5.0.4"
+
 postcss-filter-plugins@^2.0.0:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz#6d85862534d735ac420e4a85806e1f5d4286d84c"
@@ -4714,7 +4890,7 @@ postcss-filter-plugins@^2.0.0:
     postcss "^5.0.4"
     uniqid "^4.0.0"
 
-postcss-load-config@^1.0.0:
+postcss-load-config@^1.0.0, postcss-load-config@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-1.2.0.tgz#539e9afc9ddc8620121ebf9d8c3673e0ce50d28a"
   dependencies:
@@ -4746,6 +4922,21 @@ postcss-loader@1.1.0:
     postcss "^5.2.5"
     postcss-load-config "^1.0.0"
 
+postcss-loader@^1.3.3:
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-1.3.3.tgz#a621ea1fa29062a83972a46f54486771301916eb"
+  dependencies:
+    loader-utils "^1.0.2"
+    object-assign "^4.1.1"
+    postcss "^5.2.15"
+    postcss-load-config "^1.2.0"
+
+postcss-media-minmax@^2.1.0:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/postcss-media-minmax/-/postcss-media-minmax-2.1.2.tgz#444c5cf8926ab5e4fd8a2509e9297e751649cdf8"
+  dependencies:
+    postcss "^5.0.4"
+
 postcss-merge-idents@^2.1.5:
   version "2.1.7"
   resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270"
@@ -4803,6 +4994,14 @@ postcss-minify-selectors@^2.0.4:
     postcss "^5.0.14"
     postcss-selector-parser "^2.0.0"
 
+postcss-mixins@^2.1.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/postcss-mixins/-/postcss-mixins-2.1.1.tgz#b141a0803efa8e2d744867f8d91596890cf9241b"
+  dependencies:
+    globby "^3.0.1"
+    postcss "^5.0.10"
+    postcss-simple-vars "^1.0.1"
+
 postcss-modules-extract-imports@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.0.1.tgz#8fb3fef9a6dd0420d3f6d4353cf1ff73f2b2a341"
@@ -4830,6 +5029,18 @@ postcss-modules-values@^1.1.0:
     icss-replace-symbols "^1.0.2"
     postcss "^5.0.14"
 
+postcss-nested@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-1.0.1.tgz#91f28f4e6e23d567241ac154558a0cfab4cc0d8f"
+  dependencies:
+    postcss "^5.2.17"
+
+postcss-nesting@^2.0.6:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-2.3.1.tgz#94a6b6a4ef707fbec20a87fee5c957759b4e01cf"
+  dependencies:
+    postcss "^5.0.19"
+
 postcss-normalize-charset@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.0.tgz#2fbd30e12248c442981d31ea2484d46fd0628970"
@@ -4852,6 +5063,24 @@ postcss-ordered-values@^2.1.0:
     postcss "^5.0.4"
     postcss-value-parser "^3.0.1"
 
+postcss-partial-import@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/postcss-partial-import/-/postcss-partial-import-1.3.0.tgz#2f4b773a76c7b0a69b389dcf475c4d362d0d2576"
+  dependencies:
+    fs-extra "^0.24.0"
+    fs-promise "^0.3.1"
+    object-assign "^4.0.1"
+    postcss "^5.0.5"
+    string-hash "^1.1.0"
+
+postcss-property-lookup@^1.1.3:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/postcss-property-lookup/-/postcss-property-lookup-1.2.1.tgz#30450a1361b7aae758bbedd5201fbe057bb8270b"
+  dependencies:
+    object-assign "^4.0.1"
+    postcss "^5.0.4"
+    tcomb "^2.5.1"
+
 postcss-reduce-idents@^2.2.2:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.3.0.tgz#a697b52953ed6825ffea404e26a4f105d8b8d569"
@@ -4872,6 +5101,34 @@ postcss-reduce-transforms@^1.0.3:
     postcss "^5.0.8"
     postcss-value-parser "^3.0.1"
 
+postcss-sass@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/postcss-sass/-/postcss-sass-0.1.0.tgz#0d2a655b5d241ec8f419bb3da38de5ca11746ddb"
+  dependencies:
+    gonzales-pe "^4.0.3"
+    mathjs "^3.11.5"
+    postcss "^5.2.6"
+
+postcss-scss@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-0.4.1.tgz#ad771b81f0f72f5f4845d08aa60f93557653d54c"
+  dependencies:
+    postcss "^5.2.13"
+
+postcss-selector-matches@^2.0.0:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/postcss-selector-matches/-/postcss-selector-matches-2.0.5.tgz#fa0f43be57b68e77aa4cd11807023492a131027f"
+  dependencies:
+    balanced-match "^0.4.2"
+    postcss "^5.0.0"
+
+postcss-selector-not@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-selector-not/-/postcss-selector-not-2.0.0.tgz#c73ad21a3f75234bee7fee269e154fd6a869798d"
+  dependencies:
+    balanced-match "^0.2.0"
+    postcss "^5.0.0"
+
 postcss-selector-parser@^2.0.0:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.1.tgz#fdbf696103b12b0a64060e5610507f410491f7c8"
@@ -4880,6 +5137,28 @@ postcss-selector-parser@^2.0.0:
     indexes-of "^1.0.1"
     uniq "^1.0.1"
 
+postcss-simple-vars@^1.0.1:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/postcss-simple-vars/-/postcss-simple-vars-1.2.0.tgz#2e6689921144b74114e765353275a3c32143f150"
+  dependencies:
+    postcss "^5.0.13"
+
+postcss-smart-import@^0.6.12:
+  version "0.6.12"
+  resolved "https://registry.yarnpkg.com/postcss-smart-import/-/postcss-smart-import-0.6.12.tgz#6bd50846547383d15332206615265b87b4a6ae89"
+  dependencies:
+    babel-runtime "^6.23.0"
+    lodash "^4.17.4"
+    object-assign "^4.1.1"
+    postcss "^5.2.17"
+    postcss-sass "^0.1.0"
+    postcss-scss "^0.4.1"
+    postcss-value-parser "^3.3.0"
+    promise-each "^2.2.0"
+    read-cache "^1.0.0"
+    resolve "^1.3.3"
+    sugarss "^0.2.0"
+
 postcss-svgo@^2.1.1:
   version "2.1.5"
   resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.5.tgz#46fc0363f01bab6a36a9abb01c229fcc45363094"
@@ -4897,7 +5176,7 @@ postcss-unique-selectors@^2.0.2:
     postcss "^5.0.4"
     uniqs "^2.0.0"
 
-postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.1.3, postcss-value-parser@^3.2.3:
+postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.1.3, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0:
   version "3.3.0"
   resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15"
 
@@ -4908,24 +5187,15 @@ postcss-zindex@^2.0.1:
     postcss "^5.0.4"
     uniqs "^2.0.0"
 
-postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.8, postcss@^5.2.5:
-  version "5.2.15"
-  resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.15.tgz#a9e8685e50e06cc5b3fdea5297273246c26f5b30"
+postcss@^5.0.0, postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.19, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.6, postcss@^5.0.8, postcss@^5.2.13, postcss@^5.2.15, postcss@^5.2.16, postcss@^5.2.17, postcss@^5.2.4, postcss@^5.2.5, postcss@^5.2.6:
+  version "5.2.17"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.17.tgz#cf4f597b864d65c8a492b2eabe9d706c879c388b"
   dependencies:
     chalk "^1.1.3"
     js-base64 "^2.1.9"
     source-map "^0.5.6"
     supports-color "^3.2.3"
 
-postcss@^5.0.6, postcss@^5.2.2:
-  version "5.2.4"
-  resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.4.tgz#8eb4bee3e5c4e091585b116df32d8db24a535f21"
-  dependencies:
-    chalk "^1.1.3"
-    js-base64 "^2.1.9"
-    source-map "^0.5.6"
-    supports-color "^3.1.2"
-
 postgres-array@~1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-1.0.2.tgz#8e0b32eb03bf77a5c0a7851e0441c169a256a238"
@@ -4966,6 +5236,27 @@ precond@0.2:
   version "0.2.3"
   resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac"
 
+precss@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/precss/-/precss-1.4.0.tgz#8d7c3ae70f10a00a3955287f85a66e0f8b31cda3"
+  dependencies:
+    postcss "^5.0.10"
+    postcss-advanced-variables "1.2.2"
+    postcss-atroot "^0.1.2"
+    postcss-color-function "^2.0.0"
+    postcss-custom-media "^5.0.0"
+    postcss-custom-properties "^5.0.0"
+    postcss-custom-selectors "^3.0.0"
+    postcss-extend "^1.0.1"
+    postcss-media-minmax "^2.1.0"
+    postcss-mixins "^2.1.0"
+    postcss-nested "^1.0.0"
+    postcss-nesting "^2.0.6"
+    postcss-partial-import "^1.3.0"
+    postcss-property-lookup "^1.1.3"
+    postcss-selector-matches "^2.0.0"
+    postcss-selector-not "^2.0.0"
+
 prelude-ls@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
@@ -4986,7 +5277,7 @@ process-nextick-args@~1.0.6:
   version "1.0.7"
   resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
 
-process@^0.11.0, process@~0.11.0:
+process@^0.11.0:
   version "0.11.9"
   resolved "https://registry.yarnpkg.com/process/-/process-0.11.9.tgz#7bd5ad21aa6253e7da8682264f1e11d11c0318c1"
 
@@ -4994,6 +5285,12 @@ progress@^1.1.8:
   version "1.1.8"
   resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
 
+promise-each@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/promise-each/-/promise-each-2.2.0.tgz#3353174eff2694481037e04e01f77aa0fb6d1b60"
+  dependencies:
+    any-promise "^0.1.0"
+
 promise@^7.1.1:
   version "7.1.1"
   resolved "https://registry.yarnpkg.com/promise/-/promise-7.1.1.tgz#489654c692616b8aa55b0724fa809bb7db49c5bf"
@@ -5042,7 +5339,7 @@ punycode@1.3.2:
   version "1.3.2"
   resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
 
-punycode@^1.2.4, punycode@^1.3.2, punycode@^1.4.1:
+punycode@^1.2.4, punycode@^1.4.1:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
 
@@ -5050,11 +5347,11 @@ q@^1.1.2:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e"
 
-qs@6.2.0, qs@^6.1.0, qs@^6.2.0, qs@~6.2.0:
+qs@6.2.0, qs@^6.1.0:
   version "6.2.0"
   resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.0.tgz#3b7848c03c2dece69a9522b0fae8c4126d745f3b"
 
-qs@~6.3.0:
+qs@^6.2.0, qs@~6.3.0:
   version "6.3.1"
   resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.1.tgz#918c0b3bcd36679772baf135b1acb4c1651ed79d"
 
@@ -5071,7 +5368,7 @@ query-string@^4.1.0:
     object-assign "^4.1.0"
     strict-uri-encode "^1.0.0"
 
-querystring-es3@^0.2.0, querystring-es3@~0.2.0:
+querystring-es3@^0.2.0:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
 
@@ -5089,6 +5386,13 @@ raf@^3.1.0:
   dependencies:
     performance-now "~0.2.0"
 
+rails-erb-loader@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/rails-erb-loader/-/rails-erb-loader-5.0.0.tgz#33fcfbae3fd4584801283c4d5bc1195ae7b56723"
+  dependencies:
+    loader-utils "^1.1.0"
+    lodash.defaults "^4.2.0"
+
 randomatic@^1.1.3:
   version "1.1.5"
   resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.5.tgz#5e9ef5f2d573c67bd2b8124ae90b5156e457840b"
@@ -5172,7 +5476,7 @@ react-docgen@^2.12.1:
     node-dir "^0.1.10"
     recast "^0.11.5"
 
-react-dom@^15.5.4:
+react-dom@^15.4.2, react-dom@^15.5.4:
   version "15.5.4"
   resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.5.4.tgz#ba0c28786fd52ed7e4f2135fe0288d462aef93da"
   dependencies:
@@ -5208,12 +5512,29 @@ react-immutable-proptypes@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/react-immutable-proptypes/-/react-immutable-proptypes-2.1.0.tgz#023d6f39bb15c97c071e9e60d00d136eac5fa0b4"
 
+react-immutable-pure-component@^0.0.4:
+  version "0.0.4"
+  resolved "https://registry.yarnpkg.com/react-immutable-pure-component/-/react-immutable-pure-component-0.0.4.tgz#a7c438167e5b82b5231e4ec9246827ebe3257629"
+  dependencies:
+    immutable "^3.8.1"
+    react "^15.4.2"
+    react-dom "^15.4.2"
+
 react-inspector@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/react-inspector/-/react-inspector-1.1.0.tgz#8d55bb94ffc9fd3982a222eb257dbe9cdd4f1b87"
   dependencies:
     is-dom "^1.0.5"
 
+react-intl-translations-manager@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/react-intl-translations-manager/-/react-intl-translations-manager-5.0.0.tgz#3c78d3e3e44c5804d7a15c60e89c3aefd9d06615"
+  dependencies:
+    chalk "^1.1.3"
+    glob "^7.0.3"
+    json-stable-stringify "^1.0.1"
+    mkdirp "^0.5.1"
+
 react-intl@^2.1.5:
   version "2.1.5"
   resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-2.1.5.tgz#f9795ea34b790dcb5d0d8ef7060dddbe85bf8763"
@@ -5364,7 +5685,7 @@ react-virtualized@^8.11.4:
     dom-helpers "^2.4.0 || ^3.0.0"
     loose-envify "^1.3.0"
 
-react@^15.5.4:
+react@^15.4.2, react@^15.5.4:
   version "15.5.4"
   resolved "https://registry.yarnpkg.com/react/-/react-15.5.4.tgz#fa83eb01506ab237cdc1c8c3b1cea8de012bf047"
   dependencies:
@@ -5373,11 +5694,11 @@ react@^15.5.4:
     object-assign "^4.1.0"
     prop-types "^15.5.7"
 
-read-only-stream@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/read-only-stream/-/read-only-stream-2.0.0.tgz#2724fd6a8113d73764ac288d4386270c1dbf17f0"
+read-cache@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774"
   dependencies:
-    readable-stream "^2.0.2"
+    pify "^2.3.0"
 
 read-pkg-up@^1.0.1:
   version "1.0.1"
@@ -5572,7 +5893,7 @@ repeating@^2.0.0:
   dependencies:
     is-finite "^1.0.0"
 
-request@2, request@2.x, request@^2.79.0:
+request@2, request@2.x, request@^2.74.0, request@^2.79.0:
   version "2.79.0"
   resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de"
   dependencies:
@@ -5597,32 +5918,6 @@ request@2, request@2.x, request@^2.79.0:
     tunnel-agent "~0.4.1"
     uuid "^3.0.0"
 
-request@^2.74.0:
-  version "2.75.0"
-  resolved "https://registry.yarnpkg.com/request/-/request-2.75.0.tgz#d2b8268a286da13eaa5d01adf5d18cc90f657d93"
-  dependencies:
-    aws-sign2 "~0.6.0"
-    aws4 "^1.2.1"
-    bl "~1.1.2"
-    caseless "~0.11.0"
-    combined-stream "~1.0.5"
-    extend "~3.0.0"
-    forever-agent "~0.6.1"
-    form-data "~2.0.0"
-    har-validator "~2.0.6"
-    hawk "~3.1.3"
-    http-signature "~1.1.0"
-    is-typedarray "~1.0.0"
-    isstream "~0.1.2"
-    json-stringify-safe "~5.0.1"
-    mime-types "~2.1.7"
-    node-uuid "~1.4.7"
-    oauth-sign "~0.8.1"
-    qs "~6.2.0"
-    stringstream "~0.0.4"
-    tough-cookie "~2.3.0"
-    tunnel-agent "~0.4.1"
-
 require-directory@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
@@ -5642,7 +5937,7 @@ require-uncached@^1.0.2:
     caller-path "^0.1.0"
     resolve-from "^1.0.0"
 
-requires-port@1.0.x:
+requires-port@1.0.x, requires-port@1.x.x:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
 
@@ -5654,9 +5949,11 @@ resolve-from@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"
 
-resolve@1.1.7, resolve@^1.1.3, resolve@^1.1.4, resolve@^1.1.6:
-  version "1.1.7"
-  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
+resolve@^1.1.6, resolve@^1.3.3:
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5"
+  dependencies:
+    path-parse "^1.0.5"
 
 restore-cursor@^1.0.1:
   version "1.0.1"
@@ -5665,6 +5962,10 @@ restore-cursor@^1.0.1:
     exit-hook "^1.0.0"
     onetime "^1.0.0"
 
+rgb@~0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/rgb/-/rgb-0.1.0.tgz#be27b291e8feffeac1bd99729721bfa40fc037b5"
+
 right-align@^0.1.1:
   version "0.1.3"
   resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef"
@@ -5707,9 +6008,9 @@ sass-graph@^2.1.1:
     lodash "^4.0.0"
     yargs "^4.7.1"
 
-sass-loader@^6.0.2:
-  version "6.0.2"
-  resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-6.0.2.tgz#96343a9f5c585780149321c7bda9e1da633d2c73"
+sass-loader@^6.0.3:
+  version "6.0.3"
+  resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-6.0.3.tgz#33983b1f90d27ddab0e57d0dac403dce9bc7ecfd"
   dependencies:
     async "^2.1.5"
     clone-deep "^0.2.4"
@@ -5732,6 +6033,14 @@ section-iterator@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/section-iterator/-/section-iterator-2.0.0.tgz#bf444d7afeeb94ad43c39ad2fb26151627ccba2a"
 
+seed-random@2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/seed-random/-/seed-random-2.2.0.tgz#2a9b19e250a817099231a5b99a4daf80b7fbed54"
+
+select-hose@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
+
 "semver@2 || 3 || 4 || 5", semver@~5.3.0:
   version "5.3.0"
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
@@ -5767,6 +6076,18 @@ serve-favicon@^2.3.0:
     ms "0.7.1"
     parseurl "~1.3.0"
 
+serve-index@^1.7.2:
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.8.0.tgz#7c5d96c13fb131101f93c1c5774f8516a1e78d3b"
+  dependencies:
+    accepts "~1.3.3"
+    batch "0.5.3"
+    debug "~2.2.0"
+    escape-html "~1.0.3"
+    http-errors "~1.5.0"
+    mime-types "~2.1.11"
+    parseurl "~1.3.1"
+
 serve-static@~1.11.2:
   version "1.11.2"
   resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.11.2.tgz#2cf9889bd4435a320cc36895c9aa57bd662e6ac7"
@@ -5796,7 +6117,7 @@ sha.js@2.2.6:
   version "2.2.6"
   resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.2.6.tgz#17ddeddc5f722fb66501658895461977867315ba"
 
-sha.js@^2.3.6, sha.js@~2.4.4:
+sha.js@^2.3.6:
   version "2.4.5"
   resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.5.tgz#27d171efcc82a118b99639ff581660242b506e7c"
   dependencies:
@@ -5821,22 +6142,6 @@ shallowequal@0.2.x, shallowequal@^0.2.2:
   dependencies:
     lodash.keys "^3.1.2"
 
-shasum@^1.0.0:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/shasum/-/shasum-1.0.2.tgz#e7012310d8f417f4deb5712150e5678b87ae565f"
-  dependencies:
-    json-stable-stringify "~0.0.0"
-    sha.js "~2.4.4"
-
-shell-quote@^1.6.1:
-  version "1.6.1"
-  resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767"
-  dependencies:
-    array-filter "~0.0.0"
-    array-map "~0.0.0"
-    array-reduce "~0.0.0"
-    jsonify "~0.0.0"
-
 shelljs@^0.7.4:
   version "0.7.4"
   resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.4.tgz#b8f04b3a74ddfafea22acf98e0be45ded53d59c8"
@@ -5892,6 +6197,24 @@ sntp@1.x.x:
   dependencies:
     hoek "2.x.x"
 
+sockjs-client@1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.2.tgz#f0212a8550e4c9468c8cceaeefd2e3493c033ad5"
+  dependencies:
+    debug "^2.2.0"
+    eventsource "0.1.6"
+    faye-websocket "~0.11.0"
+    inherits "^2.0.1"
+    json3 "^3.3.2"
+    url-parse "^1.1.1"
+
+sockjs@0.3.18:
+  version "0.3.18"
+  resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.18.tgz#d9b289316ca7df77595ef299e075f0f937eb4207"
+  dependencies:
+    faye-websocket "^0.10.0"
+    uuid "^2.0.2"
+
 sort-keys@^1.0.0:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad"
@@ -5908,6 +6231,10 @@ source-list-map@^0.1.7, source-list-map@~0.1.7:
   version "0.1.7"
   resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.7.tgz#d4b5ce2a46535c72c7e8527c71a77d250618172e"
 
+source-list-map@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-1.1.1.tgz#1a33ac210ca144d1e561f906ebccab5669ff4cb4"
+
 source-map-support@^0.4.2:
   version "0.4.3"
   resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.3.tgz#693c8383d4389a4569486987c219744dfc601685"
@@ -5944,6 +6271,26 @@ spdx-license-ids@^1.0.2:
   version "1.2.2"
   resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57"
 
+spdy-transport@^2.0.15:
+  version "2.0.18"
+  resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-2.0.18.tgz#43fc9c56be2cccc12bb3e2754aa971154e836ea6"
+  dependencies:
+    debug "^2.2.0"
+    hpack.js "^2.1.6"
+    obuf "^1.1.0"
+    readable-stream "^2.0.1"
+    wbuf "^1.4.0"
+
+spdy@^3.4.1:
+  version "3.4.4"
+  resolved "https://registry.yarnpkg.com/spdy/-/spdy-3.4.4.tgz#e0406407ca90ff01b553eb013505442649f5a819"
+  dependencies:
+    debug "^2.2.0"
+    handle-thing "^1.2.4"
+    http-deceiver "^1.2.4"
+    select-hose "^2.0.0"
+    spdy-transport "^2.0.15"
+
 split@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/split/-/split-1.0.0.tgz#c4395ce683abcd254bc28fe1dabb6e5c27dcffae"
@@ -5983,21 +6330,14 @@ store@^1.3.20:
   version "1.3.20"
   resolved "https://registry.yarnpkg.com/store/-/store-1.3.20.tgz#13ea7e3fb2d6c239868265d686b1d84e99c5be3e"
 
-stream-browserify@^2.0.0, stream-browserify@^2.0.1:
+stream-browserify@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db"
   dependencies:
     inherits "~2.0.1"
     readable-stream "^2.0.2"
 
-stream-combiner2@^1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/stream-combiner2/-/stream-combiner2-1.1.1.tgz#fb4d8a1420ea362764e21ad4780397bebcb41cbe"
-  dependencies:
-    duplexer2 "~0.1.0"
-    readable-stream "^2.0.2"
-
-stream-http@^2.0.0, stream-http@^2.3.1:
+stream-http@^2.3.1:
   version "2.4.0"
   resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.4.0.tgz#9599aa8e263667ce4190e0dc04a1d065d3595a7e"
   dependencies:
@@ -6007,17 +6347,14 @@ stream-http@^2.0.0, stream-http@^2.3.1:
     to-arraybuffer "^1.0.0"
     xtend "^4.0.0"
 
-stream-splicer@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/stream-splicer/-/stream-splicer-2.0.0.tgz#1b63be438a133e4b671cc1935197600175910d83"
-  dependencies:
-    inherits "^2.0.1"
-    readable-stream "^2.0.2"
-
 strict-uri-encode@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
 
+string-hash@^1.1.0:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b"
+
 string-width@^1.0.1, string-width@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
@@ -6049,7 +6386,7 @@ string.prototype.padstart@^3.0.0:
     es-abstract "^1.4.3"
     function-bind "^1.0.2"
 
-string_decoder@^0.10.25, string_decoder@~0.10.0, string_decoder@~0.10.x:
+string_decoder@^0.10.25, string_decoder@~0.10.x:
   version "0.10.31"
   resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
 
@@ -6104,19 +6441,19 @@ style-loader@0.13.1:
   dependencies:
     loader-utils "^0.2.7"
 
-style-loader@^0.13.2:
-  version "0.13.2"
-  resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.13.2.tgz#74533384cf698c7104c7951150b49717adc2f3bb"
+style-loader@^0.16.1:
+  version "0.16.1"
+  resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.16.1.tgz#50e325258d4e78421dd9680636b41e8661595d10"
   dependencies:
     loader-utils "^1.0.2"
 
-subarg@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2"
+sugarss@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/sugarss/-/sugarss-0.2.0.tgz#ac34237563327c6ff897b64742bf6aec190ad39e"
   dependencies:
-    minimist "^1.1.0"
+    postcss "^5.2.4"
 
-supports-color@3.1.2, supports-color@^3.1.0, supports-color@^3.1.2:
+supports-color@3.1.2, supports-color@^3.1.0, supports-color@^3.1.1:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5"
   dependencies:
@@ -6156,12 +6493,6 @@ symbol-tree@^3.2.1:
   version "3.2.2"
   resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
 
-syntax-error@^1.1.1:
-  version "1.1.6"
-  resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.1.6.tgz#b4549706d386cc1c1dc7c2423f18579b6cade710"
-  dependencies:
-    acorn "^2.7.0"
-
 table@^3.7.8:
   version "3.8.3"
   resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f"
@@ -6220,33 +6551,28 @@ tar@^2.0.0, tar@~2.2.0, tar@~2.2.1:
     fstream "^1.0.2"
     inherits "2"
 
+tcomb@^2.5.1:
+  version "2.7.0"
+  resolved "https://registry.yarnpkg.com/tcomb/-/tcomb-2.7.0.tgz#10d62958041669a5d53567b9a4ee8cde22b1c2b0"
+
 text-table@~0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
 
-through2@^2.0.0:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.1.tgz#384e75314d49f32de12eebb8136b8eb6b5d59da9"
-  dependencies:
-    readable-stream "~2.0.0"
-    xtend "~4.0.0"
-
-through@2, "through@>=2.2.7 <3", through@^2.3.6:
+through@2, through@^2.3.6:
   version "2.3.8"
   resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
 
-timers-browserify@^1.0.1:
-  version "1.4.2"
-  resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d"
-  dependencies:
-    process "~0.11.0"
-
 timers-browserify@^2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.2.tgz#ab4883cf597dcd50af211349a00fbca56ac86b86"
   dependencies:
     setimmediate "^1.0.4"
 
+tiny-emitter@1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-1.0.2.tgz#8e49470d3f55f89e247210368a6bb9fb51aa1601"
+
 to-arraybuffer@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
@@ -6281,7 +6607,7 @@ tryit@^1.0.1:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb"
 
-tty-browserify@0.0.0, tty-browserify@~0.0.0:
+tty-browserify@0.0.0:
   version "0.0.0"
   resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
 
@@ -6314,6 +6640,10 @@ type-is@~1.6.14:
     media-typer "0.3.0"
     mime-types "~2.1.13"
 
+typed-function@0.10.5:
+  version "0.10.5"
+  resolved "https://registry.yarnpkg.com/typed-function/-/typed-function-0.10.5.tgz#2e0f18abd065219fab694a446a65c6d1981832c0"
+
 typedarray@~0.0.5:
   version "0.0.6"
   resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
@@ -6322,7 +6652,16 @@ ua-parser-js@^0.7.9:
   version "0.7.10"
   resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.10.tgz#917559ddcce07cbc09ece7d80495e4c268f4ef9f"
 
-uglify-js@^2.7.5, uglify-js@~2.7.3:
+uglify-js@^2.8.5:
+  version "2.8.22"
+  resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.22.tgz#d54934778a8da14903fa29a326fb24c0ab51a1a0"
+  dependencies:
+    source-map "~0.5.1"
+    yargs "~3.10.0"
+  optionalDependencies:
+    uglify-to-browserify "~1.0.0"
+
+uglify-js@~2.7.3:
   version "2.7.5"
   resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.7.5.tgz#4612c0c7baaee2ba7c487de4904ae122079f2ca8"
   dependencies:
@@ -6343,10 +6682,6 @@ ultron@~1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.0.tgz#b07a2e6a541a815fc6a34ccd4533baec307ca864"
 
-umd@^3.0.0:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.1.tgz#8ae556e11011f63c2596708a8837259f01b3d60e"
-
 uniq@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
@@ -6383,7 +6718,14 @@ url-parse@1.0.x:
     querystringify "0.0.x"
     requires-port "1.0.x"
 
-url@^0.11.0, url@~0.11.0:
+url-parse@^1.1.1:
+  version "1.1.8"
+  resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.1.8.tgz#7a65b3a8d57a1e86af6b4e2276e34774167c0156"
+  dependencies:
+    querystringify "0.0.x"
+    requires-port "1.0.x"
+
+url@^0.11.0:
   version "0.11.0"
   resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
   dependencies:
@@ -6412,7 +6754,7 @@ util-deprecate@~1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
 
-util@0.10.3, "util@>=0.10.3 <1", util@^0.10.3, util@~0.10.1:
+util@0.10.3, "util@>=0.10.3 <1", util@^0.10.3:
   version "0.10.3"
   resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
   dependencies:
@@ -6422,7 +6764,7 @@ utils-merge@1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8"
 
-uuid@^2.0.1, uuid@^2.0.3:
+uuid@^2.0.1, uuid@^2.0.2, uuid@^2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
 
@@ -6457,7 +6799,7 @@ verror@1.3.6:
   dependencies:
     extsprintf "1.0.2"
 
-vm-browserify@0.0.4, vm-browserify@~0.0.1:
+vm-browserify@0.0.4:
   version "0.0.4"
   resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73"
   dependencies:
@@ -6483,7 +6825,7 @@ watchpack@^0.2.1:
     chokidar "^1.0.0"
     graceful-fs "^4.1.2"
 
-watchpack@^1.2.0:
+watchpack@^1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.3.1.tgz#7d8693907b28ce6013e7f3610aa2a1acf07dad87"
   dependencies:
@@ -6491,6 +6833,12 @@ watchpack@^1.2.0:
     chokidar "^1.4.3"
     graceful-fs "^4.1.2"
 
+wbuf@^1.1.0, wbuf@^1.4.0:
+  version "1.7.2"
+  resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.2.tgz#d697b99f1f59512df2751be42769c1580b5801fe"
+  dependencies:
+    minimalistic-assert "^1.0.0"
+
 webidl-conversions@^3.0.0:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
@@ -6506,6 +6854,15 @@ webpack-core@~0.6.9:
     source-list-map "~0.1.7"
     source-map "~0.4.1"
 
+webpack-dev-middleware@^1.10.2:
+  version "1.10.2"
+  resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.10.2.tgz#2e252ce1dfb020dbda1ccb37df26f30ab014dbd1"
+  dependencies:
+    memory-fs "~0.4.1"
+    mime "^1.3.4"
+    path-is-absolute "^1.0.0"
+    range-parser "^1.0.3"
+
 webpack-dev-middleware@^1.6.0:
   version "1.8.4"
   resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.8.4.tgz#e8765c9122887ce9e3abd4cc9c3eb31b61e0948d"
@@ -6515,6 +6872,28 @@ webpack-dev-middleware@^1.6.0:
     path-is-absolute "^1.0.0"
     range-parser "^1.0.3"
 
+webpack-dev-server@^2.4.5:
+  version "2.4.5"
+  resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-2.4.5.tgz#31384ce81136be1080b4b4cde0eb9b90e54ee6cf"
+  dependencies:
+    ansi-html "0.0.7"
+    chokidar "^1.6.0"
+    compression "^1.5.2"
+    connect-history-api-fallback "^1.3.0"
+    express "^4.13.3"
+    html-entities "^1.2.0"
+    http-proxy-middleware "~0.17.4"
+    opn "4.0.2"
+    portfinder "^1.0.9"
+    serve-index "^1.7.2"
+    sockjs "0.3.18"
+    sockjs-client "1.1.2"
+    spdy "^3.4.1"
+    strip-ansi "^3.0.0"
+    supports-color "^3.1.1"
+    webpack-dev-middleware "^1.10.2"
+    yargs "^6.0.0"
+
 webpack-hot-middleware@^2.13.2:
   version "2.17.1"
   resolved "https://registry.yarnpkg.com/webpack-hot-middleware/-/webpack-hot-middleware-2.17.1.tgz#0c8fbf6f93ff29c095d684b07ab6d6c0f2f951d7"
@@ -6524,13 +6903,33 @@ webpack-hot-middleware@^2.13.2:
     querystring "^0.2.0"
     strip-ansi "^3.0.0"
 
-webpack-sources@^0.1.4:
-  version "0.1.4"
-  resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-0.1.4.tgz#ccc2c817e08e5fa393239412690bb481821393cd"
+webpack-manifest-plugin@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/webpack-manifest-plugin/-/webpack-manifest-plugin-1.1.0.tgz#6b6c718aade8a2537995784b46bd2e9836057caa"
+  dependencies:
+    fs-extra "^0.30.0"
+    lodash ">=3.5 <5"
+
+webpack-merge@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.1.0.tgz#6ad72223b3e0b837e531e4597c199f909361511e"
+  dependencies:
+    lodash "^4.17.4"
+
+webpack-sources@^0.1.0:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-0.1.5.tgz#aa1f3abf0f0d74db7111c40e500b84f966640750"
   dependencies:
     source-list-map "~0.1.7"
     source-map "~0.5.3"
 
+webpack-sources@^0.2.3:
+  version "0.2.3"
+  resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-0.2.3.tgz#17c62bfaf13c707f9d02c479e0dcdde8380697fb"
+  dependencies:
+    source-list-map "^1.1.1"
+    source-map "~0.5.3"
+
 webpack@^1.13.1:
   version "1.14.0"
   resolved "https://registry.yarnpkg.com/webpack/-/webpack-1.14.0.tgz#54f1ffb92051a328a5b2057d6ae33c289462c823"
@@ -6551,11 +6950,11 @@ webpack@^1.13.1:
     watchpack "^0.2.1"
     webpack-core "~0.6.9"
 
-webpack@^2.2.1:
-  version "2.2.1"
-  resolved "https://registry.yarnpkg.com/webpack/-/webpack-2.2.1.tgz#7bb1d72ae2087dd1a4af526afec15eed17dda475"
+webpack@^2.4.1:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/webpack/-/webpack-2.4.1.tgz#15a91dbe34966d8a4b99c7d656efd92a2e5a6f6a"
   dependencies:
-    acorn "^4.0.4"
+    acorn "^5.0.0"
     acorn-dynamic-import "^2.0.0"
     ajv "^4.7.0"
     ajv-keywords "^1.1.1"
@@ -6563,6 +6962,7 @@ webpack@^2.2.1:
     enhanced-resolve "^3.0.0"
     interpret "^1.0.0"
     json-loader "^0.5.4"
+    json5 "^0.5.1"
     loader-runner "^2.3.0"
     loader-utils "^0.2.16"
     memory-fs "~0.4.1"
@@ -6571,11 +6971,21 @@ webpack@^2.2.1:
     source-map "^0.5.3"
     supports-color "^3.1.0"
     tapable "~0.2.5"
-    uglify-js "^2.7.5"
-    watchpack "^1.2.0"
-    webpack-sources "^0.1.4"
+    uglify-js "^2.8.5"
+    watchpack "^1.3.1"
+    webpack-sources "^0.2.3"
     yargs "^6.0.0"
 
+websocket-driver@>=0.5.1:
+  version "0.6.5"
+  resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36"
+  dependencies:
+    websocket-extensions ">=0.1.1"
+
+websocket-extensions@>=0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.1.tgz#76899499c184b6ef754377c2dbb0cd6cb55d29e7"
+
 websocket.js@^0.1.7:
   version "0.1.7"
   resolved "https://registry.yarnpkg.com/websocket.js/-/websocket.js-0.1.7.tgz#8d24cefb1a080c259e7e4740c02cab8f142df2b0"
@@ -6680,7 +7090,7 @@ xml-name-validator@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635"
 
-xtend@4.0.1, xtend@^4.0.0, xtend@~4.0.0:
+xtend@4.0.1, xtend@^4.0.0:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"