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:
@@ -12,5 +12,8 @@ module.exports = {
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.jsx'],
|
||||
alias: {
|
||||
'@edx/frontend-i18n': path.resolve(__dirname, '../src/i18n/'),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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
25
src/i18n/index.jsx
Normal 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,
|
||||
};
|
||||
41
src/i18n/injectIntlWithShim.jsx
Normal file
41
src/i18n/injectIntlWithShim.jsx
Normal 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;
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user