intl-messageformat 7.8.4 → 8.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/core.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { parse, MessageFormatElement } from 'intl-messageformat-parser';
2
- import { FormatterCache, Formatters, Formats, FormatXMLElementFn, PrimitiveType, MessageFormatPart } from './formatters';
2
+ import { FormatterCache, Formatters, Formats, FormatXMLElementFn, MessageFormatPart } from './formatters';
3
3
  export interface Options {
4
4
  formatters?: Formatters;
5
5
  }
@@ -12,9 +12,8 @@ export declare class IntlMessageFormat {
12
12
  private readonly message;
13
13
  private readonly formatterCache;
14
14
  constructor(message: string | MessageFormatElement[], locales?: string | string[], overrideFormats?: Partial<Formats>, opts?: Options);
15
- format: (values?: Record<string, PrimitiveType> | undefined) => string;
16
- formatToParts: (values?: Record<string, any> | undefined) => MessageFormatPart[];
17
- formatHTMLMessage: (values?: Record<string, string | number | boolean | object | Date | FormatXMLElementFn | null | undefined> | undefined) => (string | object)[];
15
+ format: <T = void>(values?: Record<string, string | number | boolean | Date | T | FormatXMLElementFn<T> | null | undefined> | undefined) => string | T | (string | T)[];
16
+ formatToParts: <T>(values?: Record<string, string | number | boolean | Date | T | FormatXMLElementFn<T> | null | undefined> | undefined) => MessageFormatPart<T>[];
18
17
  resolvedOptions: () => {
19
18
  locale: string;
20
19
  };
package/lib/core.js CHANGED
@@ -16,7 +16,7 @@ var __assign = (this && this.__assign) || function () {
16
16
  };
17
17
  import { parse } from 'intl-messageformat-parser';
18
18
  import memoizeIntlConstructor from 'intl-format-cache';
19
- import { formatToString, formatToParts, formatHTMLMessage, } from './formatters';
19
+ import { formatToParts, } from './formatters';
20
20
  // -- MessageFormat --------------------------------------------------------
21
21
  function mergeConfig(c1, c2) {
22
22
  if (!c2) {
@@ -58,14 +58,30 @@ var IntlMessageFormat = /** @class */ (function () {
58
58
  pluralRules: {},
59
59
  };
60
60
  this.format = function (values) {
61
- return formatToString(_this.ast, _this.locales, _this.formatters, _this.formats, values, _this.message);
61
+ var parts = _this.formatToParts(values);
62
+ // Hot path for straight simple msg translations
63
+ if (parts.length === 1) {
64
+ return parts[0].value;
65
+ }
66
+ var result = parts.reduce(function (all, part) {
67
+ if (!all.length ||
68
+ part.type !== 0 /* literal */ ||
69
+ typeof all[all.length - 1] !== 'string') {
70
+ all.push(part.value);
71
+ }
72
+ else {
73
+ all[all.length - 1] += part.value;
74
+ }
75
+ return all;
76
+ }, []);
77
+ if (result.length <= 1) {
78
+ return result[0] || '';
79
+ }
80
+ return result;
62
81
  };
63
82
  this.formatToParts = function (values) {
64
83
  return formatToParts(_this.ast, _this.locales, _this.formatters, _this.formats, values, undefined, _this.message);
65
84
  };
66
- this.formatHTMLMessage = function (values) {
67
- return formatHTMLMessage(_this.ast, _this.locales, _this.formatters, _this.formats, values, _this.message);
68
- };
69
85
  this.resolvedOptions = function () { return ({
70
86
  locale: Intl.NumberFormat.supportedLocalesOf(_this.locales)[0],
71
87
  }); };
package/lib/error.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ export declare const enum ErrorCode {
2
+ MISSING_VALUE = 0,
3
+ INVALID_VALUE = 1,
4
+ MISSING_INTL_API = 2
5
+ }
6
+ export declare class FormatError extends Error {
7
+ readonly code: ErrorCode;
8
+ constructor(msg: string, code: ErrorCode);
9
+ toString(): string;
10
+ }
11
+ export declare class InvalidValueError extends FormatError {
12
+ constructor(variableId: string, value: any, options: string[]);
13
+ }
14
+ export declare class MissingValueError extends FormatError {
15
+ constructor(variableId: string, originalMessage?: string);
16
+ }
package/lib/error.js ADDED
@@ -0,0 +1,42 @@
1
+ var __extends = (this && this.__extends) || (function () {
2
+ var extendStatics = function (d, b) {
3
+ extendStatics = Object.setPrototypeOf ||
4
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
5
+ function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
6
+ return extendStatics(d, b);
7
+ };
8
+ return function (d, b) {
9
+ extendStatics(d, b);
10
+ function __() { this.constructor = d; }
11
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
12
+ };
13
+ })();
14
+ var FormatError = /** @class */ (function (_super) {
15
+ __extends(FormatError, _super);
16
+ function FormatError(msg, code) {
17
+ var _this = _super.call(this, msg) || this;
18
+ _this.code = code;
19
+ return _this;
20
+ }
21
+ FormatError.prototype.toString = function () {
22
+ return "[formatjs Error: " + this.code + "] " + this.message;
23
+ };
24
+ return FormatError;
25
+ }(Error));
26
+ export { FormatError };
27
+ var InvalidValueError = /** @class */ (function (_super) {
28
+ __extends(InvalidValueError, _super);
29
+ function InvalidValueError(variableId, value, options) {
30
+ return _super.call(this, "Invalid values for \"" + variableId + "\": \"" + value + "\". Options are \"" + Object.keys(options).join('", "') + "\"", 1 /* INVALID_VALUE */) || this;
31
+ }
32
+ return InvalidValueError;
33
+ }(FormatError));
34
+ export { InvalidValueError };
35
+ var MissingValueError = /** @class */ (function (_super) {
36
+ __extends(MissingValueError, _super);
37
+ function MissingValueError(variableId, originalMessage) {
38
+ return _super.call(this, "The intl string context variable \"" + variableId + "\" was not provided to the string \"" + originalMessage + "\"", 0 /* MISSING_VALUE */) || this;
39
+ }
40
+ return MissingValueError;
41
+ }(FormatError));
42
+ export { MissingValueError };
@@ -16,19 +16,17 @@ export interface Formatters {
16
16
  }
17
17
  export declare const enum PART_TYPE {
18
18
  literal = 0,
19
- argument = 1
19
+ object = 1
20
20
  }
21
21
  export interface LiteralPart {
22
22
  type: PART_TYPE.literal;
23
23
  value: string;
24
24
  }
25
- export interface ArgumentPart {
26
- type: PART_TYPE.argument;
27
- value: any;
25
+ export interface ObjectPart<T = any> {
26
+ type: PART_TYPE.object;
27
+ value: T;
28
28
  }
29
- export declare type MessageFormatPart = LiteralPart | ArgumentPart;
29
+ export declare type MessageFormatPart<T> = LiteralPart | ObjectPart<T>;
30
30
  export declare type PrimitiveType = string | number | boolean | null | undefined | Date;
31
- export declare function formatToParts(els: MessageFormatElement[], locales: string | string[], formatters: Formatters, formats: Formats, values?: Record<string, any>, currentPluralValue?: number, originalMessage?: string): MessageFormatPart[];
32
- export declare function formatToString(els: MessageFormatElement[], locales: string | string[], formatters: Formatters, formats: Formats, values?: Record<string, PrimitiveType>, originalMessage?: string): string;
33
- export declare type FormatXMLElementFn = (...args: any[]) => string | object;
34
- export declare function formatHTMLMessage(els: MessageFormatElement[], locales: string | string[], formatters: Formatters, formats: Formats, values?: Record<string, PrimitiveType | object | FormatXMLElementFn>, originalMessage?: string): Array<string | object>;
31
+ 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>[];
32
+ export declare type FormatXMLElementFn<T> = (...args: Array<string | T>) => string | Array<string | T>;
package/lib/formatters.js CHANGED
@@ -1,33 +1,5 @@
1
- var __extends = (this && this.__extends) || (function () {
2
- var extendStatics = function (d, b) {
3
- extendStatics = Object.setPrototypeOf ||
4
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
5
- function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
6
- return extendStatics(d, b);
7
- };
8
- return function (d, b) {
9
- extendStatics(d, b);
10
- function __() { this.constructor = d; }
11
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
12
- };
13
- })();
14
- var __spreadArrays = (this && this.__spreadArrays) || function () {
15
- for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
16
- for (var r = Array(s), k = 0, i = 0; i < il; i++)
17
- for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
18
- r[k] = a[j];
19
- return r;
20
- };
21
- import { convertNumberSkeletonToNumberFormatOptions, isArgumentElement, isDateElement, isDateTimeSkeleton, isLiteralElement, isNumberElement, isNumberSkeleton, isPluralElement, isPoundElement, isSelectElement, isTimeElement, parseDateTimeSkeleton, } from 'intl-messageformat-parser';
22
- var FormatError = /** @class */ (function (_super) {
23
- __extends(FormatError, _super);
24
- function FormatError(msg, variableId) {
25
- var _this = _super.call(this, msg) || this;
26
- _this.variableId = variableId;
27
- return _this;
28
- }
29
- return FormatError;
30
- }(Error));
1
+ import { convertNumberSkeletonToNumberFormatOptions, isArgumentElement, isDateElement, isDateTimeSkeleton, isLiteralElement, isNumberElement, isNumberSkeleton, isPluralElement, isPoundElement, isSelectElement, isTimeElement, parseDateTimeSkeleton, isTagElement, } from 'intl-messageformat-parser';
2
+ import { MissingValueError, InvalidValueError, FormatError, } from './error';
31
3
  function mergeLiteral(parts) {
32
4
  if (parts.length < 2) {
33
5
  return parts;
@@ -45,6 +17,9 @@ function mergeLiteral(parts) {
45
17
  return all;
46
18
  }, []);
47
19
  }
20
+ function isFormatXMLElementFn(el) {
21
+ return typeof el === 'function';
22
+ }
48
23
  // TODO(skeleton): add skeleton support
49
24
  export function formatToParts(els, locales, formatters, formats, values, currentPluralValue,
50
25
  // For debugging
@@ -83,7 +58,7 @@ originalMessage) {
83
58
  var varName = el.value;
84
59
  // Enforce that all required values are provided by the caller.
85
60
  if (!(values && varName in values)) {
86
- throw new FormatError("The intl string context variable \"" + varName + "\" was not provided to the string \"" + originalMessage + "\"");
61
+ throw new MissingValueError(varName, originalMessage);
87
62
  }
88
63
  var value = values[varName];
89
64
  if (isArgumentElement(el)) {
@@ -94,7 +69,7 @@ originalMessage) {
94
69
  : '';
95
70
  }
96
71
  result.push({
97
- type: 1 /* argument */,
72
+ type: typeof value === 'string' ? 0 /* literal */ : 1 /* object */,
98
73
  value: value,
99
74
  });
100
75
  continue;
@@ -140,10 +115,28 @@ originalMessage) {
140
115
  });
141
116
  continue;
142
117
  }
118
+ if (isTagElement(el)) {
119
+ var children = el.children, value_1 = el.value;
120
+ var formatFn = values[value_1];
121
+ if (!isFormatXMLElementFn(formatFn)) {
122
+ throw new TypeError("Value for \"" + value_1 + "\" must be a function");
123
+ }
124
+ var parts = formatToParts(children, locales, formatters, formats, values);
125
+ var chunks = formatFn.apply(void 0, parts.map(function (p) { return p.value; }));
126
+ if (!Array.isArray(chunks)) {
127
+ chunks = [chunks];
128
+ }
129
+ result.push.apply(result, chunks.map(function (c) {
130
+ return {
131
+ type: typeof c === 'string' ? 0 /* literal */ : 1 /* object */,
132
+ value: c,
133
+ };
134
+ }));
135
+ }
143
136
  if (isSelectElement(el)) {
144
137
  var opt = el.options[value] || el.options.other;
145
138
  if (!opt) {
146
- throw new RangeError("Invalid values for \"" + el.value + "\": \"" + value + "\". Options are \"" + Object.keys(el.options).join('", "') + "\"");
139
+ throw new InvalidValueError(el.value, value, Object.keys(el.options));
147
140
  }
148
141
  result.push.apply(result, formatToParts(opt.value, locales, formatters, formats, values));
149
142
  continue;
@@ -152,7 +145,7 @@ originalMessage) {
152
145
  var opt = el.options["=" + value];
153
146
  if (!opt) {
154
147
  if (!Intl.PluralRules) {
155
- throw new FormatError("Intl.PluralRules is not available in this environment.\nTry polyfilling it using \"@formatjs/intl-pluralrules\"\n");
148
+ throw new FormatError("Intl.PluralRules is not available in this environment.\nTry polyfilling it using \"@formatjs/intl-pluralrules\"\n", 2 /* MISSING_INTL_API */);
156
149
  }
157
150
  var rule = formatters
158
151
  .getPluralRules(locales, { type: el.pluralType })
@@ -160,7 +153,7 @@ originalMessage) {
160
153
  opt = el.options[rule] || el.options.other;
161
154
  }
162
155
  if (!opt) {
163
- throw new RangeError("Invalid values for \"" + el.value + "\": \"" + value + "\". Options are \"" + Object.keys(el.options).join('", "') + "\"");
156
+ throw new InvalidValueError(el.value, value, Object.keys(el.options));
164
157
  }
165
158
  result.push.apply(result, formatToParts(opt.value, locales, formatters, formats, values, value - (el.offset || 0)));
166
159
  continue;
@@ -168,136 +161,3 @@ originalMessage) {
168
161
  }
169
162
  return mergeLiteral(result);
170
163
  }
171
- export function formatToString(els, locales, formatters, formats, values,
172
- // For debugging
173
- originalMessage) {
174
- var parts = formatToParts(els, locales, formatters, formats, values, undefined, originalMessage);
175
- // Hot path for straight simple msg translations
176
- if (parts.length === 1) {
177
- return parts[0].value;
178
- }
179
- return parts.reduce(function (all, part) { return (all += part.value); }, '');
180
- }
181
- // Singleton
182
- var domParser;
183
- var TOKEN_DELIMITER = '@@';
184
- var TOKEN_REGEX = /@@(\d+_\d+)@@/g;
185
- var counter = 0;
186
- function generateId() {
187
- return Date.now() + "_" + ++counter;
188
- }
189
- function restoreRichPlaceholderMessage(text, objectParts) {
190
- return text
191
- .split(TOKEN_REGEX)
192
- .filter(Boolean)
193
- .map(function (c) { return (objectParts[c] != null ? objectParts[c] : c); })
194
- .reduce(function (all, c) {
195
- if (!all.length) {
196
- all.push(c);
197
- }
198
- else if (typeof c === 'string' &&
199
- typeof all[all.length - 1] === 'string') {
200
- all[all.length - 1] += c;
201
- }
202
- else {
203
- all.push(c);
204
- }
205
- return all;
206
- }, []);
207
- }
208
- /**
209
- * Not exhaustive, just for sanity check
210
- */
211
- var SIMPLE_XML_REGEX = /(<([0-9a-zA-Z-_]*?)>(.*?)<\/([0-9a-zA-Z-_]*?)>)|(<[0-9a-zA-Z-_]*?\/>)/;
212
- var TEMPLATE_ID = Date.now() + '@@';
213
- var VOID_ELEMENTS = [
214
- 'area',
215
- 'base',
216
- 'br',
217
- 'col',
218
- 'embed',
219
- 'hr',
220
- 'img',
221
- 'input',
222
- 'link',
223
- 'meta',
224
- 'param',
225
- 'source',
226
- 'track',
227
- 'wbr',
228
- ];
229
- function formatHTMLElement(el, objectParts, values) {
230
- var tagName = el.tagName;
231
- var outerHTML = el.outerHTML, textContent = el.textContent, childNodes = el.childNodes;
232
- // Regular text
233
- if (!tagName) {
234
- return restoreRichPlaceholderMessage(textContent || '', objectParts);
235
- }
236
- tagName = tagName.toLowerCase();
237
- var isVoidElement = ~VOID_ELEMENTS.indexOf(tagName);
238
- var formatFnOrValue = values[tagName];
239
- if (formatFnOrValue && isVoidElement) {
240
- throw new FormatError(tagName + " is a self-closing tag and can not be used, please use another tag name.");
241
- }
242
- if (!childNodes.length) {
243
- return [outerHTML];
244
- }
245
- var chunks = Array.prototype.slice.call(childNodes).reduce(function (all, child) {
246
- return all.concat(formatHTMLElement(child, objectParts, values));
247
- }, []);
248
- // Legacy HTML
249
- if (!formatFnOrValue) {
250
- return __spreadArrays(["<" + tagName + ">"], chunks, ["</" + tagName + ">"]);
251
- }
252
- // HTML Tag replacement
253
- if (typeof formatFnOrValue === 'function') {
254
- return [formatFnOrValue.apply(void 0, chunks)];
255
- }
256
- return [formatFnOrValue];
257
- }
258
- export function formatHTMLMessage(els, locales, formatters, formats, values,
259
- // For debugging
260
- originalMessage) {
261
- var parts = formatToParts(els, locales, formatters, formats, values, undefined, originalMessage);
262
- var objectParts = {};
263
- var formattedMessage = parts.reduce(function (all, part) {
264
- if (part.type === 0 /* literal */) {
265
- return (all += part.value);
266
- }
267
- var id = generateId();
268
- objectParts[id] = part.value;
269
- return (all += "" + TOKEN_DELIMITER + id + TOKEN_DELIMITER);
270
- }, '');
271
- // Not designed to filter out aggressively
272
- if (!SIMPLE_XML_REGEX.test(formattedMessage)) {
273
- return restoreRichPlaceholderMessage(formattedMessage, objectParts);
274
- }
275
- if (!values) {
276
- throw new FormatError('Message has placeholders but no values was given');
277
- }
278
- if (typeof DOMParser === 'undefined') {
279
- throw new FormatError('Cannot format XML message without DOMParser');
280
- }
281
- if (!domParser) {
282
- domParser = new DOMParser();
283
- }
284
- var content = domParser
285
- .parseFromString("<formatted-message id=\"" + TEMPLATE_ID + "\">" + formattedMessage + "</formatted-message>", 'text/html')
286
- .getElementById(TEMPLATE_ID);
287
- if (!content) {
288
- throw new FormatError("Malformed HTML message " + formattedMessage);
289
- }
290
- var tagsToFormat = Object.keys(values).filter(function (varName) { return !!content.getElementsByTagName(varName).length; });
291
- // No tags to format
292
- if (!tagsToFormat.length) {
293
- return restoreRichPlaceholderMessage(formattedMessage, objectParts);
294
- }
295
- var caseSensitiveTags = tagsToFormat.filter(function (tagName) { return tagName !== tagName.toLowerCase(); });
296
- if (caseSensitiveTags.length) {
297
- throw new FormatError("HTML tag must be lowercased but the following tags are not: " + caseSensitiveTags.join(', '));
298
- }
299
- // We're doing this since top node is `<formatted-message/>` which does not have a formatter
300
- return Array.prototype.slice
301
- .call(content.childNodes)
302
- .reduce(function (all, child) { return all.concat(formatHTMLElement(child, objectParts, values)); }, []);
303
- }
package/lib/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import IntlMessageFormat from './core';
2
2
  export * from './formatters';
3
3
  export * from './core';
4
+ export * from './error';
4
5
  export default IntlMessageFormat;
package/lib/index.js CHANGED
@@ -6,4 +6,5 @@ See the accompanying LICENSE file for terms.
6
6
  import IntlMessageFormat from './core';
7
7
  export * from './formatters';
8
8
  export * from './core';
9
+ export * from './error';
9
10
  export default IntlMessageFormat;
@@ -1,14 +1,19 @@
1
1
  import { MessageFormatElement } from 'intl-messageformat-parser';
2
2
  import { parse } from 'intl-messageformat-parser';
3
3
 
4
- export declare interface ArgumentPart {
5
- type: PART_TYPE.argument;
6
- value: any;
7
- }
8
-
9
4
  export declare function createDefaultFormatters(cache?: FormatterCache): Formatters;
10
5
 
11
- export declare function formatHTMLMessage(els: MessageFormatElement[], locales: string | string[], formatters: Formatters, formats: Formats, values?: Record<string, PrimitiveType | object | FormatXMLElementFn>, originalMessage?: string): Array<string | object>;
6
+ export declare const enum ErrorCode {
7
+ MISSING_VALUE = 0,
8
+ INVALID_VALUE = 1,
9
+ MISSING_INTL_API = 2
10
+ }
11
+
12
+ export declare class FormatError extends Error {
13
+ readonly code: ErrorCode;
14
+ constructor(msg: string, code: ErrorCode);
15
+ toString(): string;
16
+ }
12
17
 
13
18
  export declare interface Formats {
14
19
  number: Record<string, Intl.NumberFormatOptions>;
@@ -28,11 +33,9 @@ export declare interface Formatters {
28
33
  getPluralRules(...args: ConstructorParameters<typeof Intl.PluralRules>): Intl.PluralRules;
29
34
  }
30
35
 
31
- export declare function formatToParts(els: MessageFormatElement[], locales: string | string[], formatters: Formatters, formats: Formats, values?: Record<string, any>, currentPluralValue?: number, originalMessage?: string): MessageFormatPart[];
32
-
33
- export declare function formatToString(els: MessageFormatElement[], locales: string | string[], formatters: Formatters, formats: Formats, values?: Record<string, PrimitiveType>, originalMessage?: string): string;
36
+ 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>[];
34
37
 
35
- export declare type FormatXMLElementFn = (...args: any[]) => string | object;
38
+ export declare type FormatXMLElementFn<T> = (...args: Array<string | T>) => string | Array<string | T>;
36
39
 
37
40
  declare class IntlMessageFormat {
38
41
  private readonly ast;
@@ -42,9 +45,8 @@ declare class IntlMessageFormat {
42
45
  private readonly message;
43
46
  private readonly formatterCache;
44
47
  constructor(message: string | MessageFormatElement[], locales?: string | string[], overrideFormats?: Partial<Formats>, opts?: Options);
45
- format: (values?: Record<string, PrimitiveType> | undefined) => string;
46
- formatToParts: (values?: Record<string, any> | undefined) => MessageFormatPart[];
47
- formatHTMLMessage: (values?: Record<string, string | number | boolean | object | Date | FormatXMLElementFn | null | undefined> | undefined) => (string | object)[];
48
+ format: <T = void>(values?: Record<string, string | number | boolean | Date | T | FormatXMLElementFn<T> | null | undefined> | undefined) => string | T | (string | T)[];
49
+ formatToParts: <T>(values?: Record<string, string | number | boolean | Date | T | FormatXMLElementFn<T> | null | undefined> | undefined) => MessageFormatPart<T>[];
48
50
  resolvedOptions: () => {
49
51
  locale: string;
50
52
  };
@@ -111,12 +113,25 @@ declare class IntlMessageFormat {
111
113
  export { IntlMessageFormat }
112
114
  export default IntlMessageFormat;
113
115
 
116
+ export declare class InvalidValueError extends FormatError {
117
+ constructor(variableId: string, value: any, options: string[]);
118
+ }
119
+
114
120
  export declare interface LiteralPart {
115
121
  type: PART_TYPE.literal;
116
122
  value: string;
117
123
  }
118
124
 
119
- export declare type MessageFormatPart = LiteralPart | ArgumentPart;
125
+ export declare type MessageFormatPart<T> = LiteralPart | ObjectPart<T>;
126
+
127
+ export declare class MissingValueError extends FormatError {
128
+ constructor(variableId: string, originalMessage?: string);
129
+ }
130
+
131
+ export declare interface ObjectPart<T = any> {
132
+ type: PART_TYPE.object;
133
+ value: T;
134
+ }
120
135
 
121
136
  export declare interface Options {
122
137
  formatters?: Formatters;
@@ -124,7 +139,7 @@ export declare interface Options {
124
139
 
125
140
  export declare const enum PART_TYPE {
126
141
  literal = 0,
127
- argument = 1
142
+ object = 1
128
143
  }
129
144
 
130
145
  export declare type PrimitiveType = string | number | boolean | null | undefined | Date;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "intl-messageformat",
3
- "version": "7.8.4",
3
+ "version": "8.2.1",
4
4
  "description": "Formats ICU Message strings with number, date, plural, and select placeholders to create localized messages.",
5
5
  "keywords": [
6
6
  "i18n",
@@ -32,7 +32,7 @@
32
32
  "types": "lib/intl-messageformat.d.ts",
33
33
  "dependencies": {
34
34
  "intl-format-cache": "^4.2.21",
35
- "intl-messageformat-parser": "^3.6.4"
35
+ "intl-messageformat-parser": "^4.1.0"
36
36
  },
37
37
  "files": [
38
38
  "dist",
@@ -58,5 +58,5 @@
58
58
  "test": "tests"
59
59
  },
60
60
  "license": "BSD-3-Clause",
61
- "gitHead": "a7842673d8ad205171ad7c8cb8bb2f318b427c0c"
61
+ "gitHead": "e0bb34c71e99fd4c68244f3bc668c748b2f18413"
62
62
  }
package/src/core.ts CHANGED
@@ -10,12 +10,11 @@ import {
10
10
  FormatterCache,
11
11
  Formatters,
12
12
  Formats,
13
- formatToString,
14
13
  formatToParts,
15
14
  FormatXMLElementFn,
16
- formatHTMLMessage,
17
15
  PrimitiveType,
18
16
  MessageFormatPart,
17
+ PART_TYPE,
19
18
  } from './formatters';
20
19
 
21
20
  // -- MessageFormat --------------------------------------------------------
@@ -122,17 +121,35 @@ export class IntlMessageFormat {
122
121
  (opts && opts.formatters) || createDefaultFormatters(this.formatterCache);
123
122
  }
124
123
 
125
- format = (values?: Record<string, PrimitiveType>) =>
126
- formatToString(
127
- this.ast,
128
- this.locales,
129
- this.formatters,
130
- this.formats,
131
- values,
132
- this.message
133
- );
124
+ format = <T = void>(
125
+ values?: Record<string, PrimitiveType | T | FormatXMLElementFn<T>>
126
+ ) => {
127
+ const parts = this.formatToParts(values);
128
+ // Hot path for straight simple msg translations
129
+ if (parts.length === 1) {
130
+ return parts[0].value;
131
+ }
132
+ const result = parts.reduce((all, part) => {
133
+ if (
134
+ !all.length ||
135
+ part.type !== PART_TYPE.literal ||
136
+ typeof all[all.length - 1] !== 'string'
137
+ ) {
138
+ all.push(part.value);
139
+ } else {
140
+ all[all.length - 1] += part.value;
141
+ }
142
+ return all;
143
+ }, [] as Array<string | T>);
134
144
 
135
- formatToParts = (values?: Record<string, any>): MessageFormatPart[] =>
145
+ if (result.length <= 1) {
146
+ return result[0] || '';
147
+ }
148
+ return result;
149
+ };
150
+ formatToParts = <T>(
151
+ values?: Record<string, PrimitiveType | T | FormatXMLElementFn<T>>
152
+ ): MessageFormatPart<T>[] =>
136
153
  formatToParts(
137
154
  this.ast,
138
155
  this.locales,
@@ -142,18 +159,6 @@ export class IntlMessageFormat {
142
159
  undefined,
143
160
  this.message
144
161
  );
145
- formatHTMLMessage = (
146
- values?: Record<string, PrimitiveType | object | FormatXMLElementFn>
147
- ) =>
148
- formatHTMLMessage(
149
- this.ast,
150
- this.locales,
151
- this.formatters,
152
- this.formats,
153
- values,
154
- this.message
155
- );
156
-
157
162
  resolvedOptions = () => ({
158
163
  locale: Intl.NumberFormat.supportedLocalesOf(this.locales)[0],
159
164
  });
package/src/error.ts ADDED
@@ -0,0 +1,39 @@
1
+ export const enum ErrorCode {
2
+ // When we have a placeholder but no value to format
3
+ MISSING_VALUE,
4
+ // When value supplied is invalid
5
+ INVALID_VALUE,
6
+ // When we need specific Intl API but it's not available
7
+ MISSING_INTL_API,
8
+ }
9
+
10
+ export class FormatError extends Error {
11
+ public readonly code: ErrorCode;
12
+ constructor(msg: string, code: ErrorCode) {
13
+ super(msg);
14
+ this.code = code;
15
+ }
16
+ public toString() {
17
+ return `[formatjs Error: ${this.code}] ${this.message}`;
18
+ }
19
+ }
20
+
21
+ export class InvalidValueError extends FormatError {
22
+ constructor(variableId: string, value: any, options: string[]) {
23
+ super(
24
+ `Invalid values for "${variableId}": "${value}". Options are "${Object.keys(
25
+ options
26
+ ).join('", "')}"`,
27
+ ErrorCode.INVALID_VALUE
28
+ );
29
+ }
30
+ }
31
+
32
+ export class MissingValueError extends FormatError {
33
+ constructor(variableId: string, originalMessage?: string) {
34
+ super(
35
+ `The intl string context variable "${variableId}" was not provided to the string "${originalMessage}"`,
36
+ ErrorCode.MISSING_VALUE
37
+ );
38
+ }
39
+ }