178 lines
6.9 KiB
Plaintext
Executable File
178 lines
6.9 KiB
Plaintext
Executable File
import { isArgumentElement, isDateElement, isDateTimeSkeleton, isLiteralElement, isNumberElement, isNumberSkeleton, isPluralElement, isPoundElement, isSelectElement, isTimeElement, isTagElement, } from '@formatjs/icu-messageformat-parser';
|
|
import { MissingValueError, InvalidValueError, ErrorCode, FormatError, InvalidValueTypeError, } from './error';
|
|
export var PART_TYPE;
|
|
(function (PART_TYPE) {
|
|
PART_TYPE[PART_TYPE["literal"] = 0] = "literal";
|
|
PART_TYPE[PART_TYPE["object"] = 1] = "object";
|
|
})(PART_TYPE || (PART_TYPE = {}));
|
|
function mergeLiteral(parts) {
|
|
if (parts.length < 2) {
|
|
return parts;
|
|
}
|
|
return parts.reduce(function (all, part) {
|
|
var lastPart = all[all.length - 1];
|
|
if (!lastPart ||
|
|
lastPart.type !== PART_TYPE.literal ||
|
|
part.type !== PART_TYPE.literal) {
|
|
all.push(part);
|
|
}
|
|
else {
|
|
lastPart.value += part.value;
|
|
}
|
|
return all;
|
|
}, []);
|
|
}
|
|
export function isFormatXMLElementFn(el) {
|
|
return typeof el === 'function';
|
|
}
|
|
// TODO(skeleton): add skeleton support
|
|
export function formatToParts(els, locales, formatters, formats, values, currentPluralValue,
|
|
// For debugging
|
|
originalMessage) {
|
|
// Hot path for straight simple msg translations
|
|
if (els.length === 1 && isLiteralElement(els[0])) {
|
|
return [
|
|
{
|
|
type: PART_TYPE.literal,
|
|
value: els[0].value,
|
|
},
|
|
];
|
|
}
|
|
var result = [];
|
|
for (var _i = 0, els_1 = els; _i < els_1.length; _i++) {
|
|
var el = els_1[_i];
|
|
// Exit early for string parts.
|
|
if (isLiteralElement(el)) {
|
|
result.push({
|
|
type: PART_TYPE.literal,
|
|
value: el.value,
|
|
});
|
|
continue;
|
|
}
|
|
// TODO: should this part be literal type?
|
|
// Replace `#` in plural rules with the actual numeric value.
|
|
if (isPoundElement(el)) {
|
|
if (typeof currentPluralValue === 'number') {
|
|
result.push({
|
|
type: PART_TYPE.literal,
|
|
value: formatters.getNumberFormat(locales).format(currentPluralValue),
|
|
});
|
|
}
|
|
continue;
|
|
}
|
|
var varName = el.value;
|
|
// Enforce that all required values are provided by the caller.
|
|
if (!(values && varName in values)) {
|
|
throw new MissingValueError(varName, originalMessage);
|
|
}
|
|
var value = values[varName];
|
|
if (isArgumentElement(el)) {
|
|
if (!value || typeof value === 'string' || typeof value === 'number') {
|
|
value =
|
|
typeof value === 'string' || typeof value === 'number'
|
|
? String(value)
|
|
: '';
|
|
}
|
|
result.push({
|
|
type: typeof value === 'string' ? PART_TYPE.literal : PART_TYPE.object,
|
|
value: value,
|
|
});
|
|
continue;
|
|
}
|
|
// Recursively format plural and select parts' option — which can be a
|
|
// nested pattern structure. The choosing of the option to use is
|
|
// abstracted-by and delegated-to the part helper object.
|
|
if (isDateElement(el)) {
|
|
var style = typeof el.style === 'string'
|
|
? formats.date[el.style]
|
|
: isDateTimeSkeleton(el.style)
|
|
? el.style.parsedOptions
|
|
: undefined;
|
|
result.push({
|
|
type: PART_TYPE.literal,
|
|
value: formatters
|
|
.getDateTimeFormat(locales, style)
|
|
.format(value),
|
|
});
|
|
continue;
|
|
}
|
|
if (isTimeElement(el)) {
|
|
var style = typeof el.style === 'string'
|
|
? formats.time[el.style]
|
|
: isDateTimeSkeleton(el.style)
|
|
? el.style.parsedOptions
|
|
: formats.time.medium;
|
|
result.push({
|
|
type: PART_TYPE.literal,
|
|
value: formatters
|
|
.getDateTimeFormat(locales, style)
|
|
.format(value),
|
|
});
|
|
continue;
|
|
}
|
|
if (isNumberElement(el)) {
|
|
var style = typeof el.style === 'string'
|
|
? formats.number[el.style]
|
|
: isNumberSkeleton(el.style)
|
|
? el.style.parsedOptions
|
|
: undefined;
|
|
if (style && style.scale) {
|
|
value =
|
|
value *
|
|
(style.scale || 1);
|
|
}
|
|
result.push({
|
|
type: PART_TYPE.literal,
|
|
value: formatters
|
|
.getNumberFormat(locales, style)
|
|
.format(value),
|
|
});
|
|
continue;
|
|
}
|
|
if (isTagElement(el)) {
|
|
var children = el.children, value_1 = el.value;
|
|
var formatFn = values[value_1];
|
|
if (!isFormatXMLElementFn(formatFn)) {
|
|
throw new InvalidValueTypeError(value_1, 'function', originalMessage);
|
|
}
|
|
var parts = formatToParts(children, locales, formatters, formats, values, currentPluralValue);
|
|
var chunks = formatFn(parts.map(function (p) { return p.value; }));
|
|
if (!Array.isArray(chunks)) {
|
|
chunks = [chunks];
|
|
}
|
|
result.push.apply(result, chunks.map(function (c) {
|
|
return {
|
|
type: typeof c === 'string' ? PART_TYPE.literal : PART_TYPE.object,
|
|
value: c,
|
|
};
|
|
}));
|
|
}
|
|
if (isSelectElement(el)) {
|
|
var opt = el.options[value] || el.options.other;
|
|
if (!opt) {
|
|
throw new InvalidValueError(el.value, value, Object.keys(el.options), originalMessage);
|
|
}
|
|
result.push.apply(result, formatToParts(opt.value, locales, formatters, formats, values));
|
|
continue;
|
|
}
|
|
if (isPluralElement(el)) {
|
|
var opt = el.options["=".concat(value)];
|
|
if (!opt) {
|
|
if (!Intl.PluralRules) {
|
|
throw new FormatError("Intl.PluralRules is not available in this environment.\nTry polyfilling it using \"@formatjs/intl-pluralrules\"\n", ErrorCode.MISSING_INTL_API, originalMessage);
|
|
}
|
|
var rule = formatters
|
|
.getPluralRules(locales, { type: el.pluralType })
|
|
.select(value - (el.offset || 0));
|
|
opt = el.options[rule] || el.options.other;
|
|
}
|
|
if (!opt) {
|
|
throw new InvalidValueError(el.value, value, Object.keys(el.options), originalMessage);
|
|
}
|
|
result.push.apply(result, formatToParts(opt.value, locales, formatters, formats, values, value - (el.offset || 0)));
|
|
continue;
|
|
}
|
|
}
|
|
return mergeLiteral(result);
|
|
}
|