UNPKG

intl-messageformat

Version:

Formats ICU Message strings with number, date, plural, and select placeholders to create localized messages.

183 lines (182 loc) 7.45 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PART_TYPE = void 0; exports.isFormatXMLElementFn = isFormatXMLElementFn; exports.formatToParts = formatToParts; var icu_messageformat_parser_1 = require("@formatjs/icu-messageformat-parser"); var error_1 = require("./error"); var PART_TYPE; (function (PART_TYPE) { PART_TYPE[PART_TYPE["literal"] = 0] = "literal"; PART_TYPE[PART_TYPE["object"] = 1] = "object"; })(PART_TYPE || (exports.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; }, []); } function isFormatXMLElementFn(el) { return typeof el === 'function'; } // TODO(skeleton): add skeleton support function formatToParts(els, locales, formatters, formats, values, currentPluralValue, // For debugging originalMessage) { // Hot path for straight simple msg translations if (els.length === 1 && (0, icu_messageformat_parser_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 ((0, icu_messageformat_parser_1.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 ((0, icu_messageformat_parser_1.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 error_1.MissingValueError(varName, originalMessage); } var value = values[varName]; if ((0, icu_messageformat_parser_1.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 ((0, icu_messageformat_parser_1.isDateElement)(el)) { var style = typeof el.style === 'string' ? formats.date[el.style] : (0, icu_messageformat_parser_1.isDateTimeSkeleton)(el.style) ? el.style.parsedOptions : undefined; result.push({ type: PART_TYPE.literal, value: formatters .getDateTimeFormat(locales, style) .format(value), }); continue; } if ((0, icu_messageformat_parser_1.isTimeElement)(el)) { var style = typeof el.style === 'string' ? formats.time[el.style] : (0, icu_messageformat_parser_1.isDateTimeSkeleton)(el.style) ? el.style.parsedOptions : formats.time.medium; result.push({ type: PART_TYPE.literal, value: formatters .getDateTimeFormat(locales, style) .format(value), }); continue; } if ((0, icu_messageformat_parser_1.isNumberElement)(el)) { var style = typeof el.style === 'string' ? formats.number[el.style] : (0, icu_messageformat_parser_1.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 ((0, icu_messageformat_parser_1.isTagElement)(el)) { var children = el.children, value_1 = el.value; var formatFn = values[value_1]; if (!isFormatXMLElementFn(formatFn)) { throw new error_1.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 ((0, icu_messageformat_parser_1.isSelectElement)(el)) { var opt = el.options[value] || el.options.other; if (!opt) { throw new error_1.InvalidValueError(el.value, value, Object.keys(el.options), originalMessage); } result.push.apply(result, formatToParts(opt.value, locales, formatters, formats, values)); continue; } if ((0, icu_messageformat_parser_1.isPluralElement)(el)) { var opt = el.options["=".concat(value)]; if (!opt) { if (!Intl.PluralRules) { throw new error_1.FormatError("Intl.PluralRules is not available in this environment.\nTry polyfilling it using \"@formatjs/intl-pluralrules\"\n", error_1.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 error_1.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); }