intl-messageformat
Advanced tools
+9
-4
@@ -1,6 +0,11 @@ | ||
| import { IntlMessageFormat } from './src/core.js'; | ||
| export * from './src/core.js'; | ||
| export * from './src/error.js'; | ||
| export * from './src/formatters.js'; | ||
| /* | ||
| Copyright (c) 2014, Yahoo! Inc. All rights reserved. | ||
| Copyrights licensed under the New BSD License. | ||
| See the accompanying LICENSE file for terms. | ||
| */ | ||
| import { IntlMessageFormat } from "./src/core.js"; | ||
| export * from "./src/core.js"; | ||
| export * from "./src/error.js"; | ||
| export * from "./src/formatters.js"; | ||
| export { IntlMessageFormat }; | ||
| export default IntlMessageFormat; |
+4
-4
@@ -6,7 +6,7 @@ /* | ||
| */ | ||
| import { IntlMessageFormat } from './src/core.js'; | ||
| export * from './src/core.js'; | ||
| export * from './src/error.js'; | ||
| export * from './src/formatters.js'; | ||
| import { IntlMessageFormat } from "./src/core.js"; | ||
| export * from "./src/core.js"; | ||
| export * from "./src/error.js"; | ||
| export * from "./src/formatters.js"; | ||
| export { IntlMessageFormat }; | ||
| export default IntlMessageFormat; |
+4
-4
| { | ||
| "name": "intl-messageformat", | ||
| "description": "Formats ICU Message strings with number, date, plural, and select placeholders to create localized messages.", | ||
| "version": "11.0.8", | ||
| "version": "11.0.9", | ||
| "license": "BSD-3-Clause", | ||
@@ -15,5 +15,5 @@ "author": "Eric Ferraiuolo <eferraiuolo@gmail.com>", | ||
| "tslib": "^2.8.0", | ||
| "@formatjs/ecma402-abstract": "3.0.7", | ||
| "@formatjs/icu-messageformat-parser": "3.2.1", | ||
| "@formatjs/fast-memoize": "3.0.2" | ||
| "@formatjs/ecma402-abstract": "3.0.8", | ||
| "@formatjs/fast-memoize": "3.0.3", | ||
| "@formatjs/icu-messageformat-parser": "3.3.0" | ||
| }, | ||
@@ -20,0 +20,0 @@ "bugs": "https://github.com/formatjs/formatjs/issues", |
+26
-23
@@ -1,26 +0,29 @@ | ||
| import { MessageFormatElement, parse, ParserOptions } from '@formatjs/icu-messageformat-parser'; | ||
| import { Formats, Formatters, FormatXMLElementFn, MessageFormatPart, PrimitiveType } from './formatters.js'; | ||
| export interface Options extends Omit<ParserOptions, 'locale'> { | ||
| formatters?: Formatters; | ||
| import { type MessageFormatElement, parse, type ParserOptions } from "@formatjs/icu-messageformat-parser"; | ||
| import { type Formats, type Formatters, type FormatXMLElementFn, type MessageFormatPart, type PrimitiveType } from "./formatters.js"; | ||
| export interface Options extends Omit<ParserOptions, "locale"> { | ||
| formatters?: Formatters; | ||
| } | ||
| export declare class IntlMessageFormat { | ||
| private readonly ast; | ||
| private readonly locales; | ||
| private readonly resolvedLocale?; | ||
| private readonly formatters; | ||
| private readonly formats; | ||
| private readonly message; | ||
| private readonly formatterCache; | ||
| constructor(message: string | MessageFormatElement[], locales?: string | string[], overrideFormats?: Partial<Formats>, opts?: Options); | ||
| format: <T = void>(values?: Record<string, PrimitiveType | T | FormatXMLElementFn<T>>) => string | T | (string | T)[]; | ||
| formatToParts: <T>(values?: Record<string, PrimitiveType | T | FormatXMLElementFn<T>>) => MessageFormatPart<T>[]; | ||
| resolvedOptions: () => { | ||
| locale: string; | ||
| }; | ||
| getAst: () => MessageFormatElement[]; | ||
| private static memoizedDefaultLocale; | ||
| static get defaultLocale(): string; | ||
| static resolveLocale: (locales: string | string[]) => Intl.Locale | undefined; | ||
| static __parse: typeof parse | undefined; | ||
| static formats: Formats; | ||
| private readonly ast; | ||
| private readonly locales; | ||
| private readonly resolvedLocale?; | ||
| private readonly formatters; | ||
| private readonly formats; | ||
| private readonly message; | ||
| private readonly formatterCache; | ||
| constructor(message: string | MessageFormatElement[], locales?: string | string[], overrideFormats?: Partial<Formats>, opts?: Options); | ||
| format: <T = void>(values?: Record<string, PrimitiveType | T | FormatXMLElementFn<T>>) => string | T | (string | T)[]; | ||
| formatToParts: <T>(values?: Record<string, PrimitiveType | T | FormatXMLElementFn<T>>) => MessageFormatPart<T>[]; | ||
| resolvedOptions: () => { | ||
| locale: string; | ||
| }; | ||
| getAst: () => MessageFormatElement[]; | ||
| private static memoizedDefaultLocale; | ||
| static get defaultLocale(): string; | ||
| static resolveLocale: (locales: string | string[]) => Intl.Locale | undefined; | ||
| static __parse: typeof parse | undefined; | ||
| // Default format options used as the prototype of the `formats` provided to the | ||
| // constructor. These are used when constructing the internal Intl.NumberFormat | ||
| // and Intl.DateTimeFormat instances. | ||
| static formats: Formats; | ||
| } |
+188
-227
@@ -6,236 +6,197 @@ /* | ||
| */ | ||
| import { __assign, __rest, __spreadArray } from "tslib"; | ||
| import { memoize, strategies } from '@formatjs/fast-memoize'; | ||
| import { parse, } from '@formatjs/icu-messageformat-parser'; | ||
| import { formatToParts, PART_TYPE, } from './formatters.js'; | ||
| import { memoize, strategies } from "@formatjs/fast-memoize"; | ||
| import { parse } from "@formatjs/icu-messageformat-parser"; | ||
| import { formatToParts, PART_TYPE } from "./formatters.js"; | ||
| // -- MessageFormat -------------------------------------------------------- | ||
| function mergeConfig(c1, c2) { | ||
| if (!c2) { | ||
| return c1; | ||
| } | ||
| return __assign(__assign(__assign({}, c1), c2), Object.keys(c1).reduce(function (all, k) { | ||
| all[k] = __assign(__assign({}, c1[k]), c2[k]); | ||
| return all; | ||
| }, {})); | ||
| if (!c2) { | ||
| return c1; | ||
| } | ||
| return { | ||
| ...c1, | ||
| ...c2, | ||
| ...Object.keys(c1).reduce((all, k) => { | ||
| all[k] = { | ||
| ...c1[k], | ||
| ...c2[k] | ||
| }; | ||
| return all; | ||
| }, {}) | ||
| }; | ||
| } | ||
| function mergeConfigs(defaultConfig, configs) { | ||
| if (!configs) { | ||
| return defaultConfig; | ||
| } | ||
| return Object.keys(defaultConfig).reduce(function (all, k) { | ||
| all[k] = mergeConfig(defaultConfig[k], configs[k]); | ||
| return all; | ||
| }, __assign({}, defaultConfig)); | ||
| if (!configs) { | ||
| return defaultConfig; | ||
| } | ||
| return Object.keys(defaultConfig).reduce((all, k) => { | ||
| all[k] = mergeConfig(defaultConfig[k], configs[k]); | ||
| return all; | ||
| }, { ...defaultConfig }); | ||
| } | ||
| function createFastMemoizeCache(store) { | ||
| return { | ||
| create: function () { | ||
| return { | ||
| get: function (key) { | ||
| return store[key]; | ||
| }, | ||
| set: function (key, value) { | ||
| store[key] = value; | ||
| }, | ||
| }; | ||
| }, | ||
| }; | ||
| return { create() { | ||
| return { | ||
| get(key) { | ||
| return store[key]; | ||
| }, | ||
| set(key, value) { | ||
| store[key] = value; | ||
| } | ||
| }; | ||
| } }; | ||
| } | ||
| function createDefaultFormatters(cache) { | ||
| if (cache === void 0) { cache = { | ||
| number: {}, | ||
| dateTime: {}, | ||
| pluralRules: {}, | ||
| }; } | ||
| return { | ||
| getNumberFormat: memoize(function () { | ||
| var _a; | ||
| var args = []; | ||
| for (var _i = 0; _i < arguments.length; _i++) { | ||
| args[_i] = arguments[_i]; | ||
| } | ||
| return new ((_a = Intl.NumberFormat).bind.apply(_a, __spreadArray([void 0], args, false)))(); | ||
| }, { | ||
| cache: createFastMemoizeCache(cache.number), | ||
| strategy: strategies.variadic, | ||
| }), | ||
| getDateTimeFormat: memoize(function () { | ||
| var _a; | ||
| var args = []; | ||
| for (var _i = 0; _i < arguments.length; _i++) { | ||
| args[_i] = arguments[_i]; | ||
| } | ||
| return new ((_a = Intl.DateTimeFormat).bind.apply(_a, __spreadArray([void 0], args, false)))(); | ||
| }, { | ||
| cache: createFastMemoizeCache(cache.dateTime), | ||
| strategy: strategies.variadic, | ||
| }), | ||
| getPluralRules: memoize(function () { | ||
| var _a; | ||
| var args = []; | ||
| for (var _i = 0; _i < arguments.length; _i++) { | ||
| args[_i] = arguments[_i]; | ||
| } | ||
| return new ((_a = Intl.PluralRules).bind.apply(_a, __spreadArray([void 0], args, false)))(); | ||
| }, { | ||
| cache: createFastMemoizeCache(cache.pluralRules), | ||
| strategy: strategies.variadic, | ||
| }), | ||
| }; | ||
| function createDefaultFormatters(cache = { | ||
| number: {}, | ||
| dateTime: {}, | ||
| pluralRules: {} | ||
| }) { | ||
| return { | ||
| getNumberFormat: memoize((...args) => new Intl.NumberFormat(...args), { | ||
| cache: createFastMemoizeCache(cache.number), | ||
| strategy: strategies.variadic | ||
| }), | ||
| getDateTimeFormat: memoize((...args) => new Intl.DateTimeFormat(...args), { | ||
| cache: createFastMemoizeCache(cache.dateTime), | ||
| strategy: strategies.variadic | ||
| }), | ||
| getPluralRules: memoize((...args) => new Intl.PluralRules(...args), { | ||
| cache: createFastMemoizeCache(cache.pluralRules), | ||
| strategy: strategies.variadic | ||
| }) | ||
| }; | ||
| } | ||
| var IntlMessageFormat = /** @class */ (function () { | ||
| function IntlMessageFormat(message, locales, overrideFormats, opts) { | ||
| if (locales === void 0) { locales = IntlMessageFormat.defaultLocale; } | ||
| var _this = this; | ||
| this.formatterCache = { | ||
| number: {}, | ||
| dateTime: {}, | ||
| pluralRules: {}, | ||
| }; | ||
| this.format = function (values) { | ||
| var parts = _this.formatToParts(values); | ||
| // Hot path for straight simple msg translations | ||
| if (parts.length === 1) { | ||
| return parts[0].value; | ||
| } | ||
| var result = parts.reduce(function (all, part) { | ||
| if (!all.length || | ||
| part.type !== PART_TYPE.literal || | ||
| typeof all[all.length - 1] !== 'string') { | ||
| all.push(part.value); | ||
| } | ||
| else { | ||
| all[all.length - 1] += part.value; | ||
| } | ||
| return all; | ||
| }, []); | ||
| if (result.length <= 1) { | ||
| return result[0] || ''; | ||
| } | ||
| return result; | ||
| }; | ||
| this.formatToParts = function (values) { | ||
| return formatToParts(_this.ast, _this.locales, _this.formatters, _this.formats, values, undefined, _this.message); | ||
| }; | ||
| this.resolvedOptions = function () { | ||
| var _a; | ||
| return ({ | ||
| locale: ((_a = _this.resolvedLocale) === null || _a === void 0 ? void 0 : _a.toString()) || | ||
| Intl.NumberFormat.supportedLocalesOf(_this.locales)[0], | ||
| }); | ||
| }; | ||
| this.getAst = function () { return _this.ast; }; | ||
| // Defined first because it's used to build the format pattern. | ||
| this.locales = locales; | ||
| this.resolvedLocale = IntlMessageFormat.resolveLocale(locales); | ||
| if (typeof message === 'string') { | ||
| this.message = message; | ||
| if (!IntlMessageFormat.__parse) { | ||
| throw new TypeError('IntlMessageFormat.__parse must be set to process `message` of type `string`'); | ||
| } | ||
| var parseOpts = __rest(opts || {} | ||
| // Parse string messages into an AST. | ||
| , []); | ||
| // Parse string messages into an AST. | ||
| this.ast = IntlMessageFormat.__parse(message, __assign(__assign({}, parseOpts), { locale: this.resolvedLocale })); | ||
| } | ||
| else { | ||
| this.ast = message; | ||
| } | ||
| if (!Array.isArray(this.ast)) { | ||
| throw new TypeError('A message must be provided as a String or AST.'); | ||
| } | ||
| // Creates a new object with the specified `formats` merged with the default | ||
| // formats. | ||
| this.formats = mergeConfigs(IntlMessageFormat.formats, overrideFormats); | ||
| this.formatters = | ||
| (opts && opts.formatters) || createDefaultFormatters(this.formatterCache); | ||
| } | ||
| Object.defineProperty(IntlMessageFormat, "defaultLocale", { | ||
| get: function () { | ||
| if (!IntlMessageFormat.memoizedDefaultLocale) { | ||
| IntlMessageFormat.memoizedDefaultLocale = | ||
| new Intl.NumberFormat().resolvedOptions().locale; | ||
| } | ||
| return IntlMessageFormat.memoizedDefaultLocale; | ||
| }, | ||
| enumerable: false, | ||
| configurable: true | ||
| }); | ||
| IntlMessageFormat.memoizedDefaultLocale = null; | ||
| IntlMessageFormat.resolveLocale = function (locales) { | ||
| if (typeof Intl.Locale === 'undefined') { | ||
| return; | ||
| } | ||
| var supportedLocales = Intl.NumberFormat.supportedLocalesOf(locales); | ||
| if (supportedLocales.length > 0) { | ||
| return new Intl.Locale(supportedLocales[0]); | ||
| } | ||
| return new Intl.Locale(typeof locales === 'string' ? locales : locales[0]); | ||
| }; | ||
| IntlMessageFormat.__parse = parse; | ||
| // Default format options used as the prototype of the `formats` provided to the | ||
| // constructor. These are used when constructing the internal Intl.NumberFormat | ||
| // and Intl.DateTimeFormat instances. | ||
| IntlMessageFormat.formats = { | ||
| number: { | ||
| integer: { | ||
| maximumFractionDigits: 0, | ||
| }, | ||
| currency: { | ||
| style: 'currency', | ||
| }, | ||
| percent: { | ||
| style: 'percent', | ||
| }, | ||
| }, | ||
| date: { | ||
| short: { | ||
| month: 'numeric', | ||
| day: 'numeric', | ||
| year: '2-digit', | ||
| }, | ||
| medium: { | ||
| month: 'short', | ||
| day: 'numeric', | ||
| year: 'numeric', | ||
| }, | ||
| long: { | ||
| month: 'long', | ||
| day: 'numeric', | ||
| year: 'numeric', | ||
| }, | ||
| full: { | ||
| weekday: 'long', | ||
| month: 'long', | ||
| day: 'numeric', | ||
| year: 'numeric', | ||
| }, | ||
| }, | ||
| time: { | ||
| short: { | ||
| hour: 'numeric', | ||
| minute: 'numeric', | ||
| }, | ||
| medium: { | ||
| hour: 'numeric', | ||
| minute: 'numeric', | ||
| second: 'numeric', | ||
| }, | ||
| long: { | ||
| hour: 'numeric', | ||
| minute: 'numeric', | ||
| second: 'numeric', | ||
| timeZoneName: 'short', | ||
| }, | ||
| full: { | ||
| hour: 'numeric', | ||
| minute: 'numeric', | ||
| second: 'numeric', | ||
| timeZoneName: 'short', | ||
| }, | ||
| }, | ||
| }; | ||
| return IntlMessageFormat; | ||
| }()); | ||
| export { IntlMessageFormat }; | ||
| export class IntlMessageFormat { | ||
| ast; | ||
| locales; | ||
| resolvedLocale; | ||
| formatters; | ||
| formats; | ||
| message; | ||
| formatterCache = { | ||
| number: {}, | ||
| dateTime: {}, | ||
| pluralRules: {} | ||
| }; | ||
| constructor(message, locales = IntlMessageFormat.defaultLocale, overrideFormats, opts) { | ||
| // Defined first because it's used to build the format pattern. | ||
| this.locales = locales; | ||
| this.resolvedLocale = IntlMessageFormat.resolveLocale(locales); | ||
| if (typeof message === "string") { | ||
| this.message = message; | ||
| if (!IntlMessageFormat.__parse) { | ||
| throw new TypeError("IntlMessageFormat.__parse must be set to process `message` of type `string`"); | ||
| } | ||
| const { ...parseOpts } = opts || {}; | ||
| // Parse string messages into an AST. | ||
| this.ast = IntlMessageFormat.__parse(message, { | ||
| ...parseOpts, | ||
| locale: this.resolvedLocale | ||
| }); | ||
| } else { | ||
| this.ast = message; | ||
| } | ||
| if (!Array.isArray(this.ast)) { | ||
| throw new TypeError("A message must be provided as a String or AST."); | ||
| } | ||
| // Creates a new object with the specified `formats` merged with the default | ||
| // formats. | ||
| this.formats = mergeConfigs(IntlMessageFormat.formats, overrideFormats); | ||
| this.formatters = opts && opts.formatters || createDefaultFormatters(this.formatterCache); | ||
| } | ||
| format = (values) => { | ||
| const parts = this.formatToParts(values); | ||
| // Hot path for straight simple msg translations | ||
| if (parts.length === 1) { | ||
| return parts[0].value; | ||
| } | ||
| const result = parts.reduce((all, part) => { | ||
| if (!all.length || part.type !== PART_TYPE.literal || typeof all[all.length - 1] !== "string") { | ||
| all.push(part.value); | ||
| } else { | ||
| all[all.length - 1] += part.value; | ||
| } | ||
| return all; | ||
| }, []); | ||
| if (result.length <= 1) { | ||
| return result[0] || ""; | ||
| } | ||
| return result; | ||
| }; | ||
| formatToParts = (values) => formatToParts(this.ast, this.locales, this.formatters, this.formats, values, undefined, this.message); | ||
| resolvedOptions = () => ({ locale: this.resolvedLocale?.toString() || Intl.NumberFormat.supportedLocalesOf(this.locales)[0] }); | ||
| getAst = () => this.ast; | ||
| static memoizedDefaultLocale = null; | ||
| static get defaultLocale() { | ||
| if (!IntlMessageFormat.memoizedDefaultLocale) { | ||
| IntlMessageFormat.memoizedDefaultLocale = new Intl.NumberFormat().resolvedOptions().locale; | ||
| } | ||
| return IntlMessageFormat.memoizedDefaultLocale; | ||
| } | ||
| static resolveLocale = (locales) => { | ||
| if (typeof Intl.Locale === "undefined") { | ||
| return; | ||
| } | ||
| const supportedLocales = Intl.NumberFormat.supportedLocalesOf(locales); | ||
| if (supportedLocales.length > 0) { | ||
| return new Intl.Locale(supportedLocales[0]); | ||
| } | ||
| return new Intl.Locale(typeof locales === "string" ? locales : locales[0]); | ||
| }; | ||
| static __parse = parse; | ||
| // Default format options used as the prototype of the `formats` provided to the | ||
| // constructor. These are used when constructing the internal Intl.NumberFormat | ||
| // and Intl.DateTimeFormat instances. | ||
| static formats = { | ||
| number: { | ||
| integer: { maximumFractionDigits: 0 }, | ||
| currency: { style: "currency" }, | ||
| percent: { style: "percent" } | ||
| }, | ||
| date: { | ||
| short: { | ||
| month: "numeric", | ||
| day: "numeric", | ||
| year: "2-digit" | ||
| }, | ||
| medium: { | ||
| month: "short", | ||
| day: "numeric", | ||
| year: "numeric" | ||
| }, | ||
| long: { | ||
| month: "long", | ||
| day: "numeric", | ||
| year: "numeric" | ||
| }, | ||
| full: { | ||
| weekday: "long", | ||
| month: "long", | ||
| day: "numeric", | ||
| year: "numeric" | ||
| } | ||
| }, | ||
| time: { | ||
| short: { | ||
| hour: "numeric", | ||
| minute: "numeric" | ||
| }, | ||
| medium: { | ||
| hour: "numeric", | ||
| minute: "numeric", | ||
| second: "numeric" | ||
| }, | ||
| long: { | ||
| hour: "numeric", | ||
| minute: "numeric", | ||
| second: "numeric", | ||
| timeZoneName: "short" | ||
| }, | ||
| full: { | ||
| hour: "numeric", | ||
| minute: "numeric", | ||
| second: "numeric", | ||
| timeZoneName: "short" | ||
| } | ||
| } | ||
| }; | ||
| } |
+20
-17
| export declare enum ErrorCode { | ||
| MISSING_VALUE = "MISSING_VALUE", | ||
| INVALID_VALUE = "INVALID_VALUE", | ||
| MISSING_INTL_API = "MISSING_INTL_API" | ||
| // When we have a placeholder but no value to format | ||
| MISSING_VALUE = "MISSING_VALUE", | ||
| // When value supplied is invalid | ||
| INVALID_VALUE = "INVALID_VALUE", | ||
| // When we need specific Intl API but it's not available | ||
| MISSING_INTL_API = "MISSING_INTL_API" | ||
| } | ||
| export declare class FormatError extends Error { | ||
| readonly code: ErrorCode; | ||
| /** | ||
| * Original message we're trying to format | ||
| * `undefined` if we're only dealing w/ AST | ||
| * | ||
| * @type {(string | undefined)} | ||
| * @memberof FormatError | ||
| */ | ||
| readonly originalMessage: string | undefined; | ||
| constructor(msg: string, code: ErrorCode, originalMessage?: string); | ||
| toString(): string; | ||
| readonly code: ErrorCode; | ||
| /** | ||
| * Original message we're trying to format | ||
| * `undefined` if we're only dealing w/ AST | ||
| * | ||
| * @type {(string | undefined)} | ||
| * @memberof FormatError | ||
| */ | ||
| readonly originalMessage: string | undefined; | ||
| constructor(msg: string, code: ErrorCode, originalMessage?: string); | ||
| toString(): string; | ||
| } | ||
| export declare class InvalidValueError extends FormatError { | ||
| constructor(variableId: string, value: any, options: string[], originalMessage?: string); | ||
| constructor(variableId: string, value: any, options: string[], originalMessage?: string); | ||
| } | ||
| export declare class InvalidValueTypeError extends FormatError { | ||
| constructor(value: any, type: string, originalMessage?: string); | ||
| constructor(value: any, type: string, originalMessage?: string); | ||
| } | ||
| export declare class MissingValueError extends FormatError { | ||
| constructor(variableId: string, originalMessage?: string); | ||
| constructor(variableId: string, originalMessage?: string); | ||
| } |
+43
-48
@@ -1,48 +0,43 @@ | ||
| import { __extends } from "tslib"; | ||
| export var ErrorCode; | ||
| (function (ErrorCode) { | ||
| // When we have a placeholder but no value to format | ||
| ErrorCode["MISSING_VALUE"] = "MISSING_VALUE"; | ||
| // When value supplied is invalid | ||
| ErrorCode["INVALID_VALUE"] = "INVALID_VALUE"; | ||
| // When we need specific Intl API but it's not available | ||
| ErrorCode["MISSING_INTL_API"] = "MISSING_INTL_API"; | ||
| })(ErrorCode || (ErrorCode = {})); | ||
| var FormatError = /** @class */ (function (_super) { | ||
| __extends(FormatError, _super); | ||
| function FormatError(msg, code, originalMessage) { | ||
| var _this = _super.call(this, msg) || this; | ||
| _this.code = code; | ||
| _this.originalMessage = originalMessage; | ||
| return _this; | ||
| } | ||
| FormatError.prototype.toString = function () { | ||
| return "[formatjs Error: ".concat(this.code, "] ").concat(this.message); | ||
| }; | ||
| return FormatError; | ||
| }(Error)); | ||
| export { FormatError }; | ||
| var InvalidValueError = /** @class */ (function (_super) { | ||
| __extends(InvalidValueError, _super); | ||
| function InvalidValueError(variableId, value, options, originalMessage) { | ||
| return _super.call(this, "Invalid values for \"".concat(variableId, "\": \"").concat(value, "\". Options are \"").concat(Object.keys(options).join('", "'), "\""), ErrorCode.INVALID_VALUE, originalMessage) || this; | ||
| } | ||
| return InvalidValueError; | ||
| }(FormatError)); | ||
| export { InvalidValueError }; | ||
| var InvalidValueTypeError = /** @class */ (function (_super) { | ||
| __extends(InvalidValueTypeError, _super); | ||
| function InvalidValueTypeError(value, type, originalMessage) { | ||
| return _super.call(this, "Value for \"".concat(value, "\" must be of type ").concat(type), ErrorCode.INVALID_VALUE, originalMessage) || this; | ||
| } | ||
| return InvalidValueTypeError; | ||
| }(FormatError)); | ||
| export { InvalidValueTypeError }; | ||
| var MissingValueError = /** @class */ (function (_super) { | ||
| __extends(MissingValueError, _super); | ||
| function MissingValueError(variableId, originalMessage) { | ||
| return _super.call(this, "The intl string context variable \"".concat(variableId, "\" was not provided to the string \"").concat(originalMessage, "\""), ErrorCode.MISSING_VALUE, originalMessage) || this; | ||
| } | ||
| return MissingValueError; | ||
| }(FormatError)); | ||
| export { MissingValueError }; | ||
| export let ErrorCode = /* @__PURE__ */ function(ErrorCode) { | ||
| // When we have a placeholder but no value to format | ||
| ErrorCode["MISSING_VALUE"] = "MISSING_VALUE"; | ||
| // When value supplied is invalid | ||
| ErrorCode["INVALID_VALUE"] = "INVALID_VALUE"; | ||
| // When we need specific Intl API but it's not available | ||
| ErrorCode["MISSING_INTL_API"] = "MISSING_INTL_API"; | ||
| return ErrorCode; | ||
| }({}); | ||
| export class FormatError extends Error { | ||
| code; | ||
| /** | ||
| * Original message we're trying to format | ||
| * `undefined` if we're only dealing w/ AST | ||
| * | ||
| * @type {(string | undefined)} | ||
| * @memberof FormatError | ||
| */ | ||
| originalMessage; | ||
| constructor(msg, code, originalMessage) { | ||
| super(msg); | ||
| this.code = code; | ||
| this.originalMessage = originalMessage; | ||
| } | ||
| toString() { | ||
| return `[formatjs Error: ${this.code}] ${this.message}`; | ||
| } | ||
| } | ||
| export class InvalidValueError extends FormatError { | ||
| constructor(variableId, value, options, originalMessage) { | ||
| super(`Invalid values for "${variableId}": "${value}". Options are "${Object.keys(options).join("\", \"")}"`, ErrorCode.INVALID_VALUE, originalMessage); | ||
| } | ||
| } | ||
| export class InvalidValueTypeError extends FormatError { | ||
| constructor(value, type, originalMessage) { | ||
| super(`Value for "${value}" must be of type ${type}`, ErrorCode.INVALID_VALUE, originalMessage); | ||
| } | ||
| } | ||
| export class MissingValueError extends FormatError { | ||
| constructor(variableId, originalMessage) { | ||
| super(`The intl string context variable "${variableId}" was not provided to the string "${originalMessage}"`, ErrorCode.MISSING_VALUE, originalMessage); | ||
| } | ||
| } |
+27
-26
@@ -1,40 +0,37 @@ | ||
| import { NumberFormatOptions } from '@formatjs/ecma402-abstract'; | ||
| import { MessageFormatElement } from '@formatjs/icu-messageformat-parser'; | ||
| import { type NumberFormatOptions } from "@formatjs/ecma402-abstract"; | ||
| import { type MessageFormatElement } from "@formatjs/icu-messageformat-parser"; | ||
| declare global { | ||
| namespace FormatjsIntl { | ||
| interface Message { | ||
| } | ||
| interface IntlConfig { | ||
| } | ||
| interface Formats { | ||
| } | ||
| } | ||
| namespace FormatjsIntl { | ||
| interface Message {} | ||
| interface IntlConfig {} | ||
| interface Formats {} | ||
| } | ||
| } | ||
| type Format<Source = string> = Source extends keyof FormatjsIntl.Formats ? FormatjsIntl.Formats[Source] : string; | ||
| export interface Formats { | ||
| number: Record<Format<'number'>, NumberFormatOptions>; | ||
| date: Record<Format<'date'>, Intl.DateTimeFormatOptions>; | ||
| time: Record<Format<'time'>, Intl.DateTimeFormatOptions>; | ||
| number: Record<Format<"number">, NumberFormatOptions>; | ||
| date: Record<Format<"date">, Intl.DateTimeFormatOptions>; | ||
| time: Record<Format<"time">, Intl.DateTimeFormatOptions>; | ||
| } | ||
| export interface FormatterCache { | ||
| number: Record<string, NumberFormatOptions>; | ||
| dateTime: Record<string, Intl.DateTimeFormat>; | ||
| pluralRules: Record<string, Intl.PluralRules>; | ||
| number: Record<string, NumberFormatOptions>; | ||
| dateTime: Record<string, Intl.DateTimeFormat>; | ||
| pluralRules: Record<string, Intl.PluralRules>; | ||
| } | ||
| export interface Formatters { | ||
| getNumberFormat(locals?: string | string[], opts?: NumberFormatOptions): Intl.NumberFormat; | ||
| getDateTimeFormat(...args: ConstructorParameters<typeof Intl.DateTimeFormat>): Intl.DateTimeFormat; | ||
| getPluralRules(...args: ConstructorParameters<typeof Intl.PluralRules>): Intl.PluralRules; | ||
| getNumberFormat(locals?: string | string[], opts?: NumberFormatOptions): Intl.NumberFormat; | ||
| getDateTimeFormat(...args: ConstructorParameters<typeof Intl.DateTimeFormat>): Intl.DateTimeFormat; | ||
| getPluralRules(...args: ConstructorParameters<typeof Intl.PluralRules>): Intl.PluralRules; | ||
| } | ||
| export declare enum PART_TYPE { | ||
| literal = 0, | ||
| object = 1 | ||
| literal = 0, | ||
| object = 1 | ||
| } | ||
| export interface LiteralPart { | ||
| type: PART_TYPE.literal; | ||
| value: string; | ||
| type: PART_TYPE.literal; | ||
| value: string; | ||
| } | ||
| export interface ObjectPart<T = any> { | ||
| type: PART_TYPE.object; | ||
| value: T; | ||
| type: PART_TYPE.object; | ||
| value: T; | ||
| } | ||
@@ -44,4 +41,8 @@ export type MessageFormatPart<T> = LiteralPart | ObjectPart<T>; | ||
| export declare function isFormatXMLElementFn<T>(el: PrimitiveType | T | FormatXMLElementFn<T>): el is FormatXMLElementFn<T>; | ||
| // TODO(skeleton): add skeleton support | ||
| export declare function formatToParts<T>(els: MessageFormatElement[], locales: string | string[], formatters: Formatters, formats: Formats, values?: Record<string, PrimitiveType | T | FormatXMLElementFn<T>>, currentPluralValue?: number, originalMessage?: string): MessageFormatPart<T>[]; | ||
| export type FormatXMLElementFn<T, R = string | T | (string | T)[]> = (parts: Array<string | T>) => R; | ||
| export type FormatXMLElementFn< | ||
| T, | ||
| R = string | T | (string | T)[] | ||
| > = (parts: Array<string | T>) => R; | ||
| export {}; |
+160
-201
@@ -1,207 +0,166 @@ | ||
| import { isArgumentElement, isDateElement, isDateTimeSkeleton, isLiteralElement, isNumberElement, isNumberSkeleton, isPluralElement, isPoundElement, isSelectElement, isTagElement, isTimeElement, } from '@formatjs/icu-messageformat-parser'; | ||
| import { ErrorCode, FormatError, InvalidValueError, InvalidValueTypeError, MissingValueError, } from './error.js'; | ||
| 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 = {})); | ||
| import "@formatjs/ecma402-abstract"; | ||
| import { isArgumentElement, isDateElement, isDateTimeSkeleton, isLiteralElement, isNumberElement, isNumberSkeleton, isPluralElement, isPoundElement, isSelectElement, isTagElement, isTimeElement } from "@formatjs/icu-messageformat-parser"; | ||
| import { ErrorCode, FormatError, InvalidValueError, InvalidValueTypeError, MissingValueError } from "./error.js"; | ||
| export let PART_TYPE = /* @__PURE__ */ function(PART_TYPE) { | ||
| PART_TYPE[PART_TYPE["literal"] = 0] = "literal"; | ||
| PART_TYPE[PART_TYPE["object"] = 1] = "object"; | ||
| return 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; | ||
| }, []); | ||
| if (parts.length < 2) { | ||
| return parts; | ||
| } | ||
| return parts.reduce((all, part) => { | ||
| const 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'; | ||
| 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' || | ||
| typeof value === 'bigint') { | ||
| value = | ||
| typeof value === 'string' || | ||
| typeof value === 'number' || | ||
| typeof value === 'bigint' | ||
| ? 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) { | ||
| var scale = style.scale || 1; | ||
| // Handle bigint scale multiplication | ||
| // BigInt can only be multiplied by BigInt | ||
| if (typeof value === 'bigint') { | ||
| // Check if scale is a safe integer that can be converted to BigInt | ||
| if (!Number.isInteger(scale)) { | ||
| throw new TypeError("Cannot apply fractional scale ".concat(scale, " to bigint value. Scale must be an integer when formatting bigint.")); | ||
| } | ||
| value = value * BigInt(scale); | ||
| } | ||
| else { | ||
| value = value * scale; | ||
| } | ||
| } | ||
| 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)) { | ||
| // GH #4490: Use hasOwnProperty to avoid prototype chain issues with keys like "constructor" | ||
| var key = value; | ||
| var opt = (Object.prototype.hasOwnProperty.call(el.options, key) | ||
| ? el.options[key] | ||
| : undefined) || 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)) { | ||
| // GH #4490: Use hasOwnProperty to avoid prototype chain issues | ||
| var exactKey = "=".concat(value); | ||
| var opt = Object.prototype.hasOwnProperty.call(el.options, exactKey) | ||
| ? el.options[exactKey] | ||
| : undefined; | ||
| 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); | ||
| } | ||
| // Convert bigint to number for PluralRules (which only accepts number) | ||
| var numericValue_1 = typeof value === 'bigint' ? Number(value) : value; | ||
| var rule = formatters | ||
| .getPluralRules(locales, { type: el.pluralType }) | ||
| .select(numericValue_1 - (el.offset || 0)); | ||
| opt = | ||
| (Object.prototype.hasOwnProperty.call(el.options, rule) | ||
| ? el.options[rule] | ||
| : undefined) || el.options.other; | ||
| } | ||
| if (!opt) { | ||
| throw new InvalidValueError(el.value, value, Object.keys(el.options), originalMessage); | ||
| } | ||
| // Convert bigint to number for currentPluralValue | ||
| var numericValue = typeof value === 'bigint' ? Number(value) : value; | ||
| result.push.apply(result, formatToParts(opt.value, locales, formatters, formats, values, numericValue - (el.offset || 0))); | ||
| continue; | ||
| } | ||
| } | ||
| return mergeLiteral(result); | ||
| export function formatToParts(els, locales, formatters, formats, values, currentPluralValue, originalMessage) { | ||
| // Hot path for straight simple msg translations | ||
| if (els.length === 1 && isLiteralElement(els[0])) { | ||
| return [{ | ||
| type: PART_TYPE.literal, | ||
| value: els[0].value | ||
| }]; | ||
| } | ||
| const result = []; | ||
| for (const el of els) { | ||
| // 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; | ||
| } | ||
| const { value: varName } = el; | ||
| // Enforce that all required values are provided by the caller. | ||
| if (!(values && varName in values)) { | ||
| throw new MissingValueError(varName, originalMessage); | ||
| } | ||
| let value = values[varName]; | ||
| if (isArgumentElement(el)) { | ||
| if (!value || typeof value === "string" || typeof value === "number" || typeof value === "bigint") { | ||
| value = typeof value === "string" || typeof value === "number" || typeof value === "bigint" ? String(value) : ""; | ||
| } | ||
| result.push({ | ||
| type: typeof value === "string" ? PART_TYPE.literal : PART_TYPE.object, | ||
| 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)) { | ||
| const 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)) { | ||
| const 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)) { | ||
| const style = typeof el.style === "string" ? formats.number[el.style] : isNumberSkeleton(el.style) ? el.style.parsedOptions : undefined; | ||
| if (style && style.scale) { | ||
| const scale = style.scale || 1; | ||
| // Handle bigint scale multiplication | ||
| // BigInt can only be multiplied by BigInt | ||
| if (typeof value === "bigint") { | ||
| // Check if scale is a safe integer that can be converted to BigInt | ||
| if (!Number.isInteger(scale)) { | ||
| throw new TypeError(`Cannot apply fractional scale ${scale} to bigint value. Scale must be an integer when formatting bigint.`); | ||
| } | ||
| value = value * BigInt(scale); | ||
| } else { | ||
| value = value * scale; | ||
| } | ||
| } | ||
| result.push({ | ||
| type: PART_TYPE.literal, | ||
| value: formatters.getNumberFormat(locales, style).format(value) | ||
| }); | ||
| continue; | ||
| } | ||
| if (isTagElement(el)) { | ||
| const { children, value } = el; | ||
| const formatFn = values[value]; | ||
| if (!isFormatXMLElementFn(formatFn)) { | ||
| throw new InvalidValueTypeError(value, "function", originalMessage); | ||
| } | ||
| const parts = formatToParts(children, locales, formatters, formats, values, currentPluralValue); | ||
| let chunks = formatFn(parts.map((p) => p.value)); | ||
| if (!Array.isArray(chunks)) { | ||
| chunks = [chunks]; | ||
| } | ||
| result.push(...chunks.map((c) => { | ||
| return { | ||
| type: typeof c === "string" ? PART_TYPE.literal : PART_TYPE.object, | ||
| value: c | ||
| }; | ||
| })); | ||
| } | ||
| if (isSelectElement(el)) { | ||
| // GH #4490: Use hasOwnProperty to avoid prototype chain issues with keys like "constructor" | ||
| const key = value; | ||
| const opt = (Object.prototype.hasOwnProperty.call(el.options, key) ? el.options[key] : undefined) || el.options.other; | ||
| if (!opt) { | ||
| throw new InvalidValueError(el.value, value, Object.keys(el.options), originalMessage); | ||
| } | ||
| result.push(...formatToParts(opt.value, locales, formatters, formats, values)); | ||
| continue; | ||
| } | ||
| if (isPluralElement(el)) { | ||
| // GH #4490: Use hasOwnProperty to avoid prototype chain issues | ||
| const exactKey = `=${value}`; | ||
| let opt = Object.prototype.hasOwnProperty.call(el.options, exactKey) ? el.options[exactKey] : undefined; | ||
| if (!opt) { | ||
| if (!Intl.PluralRules) { | ||
| throw new FormatError(`Intl.PluralRules is not available in this environment. | ||
| Try polyfilling it using "@formatjs/intl-pluralrules" | ||
| `, ErrorCode.MISSING_INTL_API, originalMessage); | ||
| } | ||
| // Convert bigint to number for PluralRules (which only accepts number) | ||
| const numericValue = typeof value === "bigint" ? Number(value) : value; | ||
| const rule = formatters.getPluralRules(locales, { type: el.pluralType }).select(numericValue - (el.offset || 0)); | ||
| opt = (Object.prototype.hasOwnProperty.call(el.options, rule) ? el.options[rule] : undefined) || el.options.other; | ||
| } | ||
| if (!opt) { | ||
| throw new InvalidValueError(el.value, value, Object.keys(el.options), originalMessage); | ||
| } | ||
| // Convert bigint to number for currentPluralValue | ||
| const numericValue = typeof value === "bigint" ? Number(value) : value; | ||
| result.push(...formatToParts(opt.value, locales, formatters, formats, values, numericValue - (el.offset || 0))); | ||
| continue; | ||
| } | ||
| } | ||
| return mergeLiteral(result); | ||
| } |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
5
-16.67%105164
-43.84%12
-7.69%3656
-49.19%+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
Updated