/* * Copyright 2015, Yahoo Inc. * Copyrights licensed under the New BSD License. * See the accompanying LICENSE file for terms. */ import * as React from 'react'; import { Provider } from './injectIntl'; import { DEFAULT_INTL_CONFIG, createFormatters, invariantIntlContext, createIntlCache, } from '../utils'; import { formatNumber, formatNumberToParts } from '../formatters/number'; import { formatRelativeTime } from '../formatters/relativeTime'; import { formatDate, formatTime, formatDateToParts, formatTimeToParts, } from '../formatters/dateTime'; import { formatPlural } from '../formatters/plural'; import { formatMessage } from '../formatters/message'; import * as shallowEquals_ from 'shallow-equal/objects'; import { formatList } from '../formatters/list'; import { formatDisplayName } from '../formatters/displayName'; import { ReactIntlError } from '../error'; const shallowEquals = shallowEquals_.default || shallowEquals_; function processIntlConfig(config) { return { locale: config.locale, timeZone: config.timeZone, formats: config.formats, textComponent: config.textComponent, messages: config.messages, defaultLocale: config.defaultLocale, defaultFormats: config.defaultFormats, onError: config.onError, }; } /** * Create intl object * @param config intl config * @param cache cache for formatter instances to prevent memory leak */ export function createIntl(config, cache) { const formatters = createFormatters(cache); const resolvedConfig = Object.assign(Object.assign({}, DEFAULT_INTL_CONFIG), config); const { locale, defaultLocale, onError } = resolvedConfig; if (!locale) { if (onError) { onError(new ReactIntlError("INVALID_CONFIG" /* INVALID_CONFIG */, `"locale" was not configured, using "${defaultLocale}" as fallback. See https://github.com/formatjs/react-intl/blob/master/docs/API.md#intlshape for more details`)); } // Since there's no registered locale data for `locale`, this will // fallback to the `defaultLocale` to make sure things can render. // The `messages` are overridden to the `defaultProps` empty object // to maintain referential equality across re-renders. It's assumed // each contains a `defaultMessage` prop. resolvedConfig.locale = resolvedConfig.defaultLocale || 'en'; } else if (!Intl.NumberFormat.supportedLocalesOf(locale).length && onError) { onError(new ReactIntlError("MISSING_DATA" /* MISSING_DATA */, `Missing locale data for locale: "${locale}" in Intl.NumberFormat. Using default locale: "${defaultLocale}" as fallback. See https://github.com/formatjs/react-intl/blob/master/docs/Getting-Started.md#runtime-requirements for more details`)); } else if (!Intl.DateTimeFormat.supportedLocalesOf(locale).length && onError) { onError(new ReactIntlError("MISSING_DATA" /* MISSING_DATA */, `Missing locale data for locale: "${locale}" in Intl.DateTimeFormat. Using default locale: "${defaultLocale}" as fallback. See https://github.com/formatjs/react-intl/blob/master/docs/Getting-Started.md#runtime-requirements for more details`)); } return Object.assign(Object.assign({}, resolvedConfig), { formatters, formatNumber: formatNumber.bind(null, resolvedConfig, formatters.getNumberFormat), formatNumberToParts: formatNumberToParts.bind(null, resolvedConfig, formatters.getNumberFormat), formatRelativeTime: formatRelativeTime.bind(null, resolvedConfig, formatters.getRelativeTimeFormat), formatDate: formatDate.bind(null, resolvedConfig, formatters.getDateTimeFormat), formatDateToParts: formatDateToParts.bind(null, resolvedConfig, formatters.getDateTimeFormat), formatTime: formatTime.bind(null, resolvedConfig, formatters.getDateTimeFormat), formatTimeToParts: formatTimeToParts.bind(null, resolvedConfig, formatters.getDateTimeFormat), formatPlural: formatPlural.bind(null, resolvedConfig, formatters.getPluralRules), formatMessage: formatMessage.bind(null, resolvedConfig, formatters), formatList: formatList.bind(null, resolvedConfig, formatters.getListFormat), formatDisplayName: formatDisplayName.bind(null, resolvedConfig, formatters.getDisplayNames) }); } export default class IntlProvider extends React.PureComponent { constructor() { super(...arguments); this.cache = createIntlCache(); this.state = { cache: this.cache, intl: createIntl(processIntlConfig(this.props), this.cache), prevConfig: processIntlConfig(this.props), }; } static getDerivedStateFromProps(props, { prevConfig, cache }) { const config = processIntlConfig(props); if (!shallowEquals(prevConfig, config)) { return { intl: createIntl(config, cache), prevConfig: config, }; } return null; } render() { invariantIntlContext(this.state.intl); return React.createElement(Provider, { value: this.state.intl }, this.props.children); } } IntlProvider.displayName = 'IntlProvider'; IntlProvider.defaultProps = DEFAULT_INTL_CONFIG;