You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
95 lines
4.5 KiB
95 lines
4.5 KiB
/*
|
|
* Copyright 2015, Yahoo Inc.
|
|
* Copyrights licensed under the New BSD License.
|
|
* See the accompanying LICENSE file for terms.
|
|
*/
|
|
import * as React from 'react';
|
|
import { invariant } from '@formatjs/intl-utils';
|
|
import { createError, escape } from '../utils';
|
|
import IntlMessageFormat from 'intl-messageformat';
|
|
function setTimeZoneInOptions(opts, timeZone) {
|
|
return Object.keys(opts).reduce((all, k) => {
|
|
all[k] = Object.assign({ timeZone }, opts[k]);
|
|
return all;
|
|
}, {});
|
|
}
|
|
function deepMergeOptions(opts1, opts2) {
|
|
const keys = Object.keys(Object.assign(Object.assign({}, opts1), opts2));
|
|
return keys.reduce((all, k) => {
|
|
all[k] = Object.assign(Object.assign({}, (opts1[k] || {})), (opts2[k] || {}));
|
|
return all;
|
|
}, {});
|
|
}
|
|
function deepMergeFormatsAndSetTimeZone(f1, timeZone) {
|
|
if (!timeZone) {
|
|
return f1;
|
|
}
|
|
const mfFormats = IntlMessageFormat.formats;
|
|
return Object.assign(Object.assign(Object.assign({}, mfFormats), f1), { date: deepMergeOptions(setTimeZoneInOptions(mfFormats.date, timeZone), setTimeZoneInOptions(f1.date || {}, timeZone)), time: deepMergeOptions(setTimeZoneInOptions(mfFormats.time, timeZone), setTimeZoneInOptions(f1.time || {}, timeZone)) });
|
|
}
|
|
export const prepareIntlMessageFormatHtmlOutput = (chunks) => React.createElement(React.Fragment, null, ...chunks);
|
|
export function formatMessage({ locale, formats, messages, defaultLocale, defaultFormats, onError, timeZone, }, state, messageDescriptor = { id: '' }, values = {}) {
|
|
const { id, defaultMessage } = messageDescriptor;
|
|
// `id` is a required field of a Message Descriptor.
|
|
invariant(!!id, '[React Intl] An `id` must be provided to format a message.');
|
|
const message = messages && messages[String(id)];
|
|
formats = deepMergeFormatsAndSetTimeZone(formats, timeZone);
|
|
defaultFormats = deepMergeFormatsAndSetTimeZone(defaultFormats, timeZone);
|
|
let formattedMessageParts = [];
|
|
if (message) {
|
|
try {
|
|
const formatter = state.getMessageFormat(message, locale, formats, {
|
|
formatters: state,
|
|
});
|
|
formattedMessageParts = formatter.formatHTMLMessage(values);
|
|
}
|
|
catch (e) {
|
|
onError(createError(`Error formatting message: "${id}" for locale: "${locale}"` +
|
|
(defaultMessage ? ', using default message as fallback.' : ''), e));
|
|
}
|
|
}
|
|
else {
|
|
// This prevents warnings from littering the console in development
|
|
// when no `messages` are passed into the <IntlProvider> for the
|
|
// default locale, and a default message is in the source.
|
|
if (!defaultMessage ||
|
|
(locale && locale.toLowerCase() !== defaultLocale.toLowerCase())) {
|
|
onError(createError(`Missing message: "${id}" for locale: "${locale}"` +
|
|
(defaultMessage ? ', using default message as fallback.' : '')));
|
|
}
|
|
}
|
|
if (!formattedMessageParts.length && defaultMessage) {
|
|
try {
|
|
const formatter = state.getMessageFormat(defaultMessage, defaultLocale, defaultFormats);
|
|
formattedMessageParts = formatter.formatHTMLMessage(values);
|
|
}
|
|
catch (e) {
|
|
onError(createError(`Error formatting the default message for: "${id}"`, e));
|
|
}
|
|
}
|
|
if (!formattedMessageParts.length) {
|
|
onError(createError(`Cannot format message: "${id}", ` +
|
|
`using message ${message || defaultMessage ? 'source' : 'id'} as fallback.`));
|
|
if (typeof message === 'string') {
|
|
return message || defaultMessage || String(id);
|
|
}
|
|
return defaultMessage || String(id);
|
|
}
|
|
if (formattedMessageParts.length === 1 &&
|
|
typeof formattedMessageParts[0] === 'string') {
|
|
return formattedMessageParts[0] || defaultMessage || String(id);
|
|
}
|
|
return prepareIntlMessageFormatHtmlOutput(formattedMessageParts);
|
|
}
|
|
export function formatHTMLMessage(config, state, messageDescriptor = { id: '' }, rawValues = {}) {
|
|
// Process all the values before they are used when formatting the ICU
|
|
// Message string. Since the formatted message might be injected via
|
|
// `innerHTML`, all String-based values need to be HTML-escaped.
|
|
const escapedValues = Object.keys(rawValues).reduce((escaped, name) => {
|
|
const value = rawValues[name];
|
|
escaped[name] = typeof value === 'string' ? escape(value) : value;
|
|
return escaped;
|
|
}, {});
|
|
return formatMessage(config, state, messageDescriptor, escapedValues);
|
|
}
|