From 25ae55a640ed827f11e31858e13683099b82a3b2 Mon Sep 17 00:00:00 2001
From: Greg Fodor <gfodor@gmail.com>
Date: Mon, 19 Mar 2018 18:12:55 -0700
Subject: [PATCH] Add i18n support

---
 .babelrc                                   |   3 +
 package.json                               |   5 +-
 src/assets/{ => images}/account.svg        |   0
 src/assets/images/desktop_screen_entry.svg |  24 +++++
 src/assets/images/mobile_screen_entry.svg  |  20 +++++
 src/assets/translations.data.json          |   9 ++
 src/react-components/name-entry-panel.js   |  14 ++-
 src/react-components/ui-root.js            |  39 ++++++--
 webpack.config.js                          |   2 +
 yarn.lock                                  | 100 +++++++++++++--------
 10 files changed, 167 insertions(+), 49 deletions(-)
 rename src/assets/{ => images}/account.svg (100%)
 create mode 100755 src/assets/images/desktop_screen_entry.svg
 create mode 100755 src/assets/images/mobile_screen_entry.svg
 create mode 100644 src/assets/translations.data.json

diff --git a/.babelrc b/.babelrc
index 88f0c0ac5..2af3fcf4a 100644
--- a/.babelrc
+++ b/.babelrc
@@ -5,5 +5,8 @@
       "exclude": ["transform-regenerator"],
       "useBuiltins": true
     }]
+  ],
+  "plugins": [
+    [ "react-intl", { "messagesDir": "./public/messages", "enforceDescriptions": false } ]
   ]
 }
diff --git a/package.json b/package.json
index b5e610ad3..f79c4e4c4 100644
--- a/package.json
+++ b/package.json
@@ -21,12 +21,14 @@
     "aframe-physics-system": "https://github.com/donmccurdy/aframe-physics-system",
     "aframe-teleport-controls": "https://github.com/netpro2k/aframe-teleport-controls#feature/teleport-origin",
     "aframe-xr": "github:brianpeiris/aframe-xr#3162aed",
+    "babel-plugin-react-intl": "^2.4.0",
     "classnames": "^2.2.5",
     "detect-browser": "^2.1.0",
     "event-target-shim": "^3.0.1",
     "jsonschema": "^1.2.2",
     "material-design-lite": "^1.3.0",
     "minijanus": "^0.5.0",
+    "mobile-detect": "^1.4.1",
     "naf-janus-adapter": "https://github.com/mozilla/naf-janus-adapter#feature/disconnect",
     "networked-aframe": "https://github.com/mozillareality/networked-aframe#mr-social-client/master",
     "nipplejs": "^0.6.7",
@@ -34,9 +36,10 @@
     "raven-js": "^3.20.1",
     "react": "^16.1.1",
     "react-dom": "^16.1.1",
+    "react-intl": "^2.4.0",
     "react-router-dom": "^4.2.2",
-    "uuid": "^3.2.1",
     "super-hands": "https://github.com/infinitelee/aframe-super-hands-component#mr-social-client/master",
