UNPKG

@hi18n/core

Version:

Message internationalization meets immutability and type-safety - core runtime

208 lines (166 loc) 7.29 kB
import { defaultErrorHandler } from "./error-handling.mjs"; import { ArgumentTypeError, MessageEvaluationError, MissingArgumentError } from "./errors.mjs"; export function evaluateMessage(msg, options, numberValue) { var _msg$argType; if (typeof msg === "string") { return msg; } else if (Array.isArray(msg)) { const reduced = reduceSubmessages(msg.map(part => evaluateMessage(part, options, numberValue))); if (typeof reduced === "string") { return reduced; } const { collect } = options; if (!collect) throw new MessageEvaluationError("Invalid message: not a default-collectable message"); return collect(reduced); } else if (msg.type === "Var") { var _options$params; const value = ((_options$params = options.params) !== null && _options$params !== void 0 ? _options$params : {})[msg.name]; if (value === undefined) throw new MissingArgumentError({ argName: msg.name }); switch (msg.argType) { case undefined: if (typeof value !== "string") throw new MessageEvaluationError( // eslint-disable-next-line @typescript-eslint/restrict-template-expressions "Invalid argument ".concat(msg.name, ": expected string, got ").concat(value)); return value; case "number": { if (typeof value !== "number" && typeof value !== "bigint") { throw new ArgumentTypeError({ argName: msg.name, expectedType: "number", got: value }); } const formatOptions = {}; let modifiedValue = value; switch (msg.argStyle) { case "integer": // https://github.com/openjdk/jdk/blob/739769c8fc4b496f08a92225a12d07414537b6c0/src/java.base/share/classes/sun/util/locale/provider/NumberFormatProviderImpl.java#L196-L198 formatOptions.maximumFractionDigits = 0; if (typeof value === "number") modifiedValue = Math.round(value); break; case "currency": // Need to provide an appropriate currency from somewhere throw new Error("Unimplemented: argStyle=currency"); case "percent": formatOptions.style = msg.argStyle; break; } if ((typeof Intl === "undefined" || !Intl.NumberFormat) && (msg.argStyle === undefined || msg.argStyle === "integer")) { var _options$handleError; ((_options$handleError = options.handleError) !== null && _options$handleError !== void 0 ? _options$handleError : defaultErrorHandler)(new Error("Missing Intl.NumberFormat"), "warn"); return "".concat(modifiedValue); } // TODO: allow injecting polyfill return new Intl.NumberFormat(options.locale, formatOptions).format(modifiedValue); } case "date": case "time": { if (!isDateLike(value)) { throw new ArgumentTypeError({ argName: msg.name, expectedType: "Date", got: value }); } if (typeof options.timeZone !== "string") { throw new MissingArgumentError({ argName: "timeZone" }); } const formatOptions = { timeZone: options.timeZone }; if (typeof msg.argStyle === "object") { // parsed object from the skeleton Object.assign(formatOptions, msg.argStyle); } else { if (msg.argType === "date") { var _msg$argStyle; formatOptions.dateStyle = (_msg$argStyle = msg.argStyle) !== null && _msg$argStyle !== void 0 ? _msg$argStyle : "medium"; } else { var _msg$argStyle2; formatOptions.timeStyle = (_msg$argStyle2 = msg.argStyle) !== null && _msg$argStyle2 !== void 0 ? _msg$argStyle2 : "medium"; } } // TODO: allow injecting polyfill return new Intl.DateTimeFormat(options.locale, formatOptions).format(value); } default: throw new Error("Unimplemented: argType=".concat((_msg$argType = msg.argType) !== null && _msg$argType !== void 0 ? _msg$argType : "string")); } } else if (msg.type === "Plural") { var _options$params2; const value = ((_options$params2 = options.params) !== null && _options$params2 !== void 0 ? _options$params2 : {})[msg.name]; let relativeValue; if (value === undefined) { throw new MissingArgumentError({ argName: msg.name }); } if (typeof value === "number") { var _msg$offset; relativeValue = value - ((_msg$offset = msg.offset) !== null && _msg$offset !== void 0 ? _msg$offset : 0); } else if (typeof value === "bigint") { var _msg$offset2; relativeValue = value - BigInt((_msg$offset2 = msg.offset) !== null && _msg$offset2 !== void 0 ? _msg$offset2 : 0); } else { throw new ArgumentTypeError({ argName: msg.name, expectedType: "number", got: value }); } const rule = (() => { if (typeof Intl === "undefined" || !Intl.PluralRules) { var _options$handleError2; ((_options$handleError2 = options.handleError) !== null && _options$handleError2 !== void 0 ? _options$handleError2 : defaultErrorHandler)(new Error("Missing Intl.PluralRules"), "warn"); return "other"; } // TODO: allow injecting polyfill const pluralRules = new Intl.PluralRules(options.locale); return pluralRules.select(Number(relativeValue)); })(); for (const branch of msg.branches) { if (branch.selector === Number(value) || branch.selector === rule || branch.selector === "other") { return evaluateMessage(branch.message, options, relativeValue); } } throw new MessageEvaluationError("Non-exhaustive plural branches for ".concat(value)); } else if (msg.type === "Number" && numberValue !== undefined) { // TODO: allow injecting polyfill return new Intl.NumberFormat(options.locale).format(numberValue); } else if (msg.type === "Element") { var _options$params3; const { wrap } = options; if (!wrap) throw new MessageEvaluationError("Invalid message: unexpected elementArg"); const value = ((_options$params3 = options.params) !== null && _options$params3 !== void 0 ? _options$params3 : {})[msg.name]; if (value === undefined) throw new MissingArgumentError({ argName: msg.name }); return wrap(value, msg.message !== undefined ? evaluateMessage(msg.message, options, numberValue) : undefined); } throw new MessageEvaluationError("Invalid message"); } function reduceSubmessages(submessages) { if (submessages.every(x => typeof x === "string")) { return submessages.join(""); } const reduced = []; for (const x of submessages) { if (x === "") continue; if (typeof x === "string" && typeof reduced[reduced.length - 1] === "string") { reduced[reduced.length - 1] += x; } else { reduced.push(x); } } return reduced; } function isDateLike(obj) { return typeof obj === "object" && typeof obj.getFullYear === "function"; } //# sourceMappingURL=msgfmt-eval.mjs.map