@intlify/core-base 11.4.7 → 11.4.9

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,5 +1,5 @@
1
1
  /*!
2
- * core-base v11.4.7
2
+ * core-base v11.4.9
3
3
  * (c) 2026 kazuya kawaguchi
4
4
  * Released under the MIT License.
5
5
  */
@@ -321,7 +321,7 @@ function resolveLocale(locale) {
321
321
  * @remarks
322
322
  * A fallback locale function implemented with a simple fallback algorithm.
323
323
  *
324
- * Basically, it returns the value as specified in the `fallbackLocale` props, and is processed with the fallback inside intlify.
324
+ * Basically, the chain consists of `start` plus the specified fallback: for a string or array `fallbackLocale`, the value as specified in the props is used; for a map `fallbackLocale`, the map's **keys** are used and the `default` key is skipped since it is not a locale name.
325
325
  *
326
326
  * @param ctx - A {@link CoreContext | context}
327
327
  * @param fallback - A {@link FallbackLocale | fallback locale}
@@ -338,7 +338,7 @@ function fallbackWithSimple(ctx, fallback, start) {
338
338
  ...(shared.isArray(fallback)
339
339
  ? fallback
340
340
  : shared.isObject(fallback)
341
- ? Object.keys(fallback)
341
+ ? Object.keys(fallback).filter(locale => locale !== 'default')
342
342
  : shared.isString(fallback)
343
343
  ? [fallback]
344
344
  : [start])
@@ -366,29 +366,36 @@ function fallbackWithLocaleChain(ctx, fallback, start) {
366
366
  if (!context.__localeChainCache) {
367
367
  context.__localeChainCache = new Map();
368
368
  }
369
- let chain = context.__localeChainCache.get(startLocale);
370
- if (!chain) {
371
- chain = [];
372
- // first block defined by start
373
- let block = [start];
374
- // while any intervening block found
375
- while (shared.isArray(block)) {
376
- block = appendBlockToChain(chain, block, fallback);
377
- }
378
- // prettier-ignore
379
- // last block defined by default
380
- const defaults = shared.isArray(fallback) || !shared.isPlainObject(fallback)
381
- ? fallback
382
- : fallback['default']
383
- ? fallback['default']
384
- : null;
385
- // convert defaults to array
386
- block = shared.isString(defaults) ? [defaults] : defaults;
387
- if (shared.isArray(block)) {
388
- appendBlockToChain(chain, block, false);
389
- }
390
- context.__localeChainCache.set(startLocale, chain);
369
+ const fallbackKey = shared.friendlyJSONstringify(fallback);
370
+ let chains = context.__localeChainCache.get(startLocale);
371
+ if (!chains) {
372
+ chains = new Map();
373
+ context.__localeChainCache.set(startLocale, chains);
374
+ }
375
+ const cached = chains.get(fallbackKey);
376
+ if (cached) {
377
+ return cached;
391
378
  }
379
+ const chain = [];
380
+ // first block defined by start
381
+ let block = [start];
382
+ // while any intervening block found
383
+ while (shared.isArray(block)) {
384
+ block = appendBlockToChain(chain, block, fallback);
385
+ }
386
+ // prettier-ignore
387
+ // last block defined by default
388
+ const defaults = shared.isArray(fallback) || !shared.isPlainObject(fallback)
389
+ ? fallback
390
+ : fallback['default']
391
+ ? fallback['default']
392
+ : null;
393
+ // convert defaults to array
394
+ block = shared.isString(defaults) ? [defaults] : defaults;
395
+ if (shared.isArray(block)) {
396
+ appendBlockToChain(chain, block, false);
397
+ }
398
+ chains.set(fallbackKey, chain);
392
399
  return chain;
393
400
  }
394
401
  function appendBlockToChain(chain, block, blocks) {
@@ -747,7 +754,7 @@ function getWarnMessage(code, ...args) {
747
754
  * Intlify core-base version
748
755
  * @internal
749
756
  */
750
- const VERSION = '11.4.7';
757
+ const VERSION = '11.4.9';
751
758
  const NOT_REOSLVED = -1;
752
759
  const DEFAULT_LOCALE = 'en-US';
753
760
  const MISSING_RESOLVE_VALUE = '';
@@ -997,6 +1004,88 @@ function isImplicitFallback(targetLocale, locales) {
997
1004
  }
998
1005
  /* eslint-enable @typescript-eslint/no-explicit-any */
999
1006
 
1007
+ function resolveFormatLocale(context, key, locale, formats, missingWarn, fallbackWarn, type) {
1008
+ const { fallbackLocale, localeFallbacker, onWarn } = context;
1009
+ const locales = localeFallbacker(context, // eslint-disable-line @typescript-eslint/no-explicit-any
1010
+ fallbackLocale, locale);
1011
+ let from = locale;
1012
+ for (let i = 0; i < locales.length; i++) {
1013
+ const targetLocale = locales[i];
1014
+ if (locale !== targetLocale &&
1015
+ isTranslateFallbackWarn(fallbackWarn, key)) {
1016
+ onWarn(getWarnMessage(type === 'datetime format'
1017
+ ? CoreWarnCodes.FALLBACK_TO_DATE_FORMAT
1018
+ : CoreWarnCodes.FALLBACK_TO_NUMBER_FORMAT, {
1019
+ key,
1020
+ target: targetLocale
1021
+ }));
1022
+ }
1023
+ if (locale !== targetLocale) {
1024
+ const emitter = context.__v_emitter;
1025
+ if (emitter) {
1026
+ emitter.emit('fallback', {
1027
+ type,
1028
+ key,
1029
+ from,
1030
+ to: targetLocale,
1031
+ groupId: `${type}:${key}`
1032
+ });
1033
+ }
1034
+ }
1035
+ const format = (formats[targetLocale] || {})[key];
1036
+ if (shared.isPlainObject(format) && shared.isString(targetLocale)) {
1037
+ return targetLocale;
1038
+ }
1039
+ handleMissing(context, key, targetLocale, missingWarn, type);
1040
+ from = targetLocale;
1041
+ }
1042
+ return null;
1043
+ }
1044
+ function getFormatterCacheKey(locale, key, overrides) {
1045
+ let id = `${locale}__${key}`;
1046
+ if (shared.isPlainObject(overrides) && !shared.isEmptyObject(overrides)) {
1047
+ id = `${id}__${JSON.stringify(overrides)}`;
1048
+ }
1049
+ return id;
1050
+ }
1051
+ function clearFormatCache(formatters, locale, format) {
1052
+ for (const key in format) {
1053
+ const prefix = `${locale}__${key}`;
1054
+ for (const id of formatters.keys()) {
1055
+ if (id === prefix || id.startsWith(`${prefix}__`)) {
1056
+ formatters.delete(id);
1057
+ }
1058
+ }
1059
+ }
1060
+ }
1061
+ function parseFormatArgs(args, options, initialOverrides, optionsKeys) {
1062
+ const [, arg2, arg3, arg4] = args;
1063
+ let overrides = initialOverrides;
1064
+ if (shared.isString(arg2)) {
1065
+ options.key = arg2;
1066
+ }
1067
+ else if (shared.isPlainObject(arg2)) {
1068
+ Object.keys(arg2).forEach(key => {
1069
+ if (optionsKeys.includes(key)) {
1070
+ overrides[key] = arg2[key];
1071
+ }
1072
+ else {
1073
+ options[key] = arg2[key];
1074
+ }
1075
+ });
1076
+ }
1077
+ if (shared.isString(arg3)) {
1078
+ options.locale = arg3;
1079
+ }
1080
+ else if (shared.isPlainObject(arg3)) {
1081
+ overrides = arg3;
1082
+ }
1083
+ if (shared.isPlainObject(arg4)) {
1084
+ overrides = arg4;
1085
+ }
1086
+ return overrides;
1087
+ }
1088
+
1000
1089
  const intlDefined = typeof Intl !== 'undefined';
1001
1090
  const Availabilities = {
1002
1091
  dateTimeFormat: intlDefined && typeof Intl.DateTimeFormat !== 'undefined',
@@ -1005,7 +1094,7 @@ const Availabilities = {
1005
1094
 
1006
1095
  // implementation of `datetime` function
1007
1096
  function datetime(context, ...args) {
1008
- const { datetimeFormats, unresolving, fallbackLocale, onWarn, localeFallbacker } = context;
1097
+ const { datetimeFormats, unresolving, onWarn } = context;
1009
1098
  const { __datetimeFormatters } = context;
1010
1099
  if (!Availabilities.dateTimeFormat) {
1011
1100
  onWarn(getWarnMessage(CoreWarnCodes.CANNOT_FORMAT_DATE));
@@ -1028,57 +1117,16 @@ function datetime(context, ...args) {
1028
1117
  : context.fallbackWarn;
1029
1118
  const part = !!options.part;
1030
1119
  const locale = getLocale(context, options);
1031
- const locales = localeFallbacker(context, // eslint-disable-line @typescript-eslint/no-explicit-any
1032
- fallbackLocale, locale);
1033
1120
  if (!shared.isString(key) || key === '') {
1034
1121
  const formatter = new Intl.DateTimeFormat(locale.replace(/!/g, ''), overrides);
1035
1122
  return !part ? formatter.format(value) : formatter.formatToParts(value);
1036
1123
  }
1037
- // resolve format
1038
- let datetimeFormat = {};
1039
- let targetLocale;
1040
- let format = null;
1041
- let from = locale;
1042
- let to = null;
1043
- const type = 'datetime format';
1044
- for (let i = 0; i < locales.length; i++) {
1045
- targetLocale = to = locales[i];
1046
- if (locale !== targetLocale &&
1047
- isTranslateFallbackWarn(fallbackWarn, key)) {
1048
- onWarn(getWarnMessage(CoreWarnCodes.FALLBACK_TO_DATE_FORMAT, {
1049
- key,
1050
- target: targetLocale
1051
- }));
1052
- }
1053
- // for vue-devtools timeline event
1054
- if (locale !== targetLocale) {
1055
- const emitter = context.__v_emitter;
1056
- if (emitter) {
1057
- emitter.emit('fallback', {
1058
- type,
1059
- key,
1060
- from,
1061
- to,
1062
- groupId: `${type}:${key}`
1063
- });
1064
- }
1065
- }
1066
- datetimeFormat =
1067
- datetimeFormats[targetLocale] || {};
1068
- format = datetimeFormat[key];
1069
- if (shared.isPlainObject(format))
1070
- break;
1071
- handleMissing(context, key, targetLocale, missingWarn, type); // eslint-disable-line @typescript-eslint/no-explicit-any
1072
- from = to;
1073
- }
1074
- // checking format and target locale
1075
- if (!shared.isPlainObject(format) || !shared.isString(targetLocale)) {
1124
+ const targetLocale = resolveFormatLocale(context, key, locale, datetimeFormats, missingWarn, fallbackWarn, 'datetime format');
1125
+ if (!shared.isString(targetLocale)) {
1076
1126
  return unresolving ? NOT_REOSLVED : key;
1077
1127
  }
1078
- let id = `${targetLocale}__${key}`;
1079
- if (!shared.isEmptyObject(overrides)) {
1080
- id = `${id}__${JSON.stringify(overrides)}`;
1081
- }
1128
+ const format = datetimeFormats[targetLocale][key];
1129
+ const id = getFormatterCacheKey(targetLocale, key, overrides);
1082
1130
  let formatter = __datetimeFormatters.get(id);
1083
1131
  if (!formatter) {
1084
1132
  formatter = new Intl.DateTimeFormat(targetLocale, shared.assign({}, format, overrides));
@@ -1111,9 +1159,9 @@ const DATETIME_FORMAT_OPTIONS_KEYS = [
1111
1159
  ];
1112
1160
  /** @internal */
1113
1161
  function parseDateTimeArgs(...args) {
1114
- const [arg1, arg2, arg3, arg4] = args;
1162
+ const [arg1] = args;
1115
1163
  const options = shared.create();
1116
- let overrides = shared.create();
1164
+ const initialOverrides = shared.create();
1117
1165
  let value;
1118
1166
  if (shared.isString(arg1)) {
1119
1167
  // Only allow ISO strings - other date formats are often supported,
@@ -1150,45 +1198,18 @@ function parseDateTimeArgs(...args) {
1150
1198
  else {
1151
1199
  throw createCoreError(CoreErrorCodes.INVALID_ARGUMENT);
1152
1200
  }
1153
- if (shared.isString(arg2)) {
1154
- options.key = arg2;
1155
- }
1156
- else if (shared.isPlainObject(arg2)) {
1157
- Object.keys(arg2).forEach(key => {
1158
- if (DATETIME_FORMAT_OPTIONS_KEYS.includes(key)) {
1159
- overrides[key] = arg2[key];
1160
- }
1161
- else {
1162
- options[key] = arg2[key];
1163
- }
1164
- });
1165
- }
1166
- if (shared.isString(arg3)) {
1167
- options.locale = arg3;
1168
- }
1169
- else if (shared.isPlainObject(arg3)) {
1170
- overrides = arg3;
1171
- }
1172
- if (shared.isPlainObject(arg4)) {
1173
- overrides = arg4;
1174
- }
1201
+ const overrides = parseFormatArgs(args, options, initialOverrides, DATETIME_FORMAT_OPTIONS_KEYS);
1175
1202
  return [options.key || '', value, options, overrides];
1176
1203
  }
1177
1204
  /** @internal */
1178
1205
  function clearDateTimeFormat(ctx, locale, format) {
1179
1206
  const context = ctx;
1180
- for (const key in format) {
1181
- const id = `${locale}__${key}`;
1182
- if (!context.__datetimeFormatters.has(id)) {
1183
- continue;
1184
- }
1185
- context.__datetimeFormatters.delete(id);
1186
- }
1207
+ clearFormatCache(context.__datetimeFormatters, locale, format);
1187
1208
  }
1188
1209
 
1189
1210
  // implementation of `number` function
1190
1211
  function number(context, ...args) {
1191
- const { numberFormats, unresolving, fallbackLocale, onWarn, localeFallbacker } = context;
1212
+ const { numberFormats, unresolving, onWarn } = context;
1192
1213
  const { __numberFormatters } = context;
1193
1214
  if (!Availabilities.numberFormat) {
1194
1215
  onWarn(getWarnMessage(CoreWarnCodes.CANNOT_FORMAT_NUMBER));
@@ -1211,57 +1232,16 @@ function number(context, ...args) {
1211
1232
  : context.fallbackWarn;
1212
1233
  const part = !!options.part;
1213
1234
  const locale = getLocale(context, options);
1214
- const locales = localeFallbacker(context, // eslint-disable-line @typescript-eslint/no-explicit-any
1215
- fallbackLocale, locale);
1216
1235
  if (!shared.isString(key) || key === '') {
1217
1236
  const formatter = new Intl.NumberFormat(locale.replace(/!/g, ''), overrides);
1218
1237
  return !part ? formatter.format(value) : formatter.formatToParts(value);
1219
1238
  }
1220
- // resolve format
1221
- let numberFormat = {};
1222
- let targetLocale;
1223
- let format = null;
1224
- let from = locale;
1225
- let to = null;
1226
- const type = 'number format';
1227
- for (let i = 0; i < locales.length; i++) {
1228
- targetLocale = to = locales[i];
1229
- if (locale !== targetLocale &&
1230
- isTranslateFallbackWarn(fallbackWarn, key)) {
1231
- onWarn(getWarnMessage(CoreWarnCodes.FALLBACK_TO_NUMBER_FORMAT, {
1232
- key,
1233
- target: targetLocale
1234
- }));
1235
- }
1236
- // for vue-devtools timeline event
1237
- if (locale !== targetLocale) {
1238
- const emitter = context.__v_emitter;
1239
- if (emitter) {
1240
- emitter.emit('fallback', {
1241
- type,
1242
- key,
1243
- from,
1244
- to,
1245
- groupId: `${type}:${key}`
1246
- });
1247
- }
1248
- }
1249
- numberFormat =
1250
- numberFormats[targetLocale] || {};
1251
- format = numberFormat[key];
1252
- if (shared.isPlainObject(format))
1253
- break;
1254
- handleMissing(context, key, targetLocale, missingWarn, type); // eslint-disable-line @typescript-eslint/no-explicit-any
1255
- from = to;
1256
- }
1257
- // checking format and target locale
1258
- if (!shared.isPlainObject(format) || !shared.isString(targetLocale)) {
1239
+ const targetLocale = resolveFormatLocale(context, key, locale, numberFormats, missingWarn, fallbackWarn, 'number format');
1240
+ if (!shared.isString(targetLocale)) {
1259
1241
  return unresolving ? NOT_REOSLVED : key;
1260
1242
  }
1261
- let id = `${targetLocale}__${key}`;
1262
- if (!shared.isEmptyObject(overrides)) {
1263
- id = `${id}__${JSON.stringify(overrides)}`;
1264
- }
1243
+ const format = numberFormats[targetLocale][key];
1244
+ const id = getFormatterCacheKey(targetLocale, key, overrides);
1265
1245
  let formatter = __numberFormatters.get(id);
1266
1246
  if (!formatter) {
1267
1247
  formatter = new Intl.NumberFormat(targetLocale, shared.assign({}, format, overrides));
@@ -1294,47 +1274,20 @@ const NUMBER_FORMAT_OPTIONS_KEYS = [
1294
1274
  ];
1295
1275
  /** @internal */
1296
1276
  function parseNumberArgs(...args) {
1297
- const [arg1, arg2, arg3, arg4] = args;
1277
+ const [arg1] = args;
1298
1278
  const options = shared.create();
1299
- let overrides = shared.create();
1279
+ const initialOverrides = shared.create();
1300
1280
  if (!shared.isNumber(arg1)) {
1301
1281
  throw createCoreError(CoreErrorCodes.INVALID_ARGUMENT);
1302
1282
  }
1303
1283
  const value = arg1;
1304
- if (shared.isString(arg2)) {
1305
- options.key = arg2;
1306
- }
1307
- else if (shared.isPlainObject(arg2)) {
1308
- Object.keys(arg2).forEach(key => {
1309
- if (NUMBER_FORMAT_OPTIONS_KEYS.includes(key)) {
1310
- overrides[key] = arg2[key];
1311
- }
1312
- else {
1313
- options[key] = arg2[key];
1314
- }
1315
- });
1316
- }
1317
- if (shared.isString(arg3)) {
1318
- options.locale = arg3;
1319
- }
1320
- else if (shared.isPlainObject(arg3)) {
1321
- overrides = arg3;
1322
- }
1323
- if (shared.isPlainObject(arg4)) {
1324
- overrides = arg4;
1325
- }
1284
+ const overrides = parseFormatArgs(args, options, initialOverrides, NUMBER_FORMAT_OPTIONS_KEYS);
1326
1285
  return [options.key || '', value, options, overrides];
1327
1286
  }
1328
1287
  /** @internal */
1329
1288
  function clearNumberFormat(ctx, locale, format) {
1330
1289
  const context = ctx;
1331
- for (const key in format) {
1332
- const id = `${locale}__${key}`;
1333
- if (!context.__numberFormatters.has(id)) {
1334
- continue;
1335
- }
1336
- context.__numberFormatters.delete(id);
1337
- }
1290
+ clearFormatCache(context.__numberFormatters, locale, format);
1338
1291
  }
1339
1292
 
1340
1293
  const DEFAULT_MODIFIER = (str) => str;
@@ -71,7 +71,7 @@ export declare type CoreErrorCodes = (typeof CoreErrorCodes)[keyof typeof CoreEr
71
71
  export declare interface CoreInternalContext {
72
72
  __datetimeFormatters: Map<string, Intl.DateTimeFormat>;
73
73
  __numberFormatters: Map<string, Intl.NumberFormat>;
74
- __localeChainCache?: Map<Locale, Locale[]>;
74
+ __localeChainCache?: Map<Locale, Map<string, Locale[]>>;
75
75
  __v_emitter?: VueDevToolsEmitter;
76
76
  __meta: MetaInfo;
77
77
  }
@@ -373,7 +373,7 @@ export declare function fallbackWithLocaleChain<Message = string>(ctx: CoreConte
373
373
  * @remarks
374
374
  * A fallback locale function implemented with a simple fallback algorithm.
375
375
  *
376
- * Basically, it returns the value as specified in the `fallbackLocale` props, and is processed with the fallback inside intlify.
376
+ * Basically, the chain consists of `start` plus the specified fallback: for a string or array `fallbackLocale`, the value as specified in the props is used; for a map `fallbackLocale`, the map's **keys** are used and the `default` key is skipped since it is not a locale name.
377
377
  *
378
378
  * @param ctx - A {@link CoreContext | context}
379
379
  * @param fallback - A {@link FallbackLocale | fallback locale}