@intlify/core-base 11.4.7 → 11.4.8

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.
@@ -1,25 +1,8 @@
1
1
  /*!
2
- * core-base v11.4.7
2
+ * core-base v11.4.8
3
3
  * (c) 2026 kazuya kawaguchi
4
4
  * Released under the MIT License.
5
5
  */
6
- function warn(msg, err) {
7
- if (typeof console !== 'undefined') {
8
- console.warn(`[intlify] ` + msg);
9
- /* istanbul ignore if */
10
- if (err) {
11
- console.warn(err.stack);
12
- }
13
- }
14
- }
15
- const hasWarned = {};
16
- function warnOnce(msg) {
17
- if (!hasWarned[msg]) {
18
- hasWarned[msg] = true;
19
- warn(msg);
20
- }
21
- }
22
-
23
6
  /**
24
7
  * Original Utilities
25
8
  * written by kazuya kawaguchi
@@ -70,6 +53,93 @@ const isEmptyObject = (val) => isPlainObject(val) && Object.keys(val).length ===
70
53
  const assign = Object.assign;
71
54
  const _create = Object.create;
72
55
  const create = (obj = null) => _create(obj);
56
+ const hasOwnProperty = Object.prototype.hasOwnProperty;
57
+ function hasOwn(obj, key) {
58
+ return hasOwnProperty.call(obj, key);
59
+ }
60
+ /* eslint-enable */
61
+ /**
62
+ * Useful Utilities By Evan you
63
+ * Modified by kazuya kawaguchi
64
+ * MIT License
65
+ * https://github.com/vuejs/vue-next/blob/master/packages/shared/src/index.ts
66
+ * https://github.com/vuejs/vue-next/blob/master/packages/shared/src/codeframe.ts
67
+ */
68
+ const isArray = Array.isArray;
69
+ const isFunction = (val) => typeof val === 'function';
70
+ const isString = (val) => typeof val === 'string';
71
+ const isBoolean = (val) => typeof val === 'boolean';
72
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
+ const isObject = (val) => val !== null && typeof val === 'object';
74
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
75
+ const isPromise = (val) => {
76
+ return isObject(val) && isFunction(val.then) && isFunction(val.catch);
77
+ };
78
+ const objectToString = Object.prototype.toString;
79
+ const toTypeString = (value) => objectToString.call(value);
80
+ const isPlainObject = (val) => toTypeString(val) === '[object Object]';
81
+ // for converting list and named values to displayed strings.
82
+ const toDisplayString = (val) => {
83
+ return val == null
84
+ ? ''
85
+ : isArray(val) || (isPlainObject(val) && val.toString === objectToString)
86
+ ? JSON.stringify(val, null, 2)
87
+ : String(val);
88
+ };
89
+ function join(items, separator = '') {
90
+ return items.reduce((str, item, index) => (index === 0 ? str + item : str + separator + item), '');
91
+ }
92
+ const RANGE = 2;
93
+ function generateCodeFrame(source, start = 0, end = source.length) {
94
+ const lines = source.split(/\r?\n/);
95
+ let count = 0;
96
+ const res = [];
97
+ for (let i = 0; i < lines.length; i++) {
98
+ count += lines[i].length + 1;
99
+ if (count >= start) {
100
+ for (let j = i - RANGE; j <= i + RANGE || end > count; j++) {
101
+ if (j < 0 || j >= lines.length)
102
+ continue;
103
+ const line = j + 1;
104
+ res.push(`${line}${' '.repeat(3 - String(line).length)}| ${lines[j]}`);
105
+ const lineLength = lines[j].length;
106
+ if (j === i) {
107
+ // push underline
108
+ const pad = start - (count - lineLength) + 1;
109
+ const length = Math.max(1, end > count ? lineLength - pad : end - start);
110
+ res.push(` | ` + ' '.repeat(pad) + '^'.repeat(length));
111
+ }
112
+ else if (j > i) {
113
+ if (end > count) {
114
+ const length = Math.max(Math.min(end - count, lineLength), 1);
115
+ res.push(` | ` + '^'.repeat(length));
116
+ }
117
+ count += lineLength + 1;
118
+ }
119
+ }
120
+ break;
121
+ }
122
+ }
123
+ return res.join('\n');
124
+ }
125
+
126
+ function warn(msg, err) {
127
+ if (typeof console !== 'undefined') {
128
+ console.warn(`[intlify] ` + msg);
129
+ /* istanbul ignore if */
130
+ if (err) {
131
+ console.warn(err.stack);
132
+ }
133
+ }
134
+ }
135
+ const hasWarned = {};
136
+ function warnOnce(msg) {
137
+ if (!hasWarned[msg]) {
138
+ hasWarned[msg] = true;
139
+ warn(msg);
140
+ }
141
+ }
142
+
73
143
  function escapeHtml(rawText) {
74
144
  return rawText
75
145
  .replace(/&/g, '&amp;') // escape `&` first to avoid double escaping
@@ -82,16 +152,37 @@ function escapeHtml(rawText) {
82
152
  }
83
153
  function escapeAttributeValue(value) {
84
154
  return value
85
- .replace(/&(?![a-zA-Z0-9#]{2,6};)/g, '&amp;') // escape unescaped `&`
155
+ .replace(/&(?![a-z0-9#]{2,6};)/gi, '&amp;') // escape unescaped `&`
86
156
  .replace(/"/g, '&quot;')
87
157
  .replace(/'/g, '&apos;')
88
158
  .replace(/</g, '&lt;')
89
159
  .replace(/>/g, '&gt;');
90
160
  }
91
- const javascriptSchemePattern = /^\s*javascript\s*(?::|&#0*58;?|&#x0*3a;?|&colon;?)/i;
161
+ const javascriptSchemePattern = /^javascript:/i;
92
162
  const urlAttributePattern = /^(?:href|src|action|formaction)$/i;
163
+ const numericCharacterReferencePattern = /&#(?:x([0-9a-f]+)|(\d+));?/gi;
164
+ const namedWhitespaceCharacterReferencePattern = /&(?:Tab|NewLine);/g;
165
+ const colonCharacterReferencePattern = /&colon;?/gi;
166
+ // eslint-disable-next-line no-control-regex -- URL scheme normalization requires the full control range
167
+ const controlOrWhitespacePattern = /[\u0000-\u0020\u007f-\u009f]/g;
168
+ const eventHandlerPattern = /(?:^|[\s"'<>/])on\w+\s*=\s*["']?[^"'>]+["']?/i;
169
+ const eventHandlerAttributePattern = /(^|[\s"'<>/])on(\w+\s*=)/gi;
170
+ const unquotedUrlAttributePattern = /(^|[\s"'<>/])((?:href|src|action|formaction)\s*=\s*)([^\s"'=<>`]+)/gi;
171
+ function decodeNumericCharacterReference(match, hex, decimal) {
172
+ const digits = hex || decimal;
173
+ if (!digits) {
174
+ return match;
175
+ }
176
+ const codePoint = Number.parseInt(digits, hex ? 16 : 10);
177
+ return codePoint <= 0x7f ? String.fromCharCode(codePoint) : match;
178
+ }
93
179
  function hasJavascriptScheme(value) {
94
- return javascriptSchemePattern.test(value);
180
+ const normalized = value
181
+ .replace(numericCharacterReferencePattern, decodeNumericCharacterReference)
182
+ .replace(namedWhitespaceCharacterReferencePattern, '')
183
+ .replace(colonCharacterReferencePattern, ':')
184
+ .replace(controlOrWhitespacePattern, '');
185
+ return javascriptSchemePattern.test(normalized);
95
186
  }
96
187
  function sanitizeStyleValue(value) {
97
188
  const urlPattern = /url\s*\(/gi;
@@ -155,88 +246,18 @@ function sanitizeTranslatedHtml(html) {
155
246
  // Process attributes with single quotes
156
247
  html = html.replace(/([\w:-]+)\s*=\s*'([^']*)'/g, (_, attrName, attrValue) => `${attrName}='${sanitizeAttributeValue(attrName, attrValue)}'`);
157
248
  // Detect and neutralize event handler attributes
158
- const eventHandlerPattern = /\s*on\w+\s*=\s*["']?[^"'>]+["']?/gi;
159
249
  if (eventHandlerPattern.test(html)) {
160
250
  {
161
251
  warn('Potentially dangerous event handlers detected in translation. ' +
162
252
  'Consider removing onclick, onerror, etc. from your translation messages.');
163
253
  }
164
254
  // Neutralize event handler attributes by escaping 'on'
165
- html = html.replace(/(\s+)(on)(\w+\s*=)/gi, '$1&#111;n$3');
255
+ html = html.replace(eventHandlerAttributePattern, '$1&#111;n$2');
166
256
  }
167
257
  // Disable javascript: URLs in unquoted attributes
168
- html = html.replace(/(\s+(?:href|src|action|formaction)\s*=\s*)([^\s"'=<>`]+)/gi, (match, prefix, attrValue) => hasJavascriptScheme(attrValue) ? `${prefix}about:blank` : match);
258
+ html = html.replace(unquotedUrlAttributePattern, (match, boundary, prefix, attrValue) => hasJavascriptScheme(attrValue) ? `${boundary}${prefix}about:blank` : match);
169
259
  return html;
170
260
  }
171
- const hasOwnProperty = Object.prototype.hasOwnProperty;
172
- function hasOwn(obj, key) {
173
- return hasOwnProperty.call(obj, key);
174
- }
175
- /* eslint-enable */
176
- /**
177
- * Useful Utilities By Evan you
178
- * Modified by kazuya kawaguchi
179
- * MIT License
180
- * https://github.com/vuejs/vue-next/blob/master/packages/shared/src/index.ts
181
- * https://github.com/vuejs/vue-next/blob/master/packages/shared/src/codeframe.ts
182
- */
183
- const isArray = Array.isArray;
184
- const isFunction = (val) => typeof val === 'function';
185
- const isString = (val) => typeof val === 'string';
186
- const isBoolean = (val) => typeof val === 'boolean';
187
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
188
- const isObject = (val) => val !== null && typeof val === 'object';
189
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
190
- const isPromise = (val) => {
191
- return isObject(val) && isFunction(val.then) && isFunction(val.catch);
192
- };
193
- const objectToString = Object.prototype.toString;
194
- const toTypeString = (value) => objectToString.call(value);
195
- const isPlainObject = (val) => toTypeString(val) === '[object Object]';
196
- // for converting list and named values to displayed strings.
197
- const toDisplayString = (val) => {
198
- return val == null
199
- ? ''
200
- : isArray(val) || (isPlainObject(val) && val.toString === objectToString)
201
- ? JSON.stringify(val, null, 2)
202
- : String(val);
203
- };
204
- function join(items, separator = '') {
205
- return items.reduce((str, item, index) => (index === 0 ? str + item : str + separator + item), '');
206
- }
207
- const RANGE = 2;
208
- function generateCodeFrame(source, start = 0, end = source.length) {
209
- const lines = source.split(/\r?\n/);
210
- let count = 0;
211
- const res = [];
212
- for (let i = 0; i < lines.length; i++) {
213
- count += lines[i].length + 1;
214
- if (count >= start) {
215
- for (let j = i - RANGE; j <= i + RANGE || end > count; j++) {
216
- if (j < 0 || j >= lines.length)
217
- continue;
218
- const line = j + 1;
219
- res.push(`${line}${' '.repeat(3 - String(line).length)}| ${lines[j]}`);
220
- const lineLength = lines[j].length;
221
- if (j === i) {
222
- // push underline
223
- const pad = start - (count - lineLength) + 1;
224
- const length = Math.max(1, end > count ? lineLength - pad : end - start);
225
- res.push(` | ` + ' '.repeat(pad) + '^'.repeat(length));
226
- }
227
- else if (j > i) {
228
- if (end > count) {
229
- const length = Math.max(Math.min(end - count, lineLength), 1);
230
- res.push(` | ` + '^'.repeat(length));
231
- }
232
- count += lineLength + 1;
233
- }
234
- }
235
- break;
236
- }
237
- }
238
- return res.join('\n');
239
- }
240
261
 
241
262
  function createPosition(line, column, offset) {
242
263
  return { line, column, offset };
@@ -2511,7 +2532,7 @@ function getWarnMessage(code, ...args) {
2511
2532
  * Intlify core-base version
2512
2533
  * @internal
2513
2534
  */
2514
- const VERSION = '11.4.7';
2535
+ const VERSION = '11.4.8';
2515
2536
  const NOT_REOSLVED = -1;
2516
2537
  const DEFAULT_LOCALE = 'en-US';
2517
2538
  const MISSING_RESOLVE_VALUE = '';
@@ -2761,6 +2782,88 @@ function isImplicitFallback(targetLocale, locales) {
2761
2782
  }
2762
2783
  /* eslint-enable @typescript-eslint/no-explicit-any */
2763
2784
 
2785
+ function resolveFormatLocale(context, key, locale, formats, missingWarn, fallbackWarn, type) {
2786
+ const { fallbackLocale, localeFallbacker, onWarn } = context;
2787
+ const locales = localeFallbacker(context, // eslint-disable-line @typescript-eslint/no-explicit-any
2788
+ fallbackLocale, locale);
2789
+ let from = locale;
2790
+ for (let i = 0; i < locales.length; i++) {
2791
+ const targetLocale = locales[i];
2792
+ if (locale !== targetLocale &&
2793
+ isTranslateFallbackWarn(fallbackWarn, key)) {
2794
+ onWarn(getWarnMessage(type === 'datetime format'
2795
+ ? CoreWarnCodes.FALLBACK_TO_DATE_FORMAT
2796
+ : CoreWarnCodes.FALLBACK_TO_NUMBER_FORMAT, {
2797
+ key,
2798
+ target: targetLocale
2799
+ }));
2800
+ }
2801
+ if (locale !== targetLocale) {
2802
+ const emitter = context.__v_emitter;
2803
+ if (emitter) {
2804
+ emitter.emit('fallback', {
2805
+ type,
2806
+ key,
2807
+ from,
2808
+ to: targetLocale,
2809
+ groupId: `${type}:${key}`
2810
+ });
2811
+ }
2812
+ }
2813
+ const format = (formats[targetLocale] || {})[key];
2814
+ if (isPlainObject(format) && isString(targetLocale)) {
2815
+ return targetLocale;
2816
+ }
2817
+ handleMissing(context, key, targetLocale, missingWarn, type);
2818
+ from = targetLocale;
2819
+ }
2820
+ return null;
2821
+ }
2822
+ function getFormatterCacheKey(locale, key, overrides) {
2823
+ let id = `${locale}__${key}`;
2824
+ if (isPlainObject(overrides) && !isEmptyObject(overrides)) {
2825
+ id = `${id}__${JSON.stringify(overrides)}`;
2826
+ }
2827
+ return id;
2828
+ }
2829
+ function clearFormatCache(formatters, locale, format) {
2830
+ for (const key in format) {
2831
+ const prefix = `${locale}__${key}`;
2832
+ for (const id of formatters.keys()) {
2833
+ if (id === prefix || id.startsWith(`${prefix}__`)) {
2834
+ formatters.delete(id);
2835
+ }
2836
+ }
2837
+ }
2838
+ }
2839
+ function parseFormatArgs(args, options, initialOverrides, optionsKeys) {
2840
+ const [, arg2, arg3, arg4] = args;
2841
+ let overrides = initialOverrides;
2842
+ if (isString(arg2)) {
2843
+ options.key = arg2;
2844
+ }
2845
+ else if (isPlainObject(arg2)) {
2846
+ Object.keys(arg2).forEach(key => {
2847
+ if (optionsKeys.includes(key)) {
2848
+ overrides[key] = arg2[key];
2849
+ }
2850
+ else {
2851
+ options[key] = arg2[key];
2852
+ }
2853
+ });
2854
+ }
2855
+ if (isString(arg3)) {
2856
+ options.locale = arg3;
2857
+ }
2858
+ else if (isPlainObject(arg3)) {
2859
+ overrides = arg3;
2860
+ }
2861
+ if (isPlainObject(arg4)) {
2862
+ overrides = arg4;
2863
+ }
2864
+ return overrides;
2865
+ }
2866
+
2764
2867
  const intlDefined = typeof Intl !== 'undefined';
2765
2868
  const Availabilities = {
2766
2869
  dateTimeFormat: intlDefined && typeof Intl.DateTimeFormat !== 'undefined',
@@ -2769,7 +2872,7 @@ const Availabilities = {
2769
2872
 
2770
2873
  // implementation of `datetime` function
2771
2874
  function datetime(context, ...args) {
2772
- const { datetimeFormats, unresolving, fallbackLocale, onWarn, localeFallbacker } = context;
2875
+ const { datetimeFormats, unresolving, onWarn } = context;
2773
2876
  const { __datetimeFormatters } = context;
2774
2877
  if (!Availabilities.dateTimeFormat) {
2775
2878
  onWarn(getWarnMessage(CoreWarnCodes.CANNOT_FORMAT_DATE));
@@ -2792,57 +2895,16 @@ function datetime(context, ...args) {
2792
2895
  : context.fallbackWarn;
2793
2896
  const part = !!options.part;
2794
2897
  const locale = getLocale(context, options);
2795
- const locales = localeFallbacker(context, // eslint-disable-line @typescript-eslint/no-explicit-any
2796
- fallbackLocale, locale);
2797
2898
  if (!isString(key) || key === '') {
2798
2899
  const formatter = new Intl.DateTimeFormat(locale.replace(/!/g, ''), overrides);
2799
2900
  return !part ? formatter.format(value) : formatter.formatToParts(value);
2800
2901
  }
2801
- // resolve format
2802
- let datetimeFormat = {};
2803
- let targetLocale;
2804
- let format = null;
2805
- let from = locale;
2806
- let to = null;
2807
- const type = 'datetime format';
2808
- for (let i = 0; i < locales.length; i++) {
2809
- targetLocale = to = locales[i];
2810
- if (locale !== targetLocale &&
2811
- isTranslateFallbackWarn(fallbackWarn, key)) {
2812
- onWarn(getWarnMessage(CoreWarnCodes.FALLBACK_TO_DATE_FORMAT, {
2813
- key,
2814
- target: targetLocale
2815
- }));
2816
- }
2817
- // for vue-devtools timeline event
2818
- if (locale !== targetLocale) {
2819
- const emitter = context.__v_emitter;
2820
- if (emitter) {
2821
- emitter.emit('fallback', {
2822
- type,
2823
- key,
2824
- from,
2825
- to,
2826
- groupId: `${type}:${key}`
2827
- });
2828
- }
2829
- }
2830
- datetimeFormat =
2831
- datetimeFormats[targetLocale] || {};
2832
- format = datetimeFormat[key];
2833
- if (isPlainObject(format))
2834
- break;
2835
- handleMissing(context, key, targetLocale, missingWarn, type); // eslint-disable-line @typescript-eslint/no-explicit-any
2836
- from = to;
2837
- }
2838
- // checking format and target locale
2839
- if (!isPlainObject(format) || !isString(targetLocale)) {
2902
+ const targetLocale = resolveFormatLocale(context, key, locale, datetimeFormats, missingWarn, fallbackWarn, 'datetime format');
2903
+ if (!isString(targetLocale)) {
2840
2904
  return unresolving ? NOT_REOSLVED : key;
2841
2905
  }
2842
- let id = `${targetLocale}__${key}`;
2843
- if (!isEmptyObject(overrides)) {
2844
- id = `${id}__${JSON.stringify(overrides)}`;
2845
- }
2906
+ const format = datetimeFormats[targetLocale][key];
2907
+ const id = getFormatterCacheKey(targetLocale, key, overrides);
2846
2908
  let formatter = __datetimeFormatters.get(id);
2847
2909
  if (!formatter) {
2848
2910
  formatter = new Intl.DateTimeFormat(targetLocale, assign({}, format, overrides));
@@ -2875,9 +2937,9 @@ const DATETIME_FORMAT_OPTIONS_KEYS = [
2875
2937
  ];
2876
2938
  /** @internal */
2877
2939
  function parseDateTimeArgs(...args) {
2878
- const [arg1, arg2, arg3, arg4] = args;
2940
+ const [arg1] = args;
2879
2941
  const options = create();
2880
- let overrides = create();
2942
+ const initialOverrides = create();
2881
2943
  let value;
2882
2944
  if (isString(arg1)) {
2883
2945
  // Only allow ISO strings - other date formats are often supported,
@@ -2914,45 +2976,18 @@ function parseDateTimeArgs(...args) {
2914
2976
  else {
2915
2977
  throw createCoreError(CoreErrorCodes.INVALID_ARGUMENT);
2916
2978
  }
2917
- if (isString(arg2)) {
2918
- options.key = arg2;
2919
- }
2920
- else if (isPlainObject(arg2)) {
2921
- Object.keys(arg2).forEach(key => {
2922
- if (DATETIME_FORMAT_OPTIONS_KEYS.includes(key)) {
2923
- overrides[key] = arg2[key];
2924
- }
2925
- else {
2926
- options[key] = arg2[key];
2927
- }
2928
- });
2929
- }
2930
- if (isString(arg3)) {
2931
- options.locale = arg3;
2932
- }
2933
- else if (isPlainObject(arg3)) {
2934
- overrides = arg3;
2935
- }
2936
- if (isPlainObject(arg4)) {
2937
- overrides = arg4;
2938
- }
2979
+ const overrides = parseFormatArgs(args, options, initialOverrides, DATETIME_FORMAT_OPTIONS_KEYS);
2939
2980
  return [options.key || '', value, options, overrides];
2940
2981
  }
2941
2982
  /** @internal */
2942
2983
  function clearDateTimeFormat(ctx, locale, format) {
2943
2984
  const context = ctx;
2944
- for (const key in format) {
2945
- const id = `${locale}__${key}`;
2946
- if (!context.__datetimeFormatters.has(id)) {
2947
- continue;
2948
- }
2949
- context.__datetimeFormatters.delete(id);
2950
- }
2985
+ clearFormatCache(context.__datetimeFormatters, locale, format);
2951
2986
  }
2952
2987
 
2953
2988
  // implementation of `number` function
2954
2989
  function number(context, ...args) {
2955
- const { numberFormats, unresolving, fallbackLocale, onWarn, localeFallbacker } = context;
2990
+ const { numberFormats, unresolving, onWarn } = context;
2956
2991
  const { __numberFormatters } = context;
2957
2992
  if (!Availabilities.numberFormat) {
2958
2993
  onWarn(getWarnMessage(CoreWarnCodes.CANNOT_FORMAT_NUMBER));
@@ -2975,57 +3010,16 @@ function number(context, ...args) {
2975
3010
  : context.fallbackWarn;
2976
3011
  const part = !!options.part;
2977
3012
  const locale = getLocale(context, options);
2978
- const locales = localeFallbacker(context, // eslint-disable-line @typescript-eslint/no-explicit-any
2979
- fallbackLocale, locale);
2980
3013
  if (!isString(key) || key === '') {
2981
3014
  const formatter = new Intl.NumberFormat(locale.replace(/!/g, ''), overrides);
2982
3015
  return !part ? formatter.format(value) : formatter.formatToParts(value);
2983
3016
  }
2984
- // resolve format
2985
- let numberFormat = {};
2986
- let targetLocale;
2987
- let format = null;
2988
- let from = locale;
2989
- let to = null;
2990
- const type = 'number format';
2991
- for (let i = 0; i < locales.length; i++) {
2992
- targetLocale = to = locales[i];
2993
- if (locale !== targetLocale &&
2994
- isTranslateFallbackWarn(fallbackWarn, key)) {
2995
- onWarn(getWarnMessage(CoreWarnCodes.FALLBACK_TO_NUMBER_FORMAT, {
2996
- key,
2997
- target: targetLocale
2998
- }));
2999
- }
3000
- // for vue-devtools timeline event
3001
- if (locale !== targetLocale) {
3002
- const emitter = context.__v_emitter;
3003
- if (emitter) {
3004
- emitter.emit('fallback', {
3005
- type,
3006
- key,
3007
- from,
3008
- to,
3009
- groupId: `${type}:${key}`
3010
- });
3011
- }
3012
- }
3013
- numberFormat =
3014
- numberFormats[targetLocale] || {};
3015
- format = numberFormat[key];
3016
- if (isPlainObject(format))
3017
- break;
3018
- handleMissing(context, key, targetLocale, missingWarn, type); // eslint-disable-line @typescript-eslint/no-explicit-any
3019
- from = to;
3020
- }
3021
- // checking format and target locale
3022
- if (!isPlainObject(format) || !isString(targetLocale)) {
3017
+ const targetLocale = resolveFormatLocale(context, key, locale, numberFormats, missingWarn, fallbackWarn, 'number format');
3018
+ if (!isString(targetLocale)) {
3023
3019
  return unresolving ? NOT_REOSLVED : key;
3024
3020
  }
3025
- let id = `${targetLocale}__${key}`;
3026
- if (!isEmptyObject(overrides)) {
3027
- id = `${id}__${JSON.stringify(overrides)}`;
3028
- }
3021
+ const format = numberFormats[targetLocale][key];
3022
+ const id = getFormatterCacheKey(targetLocale, key, overrides);
3029
3023
  let formatter = __numberFormatters.get(id);
3030
3024
  if (!formatter) {
3031
3025
  formatter = new Intl.NumberFormat(targetLocale, assign({}, format, overrides));
@@ -3058,47 +3052,20 @@ const NUMBER_FORMAT_OPTIONS_KEYS = [
3058
3052
  ];
3059
3053
  /** @internal */
3060
3054
  function parseNumberArgs(...args) {
3061
- const [arg1, arg2, arg3, arg4] = args;
3055
+ const [arg1] = args;
3062
3056
  const options = create();
3063
- let overrides = create();
3057
+ const initialOverrides = create();
3064
3058
  if (!isNumber(arg1)) {
3065
3059
  throw createCoreError(CoreErrorCodes.INVALID_ARGUMENT);
3066
3060
  }
3067
3061
  const value = arg1;
3068
- if (isString(arg2)) {
3069
- options.key = arg2;
3070
- }
3071
- else if (isPlainObject(arg2)) {
3072
- Object.keys(arg2).forEach(key => {
3073
- if (NUMBER_FORMAT_OPTIONS_KEYS.includes(key)) {
3074
- overrides[key] = arg2[key];
3075
- }
3076
- else {
3077
- options[key] = arg2[key];
3078
- }
3079
- });
3080
- }
3081
- if (isString(arg3)) {
3082
- options.locale = arg3;
3083
- }
3084
- else if (isPlainObject(arg3)) {
3085
- overrides = arg3;
3086
- }
3087
- if (isPlainObject(arg4)) {
3088
- overrides = arg4;
3089
- }
3062
+ const overrides = parseFormatArgs(args, options, initialOverrides, NUMBER_FORMAT_OPTIONS_KEYS);
3090
3063
  return [options.key || '', value, options, overrides];
3091
3064
  }
3092
3065
  /** @internal */
3093
3066
  function clearNumberFormat(ctx, locale, format) {
3094
3067
  const context = ctx;
3095
- for (const key in format) {
3096
- const id = `${locale}__${key}`;
3097
- if (!context.__numberFormatters.has(id)) {
3098
- continue;
3099
- }
3100
- context.__numberFormatters.delete(id);
3101
- }
3068
+ clearFormatCache(context.__numberFormatters, locale, format);
3102
3069
  }
3103
3070
 
3104
3071
  const DEFAULT_MODIFIER = (str) => str;