diff --git a/src/containers/gui.jsx b/src/containers/gui.jsx
index 8a89fb1324c..ffc38e07f9f 100644
--- a/src/containers/gui.jsx
+++ b/src/containers/gui.jsx
@@ -4,7 +4,7 @@ import {compose} from 'redux';
import {connect} from 'react-redux';
import ReactModal from 'react-modal';
import VM from 'scratch-vm';
-import {injectIntl, intlShape} from 'react-intl';
+import {defineMessages, injectIntl, intlShape} from 'react-intl';
import ErrorBoundaryHOC from '../lib/error-boundary-hoc.jsx';
import {openExtensionLibrary} from '../reducers/modals';
@@ -25,14 +25,22 @@ import {
} from '../reducers/modals';
import FontLoaderHOC from '../lib/font-loader-hoc.jsx';
+import LocalizationHOC from '../lib/localization-hoc.jsx';
import ProjectFetcherHOC from '../lib/project-fetcher-hoc.jsx';
import ProjectSaverHOC from '../lib/project-saver-hoc.jsx';
import vmListenerHOC from '../lib/vm-listener-hoc.jsx';
import vmManagerHOC from '../lib/vm-manager-hoc.jsx';
-import {defaultProjectTitleMessages} from '../reducers/project-title';
import GUIComponent from '../components/gui/gui.jsx';
+const messages = defineMessages({
+ defaultProjectTitle: {
+ id: 'gui.gui.defaultProjectTitle',
+ description: 'Default title for project',
+ defaultMessage: 'Scratch Project'
+ }
+});
+
class GUI extends React.Component {
componentDidMount () {
this.setReduxTitle(this.props.projectTitle);
@@ -48,7 +56,7 @@ class GUI extends React.Component {
setReduxTitle (newTitle) {
if (newTitle === null || typeof newTitle === 'undefined') {
this.props.onUpdateReduxProjectTitle(
- this.props.intl.formatMessage(defaultProjectTitleMessages.defaultProjectTitle)
+ this.props.intl.formatMessage(messages.defaultProjectTitle)
);
} else {
this.props.onUpdateReduxProjectTitle(newTitle);
@@ -163,6 +171,7 @@ const ConnectedGUI = injectIntl(connect(
// the hierarchy of HOC constructor calls clearer here; it has nothing to do with redux's
// ability to compose reducers.
const WrappedGui = compose(
+ LocalizationHOC,
ErrorBoundaryHOC('Top Level App'),
FontLoaderHOC,
ProjectFetcherHOC,
diff --git a/src/lib/app-state-hoc.jsx b/src/lib/app-state-hoc.jsx
index 93d97180ef3..3c9eb9266d1 100644
--- a/src/lib/app-state-hoc.jsx
+++ b/src/lib/app-state-hoc.jsx
@@ -2,7 +2,6 @@ import React from 'react';
import PropTypes from 'prop-types';
import {Provider} from 'react-redux';
import {createStore, combineReducers, compose} from 'redux';
-import ConnectedIntlProvider from './connected-intl-provider.jsx';
import localesReducer, {initLocale, localesInitialState} from '../reducers/locales';
@@ -108,9 +107,7 @@ const AppStateHOC = function (WrappedComponent, localesOnly) {
} = this.props;
return (
-
-
-
+
);
}
diff --git a/src/lib/localization-hoc.jsx b/src/lib/localization-hoc.jsx
new file mode 100644
index 00000000000..2df5158e838
--- /dev/null
+++ b/src/lib/localization-hoc.jsx
@@ -0,0 +1,55 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {connect} from 'react-redux';
+
+import ConnectedIntlProvider from './connected-intl-provider.jsx';
+
+/*
+ * Higher Order Component to provide localiztion state. Creates a nested IntlProvider
+ * to handle Gui intl context. The component accepts an onSetLanguage callback that is
+ * called when the locale chagnes.
+ * @param {React.Component} WrappedComponent - component to provide state for
+ * @returns {React.Component} component with intl state provided from redux
+ */
+const LocalizationHOC = function (WrappedComponent) {
+ class LocalizationWrapper extends React.Component {
+ componentDidUpdate (prevProps) {
+ if (prevProps.locale !== this.props.locale) {
+ this.props.onSetLanguage(this.props.locale);
+ }
+ }
+ render () {
+ const {
+ locale, // eslint-disable-line no-unused-vars
+ onSetLanguage, // eslint-disable-line no-unused-vars
+ ...componentProps
+ } = this.props;
+ return (
+
+
+
+ );
+ }
+ }
+ LocalizationWrapper.propTypes = {
+ locale: PropTypes.string,
+ onSetLanguage: PropTypes.func
+ };
+
+ LocalizationWrapper.defaultProps = {
+ onSetLanguage: () => {}
+ };
+
+ const mapStateToProps = state => ({
+ locale: state.locales.locale
+ });
+
+ const mapDispatchToProps = () => ({});
+
+ return connect(
+ mapStateToProps,
+ mapDispatchToProps
+ )(LocalizationWrapper);
+};
+
+export default LocalizationHOC;
diff --git a/src/lib/titled-hoc.jsx b/src/lib/titled-hoc.jsx
index 2cc3e59209e..fea6a9388a8 100644
--- a/src/lib/titled-hoc.jsx
+++ b/src/lib/titled-hoc.jsx
@@ -1,7 +1,5 @@
import React from 'react';
import bindAll from 'lodash.bindall';
-import {intlShape, injectIntl} from 'react-intl';
-import {defaultProjectTitleMessages} from '../reducers/project-title';
/* Higher Order Component to get and set the project title
* @param {React.Component} WrappedComponent component to receive project title related props
@@ -15,34 +13,24 @@ const TitledHOC = function (WrappedComponent) {
'handleUpdateProjectTitle'
]);
this.state = {
- projectTitle: this.props.intl.formatMessage(defaultProjectTitleMessages.defaultProjectTitle)
+ projectTitle: null
};
}
handleUpdateProjectTitle (newTitle) {
this.setState({projectTitle: newTitle});
}
render () {
- const {
- /* eslint-disable no-unused-vars */
- intl,
- /* eslint-enable no-unused-vars */
- ...componentProps
- } = this.props;
return (
);
}
}
- TitledComponent.propTypes = {
- intl: intlShape.isRequired
- };
-
- return injectIntl(TitledComponent);
+ return TitledComponent;
};
export {
diff --git a/src/reducers/project-title.js b/src/reducers/project-title.js
index 389687ceb10..09bf6c4eab4 100644
--- a/src/reducers/project-title.js
+++ b/src/reducers/project-title.js
@@ -1,19 +1,9 @@
-import {defineMessages} from 'react-intl';
-
const SET_PROJECT_TITLE = 'projectTitle/SET_PROJECT_TITLE';
// we are initializing to a blank string instead of an actual title,
// because it would be hard to localize here
const initialState = '';
-const defaultProjectTitleMessages = defineMessages({
- defaultProjectTitle: {
- id: 'gui.gui.defaultProjectTitle',
- description: 'Default title for project',
- defaultMessage: 'Scratch Project'
- }
-});
-
const reducer = function (state, action) {
if (typeof state === 'undefined') state = initialState;
switch (action.type) {
@@ -31,6 +21,5 @@ const setProjectTitle = title => ({
export {
reducer as default,
initialState as projectTitleInitialState,
- defaultProjectTitleMessages,
setProjectTitle
};