fix: make intl.formatMessage more resilient (#155)

* fix: shim injectIntl hoc to prevent errors with undefined
* refactor: add i18n-loader exports to i18n/index
* fix: add NewRelic logging
This commit is contained in:
Adam Butterworth
2019-04-12 17:28:51 -04:00
committed by GitHub
parent 8ff7c822f3
commit ca66f20233
19 changed files with 91 additions and 16 deletions

View File

@@ -12,5 +12,8 @@ module.exports = {
},
resolve: {
extensions: ['.js', '.jsx'],
alias: {
'@edx/frontend-i18n': path.resolve(__dirname, '../src/i18n/'),
},
},
};

View File

@@ -125,7 +125,8 @@
"moduleNameMapper": {
"\\.svg": "<rootDir>/__mocks__/svgrMock.js",
"\\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
"\\.(css|scss)$": "identity-obj-proxy"
"\\.(css|scss)$": "identity-obj-proxy",
"@edx/frontend-i18n(.*)$": "<rootDir>/src/i18n$1"
},
"collectCoverageFrom": [
"src/**/*.{js,jsx}"

View File

@@ -9,7 +9,7 @@ import SiteFooter from '@edx/frontend-component-footer';
import { fetchUserAccount, UserAccountApiService } from '@edx/frontend-auth';
import apiClient from '../config/apiClient';
import { getLocale, getMessages } from '../i18n/i18n-loader';
import { getLocale, getMessages } from '@edx/frontend-i18n'; // eslint-disable-line
import SiteHeader from './common/SiteHeader';
import ConnectedProfilePage from './ProfilePage';

View File

@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { StatusAlert, Hyperlink } from '@edx/paragon';
import { connect } from 'react-redux';
import { injectIntl, intlShape } from 'react-intl';
import { injectIntl, intlShape } from '@edx/frontend-i18n'; // eslint-disable-line
// Analytics
import { sendTrackingLogEvent } from '@edx/frontend-analytics';

View File

@@ -1,7 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { injectIntl, intlShape, FormattedMessage } from 'react-intl';
import { FormattedMessage } from 'react-intl';
import { injectIntl, intlShape } from '@edx/frontend-i18n'; // eslint-disable-line
import { ValidationFormGroup } from '@edx/paragon';
import messages from './Bio.messages';

View File

@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape, FormattedDate, FormattedMessage } from 'react-intl';
import { FormattedDate, FormattedMessage } from 'react-intl';
import { injectIntl, intlShape } from '@edx/frontend-i18n'; // eslint-disable-line
import { Hyperlink } from '@edx/paragon';
import { connect } from 'react-redux';
import get from 'lodash.get';

View File

@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { injectIntl, intlShape } from 'react-intl';
import { injectIntl, intlShape } from '@edx/frontend-i18n'; // eslint-disable-line
import { ValidationFormGroup } from '@edx/paragon';
import messages from './Country.messages';

View File

@@ -1,7 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
import { FormattedMessage } from 'react-intl';
import { injectIntl, intlShape } from '@edx/frontend-i18n'; // eslint-disable-line
import get from 'lodash.get';
import { ValidationFormGroup } from '@edx/paragon';

View File

@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { injectIntl, intlShape } from 'react-intl';
import { injectIntl, intlShape } from '@edx/frontend-i18n'; // eslint-disable-line
import messages from './Name.messages';

View File

@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { injectIntl, intlShape } from 'react-intl';
import { injectIntl, intlShape } from '@edx/frontend-i18n'; // eslint-disable-line
import { ValidationFormGroup } from '@edx/paragon';
import messages from './PreferredLanguage.messages';

View File

@@ -1,7 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Dropdown } from '@edx/paragon';
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
import { FormattedMessage } from 'react-intl';
import { injectIntl, intlShape } from '@edx/frontend-i18n'; // eslint-disable-line
import { ReactComponent as DefaultAvatar } from '../../assets/avatar.svg';

View File

@@ -4,7 +4,8 @@ import { StatusAlert } from '@edx/paragon';
import { connect } from 'react-redux';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faTwitter, faFacebook, faLinkedin } from '@fortawesome/free-brands-svg-icons';
import { injectIntl, intlShape, FormattedMessage } from 'react-intl';
import { FormattedMessage } from 'react-intl';
import { injectIntl, intlShape } from '@edx/frontend-i18n'; // eslint-disable-line
import classNames from 'classnames';
import messages from './SocialLinks.messages';

View File

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import classNames from 'classnames';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faPencilAlt } from '@fortawesome/free-solid-svg-icons';
import { injectIntl, intlShape } from 'react-intl';
import { injectIntl, intlShape } from '@edx/frontend-i18n'; // eslint-disable-line
import { Button } from '@edx/paragon';
import messages from './EditButton.messages';

View File

@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, StatefulButton } from '@edx/paragon';
import { injectIntl, intlShape } from 'react-intl';
import { injectIntl, intlShape } from '@edx/frontend-i18n'; // eslint-disable-line
import messages from './FormControls.messages';

View File

@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from 'react-intl';
import { injectIntl, intlShape } from '@edx/frontend-i18n'; // eslint-disable-line
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faEyeSlash, faEye } from '@fortawesome/free-regular-svg-icons';

View File

@@ -1,6 +1,6 @@
import { connect } from 'react-redux';
import SiteHeader from '@edx/frontend-component-site-header';
import { injectIntl } from 'react-intl';
import { injectIntl } from '@edx/frontend-i18n'; // eslint-disable-line
import messages from './SiteHeader.messages';

25
src/i18n/index.jsx Normal file
View File

@@ -0,0 +1,25 @@
import { intlShape } from 'react-intl';
import injectIntlWithShim from './injectIntlWithShim';
import {
getCountryList,
getCountryMessages,
getLanguageList,
getLanguageMessages,
getLocale,
getMessages,
handleRtl,
isRtl,
} from './i18n-loader';
export {
injectIntlWithShim as injectIntl,
getCountryList,
getCountryMessages,
getLanguageList,
getLanguageMessages,
getLocale,
getMessages,
handleRtl,
isRtl,
intlShape,
};

View File

@@ -0,0 +1,41 @@
import React from 'react';
import { injectIntl, intlShape } from 'react-intl';
import LoggingService from '@edx/frontend-logging';
const injectIntlWithShim = (WrappedComponent) => {
class ShimmedIntlComponent extends React.Component {
static propTypes = {
intl: intlShape.isRequired,
};
constructor(props) {
super(props);
this.shimmedIntl = Object.create(this.props.intl, {
formatMessage: {
value: (definition) => {
if (definition === undefined || definition.id === undefined) {
const error = new Error('i18n error: An undefined message was supplied to intl.formatMessage.');
if (process.env.NODE_ENV !== 'production') {
console.error(error); // eslint-disable-line no-console
return '!!! Missing message supplied to intl.formatMessage !!!';
}
LoggingService.logError(error);
return ''; // Fail silent in production
}
return this.props.intl.formatMessage(definition);
},
},
});
}
render() {
return <WrappedComponent {...this.props} intl={this.shimmedIntl} />;
}
}
return injectIntl(ShimmedIntlComponent);
};
export default injectIntlWithShim;

View File

@@ -1,5 +1,5 @@
import { createSelector } from 'reselect';
import { getLocale, getLanguageList, getCountryList, getCountryMessages, getLanguageMessages } from '../i18n/i18n-loader';
import { getLocale, getLanguageList, getCountryList, getCountryMessages, getLanguageMessages } from '@edx/frontend-i18n'; // eslint-disable-line
export const formIdSelector = (state, props) => props.formId;
export const userAccountSelector = state => state.userAccount;