vue-i18n 11.4.13 → 11.4.14

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/dist/vue-i18n.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * vue-i18n v11.4.13
2
+ * vue-i18n v11.4.14
3
3
  * (c) 2026 kazuya kawaguchi
4
4
  * Released under the MIT License.
5
5
  */
@@ -30,7 +30,7 @@ var Vue__namespace = /*#__PURE__*/_interopNamespaceDefault(Vue);
30
30
  *
31
31
  * @VueI18nGeneral
32
32
  */
33
- const VERSION = '11.4.13';
33
+ const VERSION = '11.4.14';
34
34
 
35
35
  const I18nErrorCodes = {
36
36
  // composer module errors
@@ -64,6 +64,17 @@ const InejctWithOptionSymbol =
64
64
  const DisposeSymbol = /* #__PURE__*/ shared.makeSymbol('__dispose');
65
65
 
66
66
  /* eslint-disable @typescript-eslint/no-explicit-any */
67
+ /**
68
+ * Whether `locale` can't be a key of the locale messages and formats.
69
+ * A property name of `Object.prototype`, such as `__proto__` or `constructor`,
70
+ * would read or replace a built-in object, so it is ignored with a warning.
71
+ */
72
+ function isUnsafeLocale(locale) {
73
+ if (shared.isObjectPrototypeKey(locale)) {
74
+ return true;
75
+ }
76
+ return false;
77
+ }
67
78
  /**
68
79
  * Transform flat json in obj to normal json in obj
69
80
  */
@@ -148,7 +159,10 @@ function getLocaleMessages(locale, options) {
148
159
  if ('locale' in custom && 'resource' in custom) {
149
160
  const { locale, resource } = custom;
150
161
  if (locale) {
151
- ret[locale] = ret[locale] || shared.create();
162
+ if (isUnsafeLocale(locale)) {
163
+ return;
164
+ }
165
+ ret[locale] = shared.getOwn(ret, locale) || shared.create();
152
166
  shared.deepCopy(resource, ret[locale]);
153
167
  }
154
168
  else {
@@ -335,7 +349,6 @@ function createComposer(options = {}) {
335
349
  // pluralRules
336
350
  let _pluralRules = options.pluralRules || (__root && __root.pluralRules);
337
351
  // runtime context
338
- // eslint-disable-next-line prefer-const
339
352
  let _context;
340
353
  const getCoreContext = () => {
341
354
  _isGlobal && coreBase.setFallbackContext(null);
@@ -361,17 +374,12 @@ function createComposer(options = {}) {
361
374
  {
362
375
  ctxOptions.datetimeFormats = _datetimeFormats.value;
363
376
  ctxOptions.numberFormats = _numberFormats.value;
364
- ctxOptions.__datetimeFormatters = shared.isPlainObject(_context)
365
- ? _context.__datetimeFormatters
366
- : undefined;
367
- ctxOptions.__numberFormatters = shared.isPlainObject(_context)
368
- ? _context.__numberFormatters
369
- : undefined;
370
377
  }
371
378
  const ctx = coreBase.createCoreContext(ctxOptions);
372
379
  _isGlobal && coreBase.setFallbackContext(ctx);
373
380
  return ctx;
374
381
  };
382
+ // eslint-disable-next-line prefer-const
375
383
  _context = getCoreContext();
376
384
  coreBase.updateFallbackLocale(_context, _locale.value, _fallbackLocale.value);
377
385
  // track reactivity
@@ -393,17 +401,12 @@ function createComposer(options = {}) {
393
401
  _locale.value = val;
394
402
  }
395
403
  });
396
- // NOTE: `_context.fallbackContext` points at the root context only while a
397
- // translation runs (see `wrapWithDeps`). Point it there while invalidating too,
398
- // so that the chain the root cached for this composer's fallbackLocale is dropped.
399
- function updateFallbackLocaleWithRoot(fallback) {
404
+ function withRootFallbackContext(fn) {
400
405
  if (!_isGlobal) {
401
- _context.fallbackContext = __root
402
- ? coreBase.getFallbackContext()
403
- : undefined;
406
+ _context.fallbackContext = coreBase.getFallbackContext();
404
407
  }
405
408
  try {
406
- coreBase.updateFallbackLocale(_context, _locale.value, fallback);
409
+ return fn();
407
410
  }
408
411
  finally {
409
412
  if (!_isGlobal) {
@@ -411,6 +414,12 @@ function createComposer(options = {}) {
411
414
  }
412
415
  }
413
416
  }
417
+ // NOTE: `_context.fallbackContext` points at the root context only while a
418
+ // translation runs (see `wrapWithDeps`). Point it there while invalidating too,
419
+ // so that the chain the root cached for this composer's fallbackLocale is dropped.
420
+ function updateFallbackLocaleWithRoot(fallback) {
421
+ withRootFallbackContext(() => coreBase.updateFallbackLocale(_context, _locale.value, fallback));
422
+ }
414
423
  // fallbackLocale
415
424
  const fallbackLocale = Vue.computed({
416
425
  get: () => _fallbackLocale.value,
@@ -455,17 +464,9 @@ function createComposer(options = {}) {
455
464
  let ret;
456
465
  try {
457
466
  if (false || false) ;
458
- if (!_isGlobal) {
459
- _context.fallbackContext = __root
460
- ? coreBase.getFallbackContext()
461
- : undefined;
462
- }
463
- ret = fn(_context);
467
+ ret = withRootFallbackContext(() => fn(_context));
464
468
  }
465
469
  finally {
466
- if (!_isGlobal) {
467
- _context.fallbackContext = undefined;
468
- }
469
470
  }
470
471
  if ((warnType !== 'translate exists' && // for not `te` (e.g `t`)
471
472
  shared.isNumber(ret) &&
@@ -563,9 +564,7 @@ function createComposer(options = {}) {
563
564
  if (resolved === null) {
564
565
  resolved = message[key];
565
566
  }
566
- if (coreBase.isMessageAST(resolved) ||
567
- coreBase.isMessageFunction(resolved) ||
568
- shared.isString(resolved)) {
567
+ if (coreBase.isMessageFormat(resolved)) {
569
568
  return true;
570
569
  }
571
570
  }
@@ -578,7 +577,7 @@ function createComposer(options = {}) {
578
577
  let messages = null;
579
578
  const locales = coreBase.fallbackWithLocaleChain(_context, _fallbackLocale.value, _locale.value);
580
579
  for (let i = 0; i < locales.length; i++) {
581
- const targetLocaleMessages = _messages.value[locales[i]] || {};
580
+ const targetLocaleMessages = shared.getOwn(_messages.value, locales[i]) || {};
582
581
  const messageValue = _context.messageResolver(targetLocaleMessages, key);
583
582
  if (messageValue != null) {
584
583
  messages = messageValue;
@@ -599,66 +598,72 @@ function createComposer(options = {}) {
599
598
  }
600
599
  // getLocaleMessage
601
600
  function getLocaleMessage(locale) {
602
- return (_messages.value[locale] || {});
601
+ return (shared.getOwn(_messages.value, locale) || {});
603
602
  }
604
603
  // setLocaleMessage
605
604
  function setLocaleMessage(locale, message) {
605
+ if (isUnsafeLocale(locale)) {
606
+ return;
607
+ }
606
608
  if (flatJson) {
607
- const _message = { [locale]: message };
608
- for (const key in _message) {
609
- if (shared.hasOwn(_message, key)) {
610
- handleFlatJson(_message[key]);
611
- }
612
- }
613
- message = _message[locale];
609
+ handleFlatJson(message);
614
610
  }
615
611
  _messages.value[locale] = message;
616
612
  _context.messages = _messages.value;
617
613
  }
618
614
  // mergeLocaleMessage
619
615
  function mergeLocaleMessage(locale, message) {
620
- _messages.value[locale] = _messages.value[locale] || {};
621
- const _message = { [locale]: message };
616
+ if (isUnsafeLocale(locale)) {
617
+ return;
618
+ }
619
+ _messages.value[locale] = shared.getOwn(_messages.value, locale) || {};
622
620
  if (flatJson) {
623
- for (const key in _message) {
624
- if (shared.hasOwn(_message, key)) {
625
- handleFlatJson(_message[key]);
626
- }
627
- }
621
+ handleFlatJson(message);
628
622
  }
629
- message = _message[locale];
630
623
  shared.deepCopy(message, _messages.value[locale]);
631
624
  _context.messages = _messages.value;
632
625
  }
633
626
  // getDateTimeFormat
634
627
  function getDateTimeFormat(locale) {
635
- return _datetimeFormats.value[locale] || {};
628
+ return shared.getOwn(_datetimeFormats.value, locale) || {};
636
629
  }
637
630
  // setDateTimeFormat
638
631
  function setDateTimeFormat(locale, format) {
632
+ if (isUnsafeLocale(locale)) {
633
+ return;
634
+ }
639
635
  _datetimeFormats.value[locale] = format;
640
636
  _context.datetimeFormats = _datetimeFormats.value;
641
637
  coreBase.clearDateTimeFormat(_context, locale, format);
642
638
  }
643
639
  // mergeDateTimeFormat
644
640
  function mergeDateTimeFormat(locale, format) {
645
- _datetimeFormats.value[locale] = shared.assign(_datetimeFormats.value[locale] || {}, format);
641
+ if (isUnsafeLocale(locale)) {
642
+ return;
643
+ }
644
+ _datetimeFormats.value[locale] = shared.assign(shared.getOwn(_datetimeFormats.value, locale) || {}, format);
646
645
  _context.datetimeFormats = _datetimeFormats.value;
647
646
  coreBase.clearDateTimeFormat(_context, locale, format);
648
647
  }
649
648
  // getNumberFormat
650
649
  function getNumberFormat(locale) {
651
- return _numberFormats.value[locale] || {};
650
+ return shared.getOwn(_numberFormats.value, locale) || {};
652
651
  }
653
652
  // setNumberFormat
654
653
  function setNumberFormat(locale, format) {
654
+ if (isUnsafeLocale(locale)) {
655
+ return;
656
+ }
655
657
  _numberFormats.value[locale] = format;
656
658
  _context.numberFormats = _numberFormats.value;
657
659
  coreBase.clearNumberFormat(_context, locale, format);
658
660
  }
659
661
  // mergeNumberFormat
660
662
  function mergeNumberFormat(locale, format) {
661
- _numberFormats.value[locale] = shared.assign(_numberFormats.value[locale] || {}, format);
663
+ if (isUnsafeLocale(locale)) {
664
+ return;
665
+ }
666
+ _numberFormats.value[locale] = shared.assign(shared.getOwn(_numberFormats.value, locale) || {}, format);
662
667
  _context.numberFormats = _numberFormats.value;
663
668
  coreBase.clearNumberFormat(_context, locale, format);
664
669
  }
@@ -829,7 +834,10 @@ function convertComposerOptions(options) {
829
834
  const sharedMessages = options.sharedMessages;
830
835
  const locales = Object.keys(sharedMessages);
831
836
  messages = locales.reduce((messages, locale) => {
832
- const message = messages[locale] || (messages[locale] = {});
837
+ if (isUnsafeLocale(locale)) {
838
+ return messages;
839
+ }
840
+ const message = shared.getOwn(messages, locale) || (messages[locale] = {});
833
841
  shared.assign(message, sharedMessages[locale]);
834
842
  return messages;
835
843
  }, (messages || {}));
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * vue-i18n v11.4.13
2
+ * vue-i18n v11.4.14
3
3
  * (c) 2026 kazuya kawaguchi
4
4
  * Released under the MIT License.
5
5
  */
@@ -80,6 +80,27 @@ function hasOwn(obj, key) {
80
80
  return hasOwnProperty.call(obj, key);
81
81
  }
82
82
  /* eslint-enable */
83
+ /**
84
+ * Get `obj[key]` only when `key` is an own property of `obj`, so that a key
85
+ * such as `constructor` does not read a property inherited from
86
+ * `Object.prototype`.
87
+ * The property is read before the check, so that a reactive `obj` tracks
88
+ * `key` even when it is missing.
89
+ */
90
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
91
+ function getOwn(obj, key) {
92
+ const value = obj[key];
93
+ return hasOwn(obj, key) ? value : undefined;
94
+ }
95
+ /**
96
+ * Whether `key` is a property name of `Object.prototype`, such as `__proto__`,
97
+ * `constructor` or `toString`.
98
+ * On a plain object, such a key reads a built-in, and assigning to `__proto__`
99
+ * replaces the prototype.
100
+ */
101
+ function isObjectPrototypeKey(key) {
102
+ return hasOwn(Object.prototype, key);
103
+ }
83
104
  /**
84
105
  * Useful Utilities By Evan you
85
106
  * Modified by kazuya kawaguchi
@@ -189,6 +210,10 @@ const colonCharacterReferencePattern = /&colon;?/gi;
189
210
  const controlOrWhitespacePattern = /[\u0000-\u0020\u007f-\u009f]/g;
190
211
  const eventHandlerPattern = /(?:^|[\s"'<>/])on\w+\s*=\s*["']?[^"'>]+["']?/i;
191
212
  const eventHandlerAttributePattern = /(^|[\s"'<>/])on(\w+\s*=)/gi;
213
+ // `srcdoc` holds a whole HTML document: its value is entity-decoded and then parsed again,
214
+ // so neither escaping the value nor neutralizing `on*` inside it makes it inert
215
+ const srcdocPattern = /(?:^|[\s"'<>/])srcdoc\s*=/i;
216
+ const srcdocAttributePattern = /(^|[\s"'<>/])s(rcdoc\s*=)/gi;
192
217
  const unquotedUrlAttributePattern = /(^|[\s"'<>/])((?:href|src|action|formaction)\s*=\s*)([^\s"'=<>`]+)/gi;
193
218
  function decodeNumericCharacterReference(match, hex, decimal) {
194
219
  const digits = hex || decimal;
@@ -276,6 +301,15 @@ function sanitizeTranslatedHtml(html) {
276
301
  // Neutralize event handler attributes by escaping 'on'
277
302
  html = html.replace(eventHandlerAttributePattern, '$1&#111;n$2');
278
303
  }
304
+ // Detect and neutralize `srcdoc` attributes
305
+ if (srcdocPattern.test(html)) {
306
+ {
307
+ warn('Potentially dangerous srcdoc attribute detected in translation. ' +
308
+ 'Consider removing srcdoc from your translation messages.');
309
+ }
310
+ // Neutralize `srcdoc` by escaping 's': character references are not decoded in attribute names
311
+ html = html.replace(srcdocAttributePattern, '$1&#115;$2');
312
+ }
279
313
  // Disable javascript: URLs in unquoted attributes
280
314
  html = html.replace(unquotedUrlAttributePattern, (match, boundary, prefix, attrValue) => hasJavascriptScheme(attrValue) ? `${boundary}${prefix}about:blank` : match);
281
315
  return html;
@@ -325,8 +359,12 @@ function createEmitter() {
325
359
  const isNotObjectOrIsArray = (val) => !isObject(val) || isArray(val);
326
360
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
327
361
  function deepCopy(src, des) {
328
- // src and des should both be objects, and none of them can be a array
329
- if (isNotObjectOrIsArray(src) || isNotObjectOrIsArray(des)) {
362
+ // src and des should both be objects, and none of them can be a array.
363
+ // des can't be `Object.prototype` either: copying into it would pollute
364
+ // every object
365
+ if (isNotObjectOrIsArray(src) ||
366
+ isNotObjectOrIsArray(des) ||
367
+ des === Object.prototype) {
330
368
  throw new Error('Invalid value');
331
369
  }
332
370
  const stack = [{ src, des }];
@@ -2089,9 +2127,7 @@ function compile(message, context) {
2089
2127
  // compose message function from AST
2090
2128
  const msg = format(ast);
2091
2129
  // if occurred compile error, don't cache
2092
- return !detectError
2093
- ? (compileCache[cacheKey] = msg)
2094
- : msg;
2130
+ return !detectError ? (compileCache[cacheKey] = msg) : msg;
2095
2131
  }
2096
2132
  else {
2097
2133
  if (!isMessageAST(message)) {
@@ -2106,8 +2142,7 @@ function compile(message, context) {
2106
2142
  return cached;
2107
2143
  }
2108
2144
  // compose message function from message (AST)
2109
- return (compileCache[cacheKey] =
2110
- format(message));
2145
+ return (compileCache[cacheKey] = format(message));
2111
2146
  }
2112
2147
  else {
2113
2148
  return format(message);
@@ -2650,7 +2685,7 @@ function getWarnMessage$1(code, ...args) {
2650
2685
  * Intlify core-base version
2651
2686
  * @internal
2652
2687
  */
2653
- const VERSION$1 = '11.4.13';
2688
+ const VERSION$1 = '11.4.14';
2654
2689
  const NOT_REOSLVED = -1;
2655
2690
  const DEFAULT_LOCALE = 'en-US';
2656
2691
  const MISSING_RESOLVE_VALUE = '';
@@ -3317,6 +3352,8 @@ function createMessageContext(options = {}) {
3317
3352
 
3318
3353
  const NOOP_MESSAGE_FUNCTION = () => '';
3319
3354
  const isMessageFunction = (val) => isFunction(val);
3355
+ /** @internal */
3356
+ const isMessageFormat = (val) => isString(val) || isMessageAST(val) || isMessageFunction(val);
3320
3357
  // implementation of `translate` function
3321
3358
  function translate(context, ...args) {
3322
3359
  const { fallbackFormat, postTranslation, unresolving, messageCompiler, fallbackLocale, messages } = context;
@@ -3362,10 +3399,7 @@ function translate(context, ...args) {
3362
3399
  let format = formatScope;
3363
3400
  // if you use default message, set it as message format!
3364
3401
  let cacheBaseKey = key;
3365
- if (!resolvedMessage &&
3366
- !(isString(format) ||
3367
- isMessageAST(format) ||
3368
- isMessageFunction(format))) {
3402
+ if (!resolvedMessage && !isMessageFormat(format)) {
3369
3403
  if (enableDefaultMsg) {
3370
3404
  format = defaultMsgOrKey;
3371
3405
  cacheBaseKey = format;
@@ -3373,10 +3407,7 @@ function translate(context, ...args) {
3373
3407
  }
3374
3408
  // checking message format and target locale
3375
3409
  if (!resolvedMessage &&
3376
- (!(isString(format) ||
3377
- isMessageAST(format) ||
3378
- isMessageFunction(format)) ||
3379
- !isString(targetLocale))) {
3410
+ (!isMessageFormat(format) || !isString(targetLocale))) {
3380
3411
  return unresolving ? NOT_REOSLVED : key;
3381
3412
  }
3382
3413
  // TODO: refactor
@@ -3438,15 +3469,19 @@ function translate(context, ...args) {
3438
3469
  return ret;
3439
3470
  }
3440
3471
  function escapeParams(options) {
3472
+ // a call can pass both list and named values, so escape each of them
3441
3473
  if (isArray(options.list)) {
3442
3474
  options.list = options.list.map(item => isString(item) ? escapeHtml(item) : item);
3443
3475
  }
3444
- else if (isObject(options.named)) {
3445
- Object.keys(options.named).forEach(key => {
3446
- if (isString(options.named[key])) {
3447
- options.named[key] = escapeHtml(options.named[key]);
3448
- }
3449
- });
3476
+ if (isObject(options.named)) {
3477
+ // escape into a new object, so that the values of the caller are not rewritten
3478
+ const source = options.named;
3479
+ const named = create();
3480
+ for (const key of Object.keys(source)) {
3481
+ const value = source[key];
3482
+ named[key] = isString(value) ? escapeHtml(value) : value;
3483
+ }
3484
+ options.named = named;
3450
3485
  }
3451
3486
  }
3452
3487
  function resolveMessageFormat(context, key, locale, fallbackLocale, fallbackWarn, missingWarn) {
@@ -3515,7 +3550,7 @@ function resolveMessageFormat(context, key, locale, fallbackLocale, fallbackWarn
3515
3550
  measure('intlify message resolve', startTag, endTag);
3516
3551
  }
3517
3552
  }
3518
- if (isString(format) || isMessageAST(format) || isMessageFunction(format)) {
3553
+ if (isMessageFormat(format)) {
3519
3554
  break;
3520
3555
  }
3521
3556
  if (!isImplicitFallback(targetLocale, locales)) {
@@ -3744,7 +3779,7 @@ function getMessageContextOptions(context, locale, message, options) {
3744
3779
  *
3745
3780
  * @VueI18nGeneral
3746
3781
  */
3747
- const VERSION = '11.4.13';
3782
+ const VERSION = '11.4.14';
3748
3783
  /**
3749
3784
  * This is only called development env
3750
3785
  * istanbul-ignore-next
@@ -3821,7 +3856,8 @@ const I18nWarnCodes = {
3821
3856
  DEPRECATE_TRANSLATE_CUSTOME_DIRECTIVE: (CORE_WARN_CODES_EXTEND_POINT +
3822
3857
  4),
3823
3858
  // duplicate `useI18n` calling
3824
- DUPLICATE_USE_I18N_CALLING: (CORE_WARN_CODES_EXTEND_POINT + 5)
3859
+ DUPLICATE_USE_I18N_CALLING: (CORE_WARN_CODES_EXTEND_POINT + 5),
3860
+ IGNORE_UNSAFE_LOCALE: (CORE_WARN_CODES_EXTEND_POINT + 6)
3825
3861
  };
3826
3862
  const warnMessages = {
3827
3863
  [I18nWarnCodes.FALLBACK_TO_ROOT]: `Fall back to {type} '{key}' with root locale.`,
@@ -3835,13 +3871,26 @@ const warnMessages = {
3835
3871
  * @deprecated will be removed at vue-i18n v12
3836
3872
  */
3837
3873
  [I18nWarnCodes.DEPRECATE_TRANSLATE_CUSTOME_DIRECTIVE]: `'v-t' has been deprecated in v11. Use translate APIs ('t' or '$t') instead.`,
3838
- [I18nWarnCodes.DUPLICATE_USE_I18N_CALLING]: "Duplicate `useI18n` calling by local scope. Please don't call it on local scope, due to it does not work properly in component."
3874
+ [I18nWarnCodes.DUPLICATE_USE_I18N_CALLING]: "Duplicate `useI18n` calling by local scope. Please don't call it on local scope, due to it does not work properly in component.",
3875
+ [I18nWarnCodes.IGNORE_UNSAFE_LOCALE]: `Ignore '{locale}' locale: it is a property name of Object.prototype`
3839
3876
  };
3840
3877
  function getWarnMessage(code, ...args) {
3841
3878
  return format$1(warnMessages[code], ...args);
3842
3879
  }
3843
3880
 
3844
3881
  /* eslint-disable @typescript-eslint/no-explicit-any */
3882
+ /**
3883
+ * Whether `locale` can't be a key of the locale messages and formats.
3884
+ * A property name of `Object.prototype`, such as `__proto__` or `constructor`,
3885
+ * would read or replace a built-in object, so it is ignored with a warning.
3886
+ */
3887
+ function isUnsafeLocale(locale) {
3888
+ if (isObjectPrototypeKey(locale)) {
3889
+ warn(getWarnMessage(I18nWarnCodes.IGNORE_UNSAFE_LOCALE, { locale }));
3890
+ return true;
3891
+ }
3892
+ return false;
3893
+ }
3845
3894
  /**
3846
3895
  * Transform flat json in obj to normal json in obj
3847
3896
  */
@@ -3929,7 +3978,10 @@ function getLocaleMessages(locale, options) {
3929
3978
  if ('locale' in custom && 'resource' in custom) {
3930
3979
  const { locale, resource } = custom;
3931
3980
  if (locale) {
3932
- ret[locale] = ret[locale] || create();
3981
+ if (isUnsafeLocale(locale)) {
3982
+ return;
3983
+ }
3984
+ ret[locale] = getOwn(ret, locale) || create();
3933
3985
  deepCopy(resource, ret[locale]);
3934
3986
  }
3935
3987
  else {
@@ -4116,7 +4168,6 @@ function createComposer(options = {}) {
4116
4168
  // pluralRules
4117
4169
  let _pluralRules = options.pluralRules || (__root && __root.pluralRules);
4118
4170
  // runtime context
4119
- // eslint-disable-next-line prefer-const
4120
4171
  let _context;
4121
4172
  const getCoreContext = () => {
4122
4173
  _isGlobal && setFallbackContext(null);
@@ -4142,22 +4193,12 @@ function createComposer(options = {}) {
4142
4193
  {
4143
4194
  ctxOptions.datetimeFormats = _datetimeFormats.value;
4144
4195
  ctxOptions.numberFormats = _numberFormats.value;
4145
- ctxOptions.__datetimeFormatters = isPlainObject(_context)
4146
- ? _context.__datetimeFormatters
4147
- : undefined;
4148
- ctxOptions.__numberFormatters = isPlainObject(_context)
4149
- ? _context.__numberFormatters
4150
- : undefined;
4151
- }
4152
- {
4153
- ctxOptions.__v_emitter = isPlainObject(_context)
4154
- ? _context.__v_emitter
4155
- : undefined;
4156
4196
  }
4157
4197
  const ctx = createCoreContext(ctxOptions);
4158
4198
  _isGlobal && setFallbackContext(ctx);
4159
4199
  return ctx;
4160
4200
  };
4201
+ // eslint-disable-next-line prefer-const
4161
4202
  _context = getCoreContext();
4162
4203
  updateFallbackLocale(_context, _locale.value, _fallbackLocale.value);
4163
4204
  // track reactivity
@@ -4179,17 +4220,12 @@ function createComposer(options = {}) {
4179
4220
  _locale.value = val;
4180
4221
  }
4181
4222
  });
4182
- // NOTE: `_context.fallbackContext` points at the root context only while a
4183
- // translation runs (see `wrapWithDeps`). Point it there while invalidating too,
4184
- // so that the chain the root cached for this composer's fallbackLocale is dropped.
4185
- function updateFallbackLocaleWithRoot(fallback) {
4223
+ function withRootFallbackContext(fn) {
4186
4224
  if (!_isGlobal) {
4187
- _context.fallbackContext = __root
4188
- ? getFallbackContext()
4189
- : undefined;
4225
+ _context.fallbackContext = getFallbackContext();
4190
4226
  }
4191
4227
  try {
4192
- updateFallbackLocale(_context, _locale.value, fallback);
4228
+ return fn();
4193
4229
  }
4194
4230
  finally {
4195
4231
  if (!_isGlobal) {
@@ -4197,6 +4233,12 @@ function createComposer(options = {}) {
4197
4233
  }
4198
4234
  }
4199
4235
  }
4236
+ // NOTE: `_context.fallbackContext` points at the root context only while a
4237
+ // translation runs (see `wrapWithDeps`). Point it there while invalidating too,
4238
+ // so that the chain the root cached for this composer's fallbackLocale is dropped.
4239
+ function updateFallbackLocaleWithRoot(fallback) {
4240
+ withRootFallbackContext(() => updateFallbackLocale(_context, _locale.value, fallback));
4241
+ }
4200
4242
  // fallbackLocale
4201
4243
  const fallbackLocale = computed({
4202
4244
  get: () => _fallbackLocale.value,
@@ -4246,17 +4288,9 @@ function createComposer(options = {}) {
4246
4288
  if (true || false) {
4247
4289
  setAdditionalMeta(getMetaInfo());
4248
4290
  }
4249
- if (!_isGlobal) {
4250
- _context.fallbackContext = __root
4251
- ? getFallbackContext()
4252
- : undefined;
4253
- }
4254
- ret = fn(_context);
4291
+ ret = withRootFallbackContext(() => fn(_context));
4255
4292
  }
4256
4293
  finally {
4257
- if (!_isGlobal) {
4258
- _context.fallbackContext = undefined;
4259
- }
4260
4294
  }
4261
4295
  if ((warnType !== 'translate exists' && // for not `te` (e.g `t`)
4262
4296
  isNumber(ret) &&
@@ -4378,9 +4412,7 @@ function createComposer(options = {}) {
4378
4412
  if (resolved === null) {
4379
4413
  resolved = message[key];
4380
4414
  }
4381
- if (isMessageAST(resolved) ||
4382
- isMessageFunction(resolved) ||
4383
- isString(resolved)) {
4415
+ if (isMessageFormat(resolved)) {
4384
4416
  return true;
4385
4417
  }
4386
4418
  }
@@ -4393,7 +4425,7 @@ function createComposer(options = {}) {
4393
4425
  let messages = null;
4394
4426
  const locales = fallbackWithLocaleChain(_context, _fallbackLocale.value, _locale.value);
4395
4427
  for (let i = 0; i < locales.length; i++) {
4396
- const targetLocaleMessages = _messages.value[locales[i]] || {};
4428
+ const targetLocaleMessages = getOwn(_messages.value, locales[i]) || {};
4397
4429
  const messageValue = _context.messageResolver(targetLocaleMessages, key);
4398
4430
  if (messageValue != null) {
4399
4431
  messages = messageValue;
@@ -4414,66 +4446,72 @@ function createComposer(options = {}) {
4414
4446
  }
4415
4447
  // getLocaleMessage
4416
4448
  function getLocaleMessage(locale) {
4417
- return (_messages.value[locale] || {});
4449
+ return (getOwn(_messages.value, locale) || {});
4418
4450
  }
4419
4451
  // setLocaleMessage
4420
4452
  function setLocaleMessage(locale, message) {
4453
+ if (isUnsafeLocale(locale)) {
4454
+ return;
4455
+ }
4421
4456
  if (flatJson) {
4422
- const _message = { [locale]: message };
4423
- for (const key in _message) {
4424
- if (hasOwn(_message, key)) {
4425
- handleFlatJson(_message[key]);
4426
- }
4427
- }
4428
- message = _message[locale];
4457
+ handleFlatJson(message);
4429
4458
  }
4430
4459
  _messages.value[locale] = message;
4431
4460
  _context.messages = _messages.value;
4432
4461
  }
4433
4462
  // mergeLocaleMessage
4434
4463
  function mergeLocaleMessage(locale, message) {
4435
- _messages.value[locale] = _messages.value[locale] || {};
4436
- const _message = { [locale]: message };
4464
+ if (isUnsafeLocale(locale)) {
4465
+ return;
4466
+ }
4467
+ _messages.value[locale] = getOwn(_messages.value, locale) || {};
4437
4468
  if (flatJson) {
4438
- for (const key in _message) {
4439
- if (hasOwn(_message, key)) {
4440
- handleFlatJson(_message[key]);
4441
- }
4442
- }
4469
+ handleFlatJson(message);
4443
4470
  }
4444
- message = _message[locale];
4445
4471
  deepCopy(message, _messages.value[locale]);
4446
4472
  _context.messages = _messages.value;
4447
4473
  }
4448
4474
  // getDateTimeFormat
4449
4475
  function getDateTimeFormat(locale) {
4450
- return _datetimeFormats.value[locale] || {};
4476
+ return getOwn(_datetimeFormats.value, locale) || {};
4451
4477
  }
4452
4478
  // setDateTimeFormat
4453
4479
  function setDateTimeFormat(locale, format) {
4480
+ if (isUnsafeLocale(locale)) {
4481
+ return;
4482
+ }
4454
4483
  _datetimeFormats.value[locale] = format;
4455
4484
  _context.datetimeFormats = _datetimeFormats.value;
4456
4485
  clearDateTimeFormat(_context, locale, format);
4457
4486
  }
4458
4487
  // mergeDateTimeFormat
4459
4488
  function mergeDateTimeFormat(locale, format) {
4460
- _datetimeFormats.value[locale] = assign(_datetimeFormats.value[locale] || {}, format);
4489
+ if (isUnsafeLocale(locale)) {
4490
+ return;
4491
+ }
4492
+ _datetimeFormats.value[locale] = assign(getOwn(_datetimeFormats.value, locale) || {}, format);
4461
4493
  _context.datetimeFormats = _datetimeFormats.value;
4462
4494
  clearDateTimeFormat(_context, locale, format);
4463
4495
  }
4464
4496
  // getNumberFormat
4465
4497
  function getNumberFormat(locale) {
4466
- return _numberFormats.value[locale] || {};
4498
+ return getOwn(_numberFormats.value, locale) || {};
4467
4499
  }
4468
4500
  // setNumberFormat
4469
4501
  function setNumberFormat(locale, format) {
4502
+ if (isUnsafeLocale(locale)) {
4503
+ return;
4504
+ }
4470
4505
  _numberFormats.value[locale] = format;
4471
4506
  _context.numberFormats = _numberFormats.value;
4472
4507
  clearNumberFormat(_context, locale, format);
4473
4508
  }
4474
4509
  // mergeNumberFormat
4475
4510
  function mergeNumberFormat(locale, format) {
4476
- _numberFormats.value[locale] = assign(_numberFormats.value[locale] || {}, format);
4511
+ if (isUnsafeLocale(locale)) {
4512
+ return;
4513
+ }
4514
+ _numberFormats.value[locale] = assign(getOwn(_numberFormats.value, locale) || {}, format);
4477
4515
  _context.numberFormats = _numberFormats.value;
4478
4516
  clearNumberFormat(_context, locale, format);
4479
4517
  }
@@ -5204,7 +5242,10 @@ function convertComposerOptions(options) {
5204
5242
  const sharedMessages = options.sharedMessages;
5205
5243
  const locales = Object.keys(sharedMessages);
5206
5244
  messages = locales.reduce((messages, locale) => {
5207
- const message = messages[locale] || (messages[locale] = {});
5245
+ if (isUnsafeLocale(locale)) {
5246
+ return messages;
5247
+ }
5248
+ const message = getOwn(messages, locale) || (messages[locale] = {});
5208
5249
  assign(message, sharedMessages[locale]);
5209
5250
  return messages;
5210
5251
  }, (messages || {}));