+    "uuid": "^3.2.1",
     "webrtc-adapter": "^6.0.2"
   },
   "devDependencies": {
diff --git a/src/assets/account.svg b/src/assets/images/account.svg
similarity index 100%
rename from src/assets/account.svg
rename to src/assets/images/account.svg
diff --git a/src/assets/images/desktop_screen_entry.svg b/src/assets/images/desktop_screen_entry.svg
new file mode 100755
index 000000000..fbc322893
--- /dev/null
+++ b/src/assets/images/desktop_screen_entry.svg
@@ -0,0 +1,24 @@
+<svg width="94" height="94" viewBox="0 0 94 94" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<title>desktop_screen_entry</title>
+<desc>Created using Figma</desc>
+<g id="Canvas" transform="translate(-659 -277)">
+<g id="desktop_screen_entry">
+<g id="Oval Copy">
+<use xlink:href="#path0_fill" transform="translate(661 279)" fill="#D8D8D8" fill-opacity="0.01"/>
+<use xlink:href="#path1_stroke" transform="translate(661 279)" fill="#FFFFFF"/>
+</g>
+<g id="Rectangle 3">
+<use xlink:href="#path2_fill" transform="translate(696 341)" fill="#FFFFFF"/>
+</g>
+<g id="desktop">
+<use xlink:href="#path3_fill" transform="translate(685 307)" fill="#FFFFFF"/>
+</g>
+</g>
+</g>
+<defs>
+<path id="path0_fill" fill-rule="evenodd" d="M 45 90C 69.8528 90 90 69.8528 90 45C 90 20.1472 69.8528 0 45 0C 20.1472 0 0 20.1472 0 45C 0 69.8528 20.1472 90 45 90Z"/>
+<path id="path1_stroke" d="M 45 91.5C 70.6812 91.5 91.5 70.6812 91.5 45L 88.5 45C 88.5 69.0244 69.0244 88.5 45 88.5L 45 91.5ZM 91.5 45C 91.5 19.3188 70.6812 -1.5 45 -1.5L 45 1.5C 69.0244 1.5 88.5 20.9756 88.5 45L 91.5 45ZM 45 -1.5C 19.3188 -1.5 -1.5 19.3188 -1.5 45L 1.5 45C 1.5 20.9756 20.9756 1.5 45 1.5L 45 -1.5ZM -1.5 45C -1.5 70.6812 19.3188 91.5 45 91.5L 45 88.5C 20.9756 88.5 1.5 69.0244 1.5 45L -1.5 45Z"/>
+<path id="path2_fill" d="M 0 0L 20 0L 20 3L 0 3L 0 0Z"/>
+<path id="path3_fill" fill-rule="evenodd" d="M 3 0C 1.34314 0 0 1.34314 0 3L 0 31C 0 32.6569 1.34314 34 3 34L 38 34C 39.6569 34 41 32.6569 41 31L 41 3C 41 1.34314 39.6569 0 38 0L 3 0ZM 36 6L 5 6L 5 28L 36 28L 36 6Z"/>
+</defs>
+</svg>
diff --git a/src/assets/images/mobile_screen_entry.svg b/src/assets/images/mobile_screen_entry.svg
new file mode 100755
index 000000000..8c249bfd8
--- /dev/null
+++ b/src/assets/images/mobile_screen_entry.svg
@@ -0,0 +1,20 @@
+<svg width="94" height="94" viewBox="0 0 94 94" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<title>mobile_screen_entry</title>
+<desc>Created using Figma</desc>
+<g id="Canvas" transform="translate(-659 -277)">
+<g id="mobile_screen_entry">
+<g id="Oval Copy">
+<use xlink:href="#path0_fill" transform="translate(661 279)" fill="#D8D8D8" fill-opacity="0.01"/>
+<use xlink:href="#path1_stroke" transform="translate(661 279)" fill="#FFFFFF"/>
+</g>
+<g id="Phone Copy">
+<use xlink:href="#path2_fill" transform="translate(693 305)" fill="#FFFFFF"/>
+</g>
+</g>
+</g>
+<defs>
+<path id="path0_fill" fill-rule="evenodd" d="M 45 90C 69.8528 90 90 69.8528 90 45C 90 20.1472 69.8528 0 45 0C 20.1472 0 0 20.1472 0 45C 0 69.8528 20.1472 90 45 90Z"/>
+<path id="path1_stroke" d="M 45 91.5C 70.6812 91.5 91.5 70.6812 91.5 45L 88.5 45C 88.5 69.0244 69.0244 88.5 45 88.5L 45 91.5ZM 91.5 45C 91.5 19.3188 70.6812 -1.5 45 -1.5L 45 1.5C 69.0244 1.5 88.5 20.9756 88.5 45L 91.5 45ZM 45 -1.5C 19.3188 -1.5 -1.5 19.3188 -1.5 45L 1.5 45C 1.5 20.9756 20.9756 1.5 45 1.5L 45 -1.5ZM -1.5 45C -1.5 70.6812 19.3188 91.5 45 91.5L 45 88.5C 20.9756 88.5 1.5 69.0244 1.5 45L -1.5 45Z"/>
+<path id="path2_fill" fill-rule="evenodd" d="M 5 0C 2.23859 0 0 2.23859 0 5L 0 33C 0 35.7614 2.23859 38 5 38L 20 38C 22.7614 38 25 35.7614 25 33L 25 5C 25 2.23859 22.7614 0 20 0L 5 0ZM 22 4L 3 4L 3 29L 22 29L 22 4Z"/>
+</defs>
+</svg>
diff --git a/src/assets/translations.data.json b/src/assets/translations.data.json
new file mode 100644
index 000000000..d776b89e3
--- /dev/null
+++ b/src/assets/translations.data.json
@@ -0,0 +1,9 @@
+{
+  "en":
+  {
+    "entry.screen": "Enter on this screen",
+    "profile.save": "SAVE",
+    "profile.display_name.validation_warning": "Alphanumerics and hyphens. At least 3 characters, no more than 32",
+    "profile.header": "Your identity"
+  }
+}
diff --git a/src/react-components/name-entry-panel.js b/src/react-components/name-entry-panel.js
index 44bb7b220..c61658e2d 100644
--- a/src/react-components/name-entry-panel.js
+++ b/src/react-components/name-entry-panel.js
@@ -1,10 +1,12 @@
 import React, { Component } from 'react';
 import PropTypes from 'prop-types';
+import { injectIntl, FormattedMessage } from 'react-intl';
 import { SCHEMA } from "../storage/store";
 
-export default class NameEntryPanel extends Component {
+class NameEntryPanel extends Component {
   static propTypes = {
     store: PropTypes.object,
+    messages: PropTypes.object,
     finished: PropTypes.func
   }
 
@@ -37,23 +39,27 @@ export default class NameEntryPanel extends Component {
   }
 
   render () {
+    const { formatMessage } = this.props.intl;
+
     return (
       <div className="name-entry">
         <form onSubmit={this.saveName}>
         <div className="name-entry__box name-entry__box--darkened">
           <div className="name-entry__subtitle">
-            Your identity
+            <FormattedMessage id="profile.header"/>
           </div>
           <input
             className="name-entry__form-field-text"
             value={this.state.name} onChange={(e) => this.setState({name: e.target.value})}
             required pattern={SCHEMA.definitions.profile.properties.display_name.pattern}
-            title="Alphanumerics and hyphens. At least 3 characters, no more than 32"
+            title={formatMessage({ id: "profile.display_name.validation_warning" })}
             ref={inp => this.nameInput = inp}/>
-          <input className="name-entry__form-submit" type="submit" value="SAVE" />
+          <input className="name-entry__form-submit" type="submit" value={formatMessage({ id: "profile.save" }) }/>
           </div>
         </form>
       </div>
     );
   }
 }
+
+export default injectIntl(NameEntryPanel);
diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js
index e43e089ce..1c7cf99f0 100644
--- a/src/react-components/ui-root.js
+++ b/src/react-components/ui-root.js
@@ -5,9 +5,20 @@ import NameEntryPanel from './name-entry-panel';
 import { VR_DEVICE_AVAILABILITY } from "../utils/vr-caps-detect";
 import queryString from "query-string";
 import { SCHEMA } from "../storage/store";
-const { detect } = require("detect-browser");
+import MobileDetect from 'mobile-detect';
+import { IntlProvider, FormattedMessage, addLocaleData } from 'react-intl';
+import en from 'react-intl/locale-data/en';
 
-const browser = detect();
+const mobiledetect = new MobileDetect(navigator.userAgent);
+
+const lang = ((navigator.languages && navigator.languages[0]) ||
+                   navigator.language || navigator.userLanguage).toLowerCase().split(/[_-]+/)[0];
+
+import localeData from '../assets/translations.data.json';
+console.log(localeData);
+addLocaleData([...en]);
+
+const messages = localeData[lang] || localeData.en;
 
 const ENTRY_STEPS = {
   start: "start",
@@ -27,11 +38,15 @@ async function hasGrantedMicPermissions() {
   return micLabels.length > 0;
 }
 
-const TwoDEntryButton = (props) => (
-  <button {...props}>
-    Enter on this Screen
-  </button>
-);
+const TwoDEntryButton = (props) => {
+  const iconSrc = mobiledetect.mobile() ? 
+    "./src/assets/images/mobile_screen_entry.svg" : "./src/assets/images/desktop_screen_entry.svg";
+
+  return (<div className="entry-button" {...props}>
+    <img src={iconSrc} className="entry-button__icon"/>
+    <FormattedMessage id="entry.screen"/>
+  </div>);
+}
 
 const GenericEntryButton = (props) => (
   <button {...props}>
@@ -65,7 +80,7 @@ const AutoExitWarning = (props) => (
 
 const ProfileInfoHeader = (props) => (
   <div className="profile-info-header">
-    <img src="./src/assets/account.svg" onClick={props.onClick} className="profile-info-header__icon"/>
+    <img src="./src/assets/images/account.svg" onClick={props.onClick} className="profile-info-header__icon"/>
     <div className="profile-info-header__profile_display_name" onClick={props.onClick}>
       {props.name}
     </div>
@@ -440,7 +455,7 @@ class UIRoot extends Component {
       'ui-dialog-box-contents--backgrounded': this.state.showProfileEntry
     });
 
-    return !this.state.exited ?
+    const content = !this.state.exited ?
       (
         <div className={dialogClassNames}>
           {
@@ -461,6 +476,12 @@ class UIRoot extends Component {
       (
         <div>Exited</div>
       )
+
+    return (
+      <IntlProvider locale={lang} messages={messages}>
+        {content}
+      </IntlProvider>
+    );
   }
 }
 
diff --git a/webpack.config.js b/webpack.config.js
index 3fd0ea732..4996b8925 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -147,6 +147,7 @@ const config = {
             {
               loader: "css-loader",
               options: {
+                name: "[path][name]-[hash].[ext]",
                 minimize: process.env.NODE_ENV === "production"
               }
             },
@@ -161,6 +162,7 @@ const config = {
           use: {
             loader: "css-loader",
             options: {
+              name: "[path][name]-[hash].[ext]",
               minimize: process.env.NODE_ENV === "production"
             }
           }
diff --git a/yarn.lock b/yarn.lock
index 8a575dc6c..553764888 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1,6 +1,7 @@
 # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
 # yarn lockfile v1
 
+
 "@babel/code-frame@7.0.0-beta.40", "@babel/code-frame@^7.0.0-beta.40":
   version "7.0.0-beta.40"
   resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.40.tgz#37e2b0cf7c56026b4b21d3927cadf81adec32ac6"
@@ -122,14 +123,10 @@ acorn@^4.0.3:
   version "4.0.13"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
 
-acorn@^5.0.0, acorn@^5.4.0:
+acorn@^5.0.0, acorn@^5.2.1, acorn@^5.4.0, acorn@^5.4.1:
   version "5.4.1"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.4.1.tgz#fdc58d9d17f4a4e98d102ded826a9b9759125102"
 
-acorn@^5.2.1, acorn@^5.4.1:
-  version "5.5.3"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9"
-
 aframe-billboard-component@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/aframe-billboard-component/-/aframe-billboard-component-1.0.0.tgz#10ce2482729eef7386c5844d65917581a62d3adc"
@@ -687,6 +684,14 @@ babel-plugin-check-es2015-constants@^6.22.0:
   dependencies:
     babel-runtime "^6.22.0"
 
+babel-plugin-react-intl@^2.4.0:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-react-intl/-/babel-plugin-react-intl-2.4.0.tgz#292fca8030603a9e0476973290836aa0c7da17e2"
+  dependencies:
+    babel-runtime "^6.2.0"
+    intl-messageformat-parser "^1.2.0"
+    mkdirp "^0.5.1"
+
 babel-plugin-syntax-async-functions@^6.8.0:
   version "6.13.0"
   resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95"
@@ -1136,7 +1141,7 @@ babel-register@^6.26.0, babel-register@^6.9.0:
     mkdirp "^0.5.1"
     source-map-support "^0.4.15"
 
-babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0:
+babel-runtime@^6.18.0, babel-runtime@^6.2.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0:
   version "6.26.0"
   resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
   dependencies:
@@ -2047,7 +2052,7 @@ 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.0, concat-stream@^1.6.0:
+concat-stream@^1.4.7, concat-stream@^1.5.0, concat-stream@^1.6.0, concat-stream@~1.6.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7"
   dependencies:
@@ -2063,14 +2068,6 @@ concat-stream@~1.5.0, concat-stream@~1.5.1:
     readable-stream "~2.0.0"
     typedarray "~0.0.5"
 
-concat-stream@~1.6.0:
-  version "1.6.1"
-  resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.1.tgz#261b8f518301f1d834e36342b9fea095d2620a26"
-  dependencies:
-    inherits "^2.0.3"
-    readable-stream "^2.2.2"
-    typedarray "^0.0.6"
-
 connect-history-api-fallback@^1.3.0:
   version "1.5.0"
   resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz#b06873934bc5e344fef611a196a6faae0aee015a"
@@ -3499,7 +3496,7 @@ glob@^6.0.1, glob@^6.0.4:
     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.0, glob@^7.1.2:
+glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.0, glob@^7.1.2, glob@~7.1.1:
   version "7.1.2"
   resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
   dependencies:
@@ -4062,7 +4059,27 @@ interpret@^1.0.0, interpret@^1.0.4:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614"
 
-invariant@^2.2.0, invariant@^2.2.1, invariant@^2.2.2:
+intl-format-cache@^2.0.5:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/intl-format-cache/-/intl-format-cache-2.1.0.tgz#04a369fecbfad6da6005bae1f14333332dcf9316"
+
+intl-messageformat-parser@1.4.0, intl-messageformat-parser@^1.2.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/intl-messageformat-parser/-/intl-messageformat-parser-1.4.0.tgz#b43d45a97468cadbe44331d74bb1e8dea44fc075"
+
+intl-messageformat@^2.0.0, intl-messageformat@^2.1.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-2.2.0.tgz#345bcd46de630b7683330c2e52177ff5eab484fc"
+  dependencies:
+    intl-messageformat-parser "1.4.0"
+
+intl-relativeformat@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/intl-relativeformat/-/intl-relativeformat-2.1.0.tgz#010f1105802251f40ac47d0e3e1a201348a255df"
+  dependencies:
+    intl-messageformat "^2.0.0"
+
+invariant@^2.1.1, invariant@^2.2.0, invariant@^2.2.1, invariant@^2.2.2:
   version "2.2.3"
   resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.3.tgz#1a827dfde7dcbd7c323f0ca826be8fa7c5e9d688"
   dependencies:
@@ -4496,6 +4513,10 @@ jsonify@~0.0.0:
   version "0.0.0"
   resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
 
+jsonparse@^1.2.0:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
+
 jsonpointer@^4.0.0:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9"
@@ -4504,10 +4525,6 @@ jsonschema@^1.2.2:
   version "1.2.2"
   resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.2.2.tgz#83ab9c63d65bf4d596f91d81195e78772f6452bc"
 
-jsonparse@^1.2.0:
-  version "1.3.1"
-  resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
-
 jsprim@^1.2.2:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
@@ -4702,14 +4719,14 @@ lodash.memoize@^4.1.2:
   version "4.1.2"
   resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
 
-lodash.mergewith@^4.6.0:
-  version "4.6.1"
-  resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927"
-
 lodash.memoize@~3.0.3:
   version "3.0.4"
   resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f"
 
+lodash.mergewith@^4.6.0:
+  version "4.6.1"
+  resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927"
+
 lodash.startswith@^4.2.1:
   version "4.2.1"
   resolved "https://registry.yarnpkg.com/lodash.startswith/-/lodash.startswith-4.2.1.tgz#c598c4adce188a27e53145731cdc6c0e7177600c"
@@ -5041,6 +5058,10 @@ mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkd
   dependencies:
     minimist "0.0.8"
 
+mobile-detect@^1.4.1:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/mobile-detect/-/mobile-detect-1.4.1.tgz#f4b67c49bb84bf0437f72e3067deb1c60ad7b23c"
+
 module-deps@^4.0.8:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-4.1.1.tgz#23215833f1da13fd606ccb8087b44852dcb821fd"
@@ -6294,6 +6315,15 @@ react-dom@^16.1.1:
     object-assign "^4.1.1"
     prop-types "^15.6.0"
 
+react-intl@^2.4.0:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-2.4.0.tgz#66c14dc9df9a73b2fbbfbd6021726e80a613eb15"
+  dependencies:
+    intl-format-cache "^2.0.5"
+    intl-messageformat "^2.1.0"
+    intl-relativeformat "^2.0.0"
+    invariant "^2.1.1"
+
 react-router-dom@^4.2.2:
   version "4.2.2"
   resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.2.2.tgz#c8a81df3adc58bba8a76782e946cbd4eae649b8d"
@@ -7149,7 +7179,7 @@ source-map-url@^0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
 
-source-map@0.5.x, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.0, source-map@~0.5.3:
+source-map@0.5.x, source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.0, source-map@~0.5.3:
   version "0.5.7"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
 
@@ -7254,21 +7284,21 @@ static-extend@^0.1.1:
     define-property "^0.2.5"
     object-copy "^0.1.0"
 
-"statuses@>= 1.3.1 < 2", statuses@~1.4.0:
-  version "1.4.0"
-  resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
-
-statuses@~1.3.1:
+"statuses@>= 1.3.1 < 2", statuses@~1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e"
 
+statuses@~1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
+
 stdout-stream@^1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.0.tgz#a2c7c8587e54d9427ea9edb3ac3f2cd522df378b"
   dependencies:
     readable-stream "^2.0.1"
 
-stream-browserify@^2.0.1:
+stream-browserify@^2.0.0, stream-browserify@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db"
   dependencies:
@@ -7654,11 +7684,11 @@ trim-right@^1.0.1:
   dependencies:
     glob "^6.0.4"
 
-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"
 
-tty-browserify@0.0.1, tty-browserify@~0.0.0:
+tty-browserify@0.0.1:
   version "0.0.1"
   resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811"
 
@@ -8196,7 +8226,7 @@ which-module@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
 
-which@^1.2.14, which@^1.2.4, which@^1.2.9:
+which@1, which@^1.2.14, which@^1.2.4, which@^1.2.9:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a"
   dependencies:
-- 
GitLab