import React from 'react';
import PropTypes from 'prop-types';
import {
getMessage,
hasMessage,
postProcessMessage,
preProcessMessage,
} from '../util/locale/messageFormatUtil';
/**
* Provides a localization function getLocale to child components
*/
class LocaleProvider extends React.Component {
constructor(props) {
super(props);
this.getMessage = this.getMessage.bind(this);
this.hasMessage = this.hasMessage.bind(this);
this.messages = null;
this.state = {
localeReady: true,
};
this.initMessages();
}
getChildContext() {
return {
getMessage: this.getMessage,
hasMessage: this.hasMessage,
};
}
/**
* Gets a locale message formatted by MessageFormat
*
* Additional processing is enabled in this function that allows for more advanced formatting.
* You can now pass objects (jsx) or functions as param values, which makes the return value JSX
* wrapped into a <span>.
*
* When using functions, you can wrap a piece of copy between two variable placeholders, which
* will be passed to that function as argument.
*
* @param id The id of the message to get
* @param params Parameters to pass to the MessageFormat compiler for this message
*
* @example
*
* foo {date} bar
* getMessage('foo.bar', { date: <time>2017</time> });
*
* foo {link}http://www.google.com/{_link}
* getMessage('foo.bar', { link: link => <a href={link}>{link}</a> });
*
* click {link}http://www.google.com/|here{_link}
* getMessage('foo.bar', { link: (url, text) => <a href={url}>{text}</a> });
*/
getMessage(id, params = {}) {
let message = id;
const { updatedParams, ...preProcessValues } = preProcessMessage(params);
try {
message = getMessage(this.messages, id, updatedParams);
return postProcessMessage(message, preProcessValues);
} catch (e) {
console.error(`Error getting locale message: ${e.message}`); // eslint-disable-line no-console
}
return message;
}
/**
* Checks if a message in the given MessageFormat messages bundle exists
* @param id The id of the message to get
* @returns {boolean} true when the message exists
*/
hasMessage(id) {
return hasMessage(this.messages, id);
}
/**
* Loads the locale from the getMessages function passed in props.
* Supports both a synchronous and asynchronous return value from getMessages.
* If the getMessages function returns a promise, we will set localeReady on this
* component's state to false so child components do not get rendered until the
* locale has completed loading.
*
* When loading is complete, calls the onLocaleReady callback on props, if
* it is provided.
*/
initMessages() {
const getMessages = this.props.getMessages();
if (typeof getMessages.then === 'function') {
// getMessages is async.
this.state({
localeReady: false,
});
getMessages.then(messages => {
this.messages = messages;
if (this.props.onLocaleReady) {
this.props.onLocaleReady(this.messages);
}
this.state({
localeReady: true,
});
});
} else {
// getMessages is synchronous
this.messages = getMessages;
if (this.props.onLocaleReady) {
this.props.onLocaleReady(this.messages);
}
}
}
render() {
return this.state.localeReady ? this.props.children : <div className="loading-locales" />;
}
}
LocaleProvider.propTypes = {
children: PropTypes.node,
/* Function that retrieves the messages for a given locale id */
getMessages: PropTypes.func.isRequired,
/* callback function that will be called with the loaded locale definitions when ready */
onLocaleReady: PropTypes.func,
};
LocaleProvider.childContextTypes = {
getMessage: PropTypes.func.isRequired,
hasMessage: PropTypes.func.isRequired,
};
export default LocaleProvider;