@intlify/core-base 11.2.8 → 11.3.0

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,6 +1,6 @@
1
1
  /*!
2
- * core-base v11.2.8
3
- * (c) 2025 kazuya kawaguchi
2
+ * core-base v11.3.0
3
+ * (c) 2026 kazuya kawaguchi
4
4
  * Released under the MIT License.
5
5
  */
6
6
  'use strict';
@@ -682,6 +682,7 @@ function resolveValue(obj, path) {
682
682
  }
683
683
  // resolve path value
684
684
  const len = hit.length;
685
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- for generic object
685
686
  let last = obj;
686
687
  let i = 0;
687
688
  while (i < len) {
@@ -694,6 +695,12 @@ function resolveValue(obj, path) {
694
695
  if (AST_NODE_PROPS_KEYS.includes(key) && isMessageAST(last)) {
695
696
  return null;
696
697
  }
698
+ if (!shared.isObject(last)) {
699
+ return null;
700
+ }
701
+ if (!shared.hasOwn(last, key)) {
702
+ return null;
703
+ }
697
704
  const val = last[key];
698
705
  if (val === undefined) {
699
706
  return null;
@@ -714,9 +721,11 @@ const CoreWarnCodes = {
714
721
  FALLBACK_TO_NUMBER_FORMAT: 4,
715
722
  CANNOT_FORMAT_DATE: 5,
716
723
  FALLBACK_TO_DATE_FORMAT: 6,
717
- EXPERIMENTAL_CUSTOM_MESSAGE_COMPILER: 7
724
+ EXPERIMENTAL_CUSTOM_MESSAGE_COMPILER: 7,
725
+ INVALID_NUMBER_ARGUMENT: 8,
726
+ INVALID_DATE_ARGUMENT: 9
718
727
  };
719
- const CORE_WARN_CODES_EXTEND_POINT = 8;
728
+ const CORE_WARN_CODES_EXTEND_POINT = 10;
720
729
  /** @internal */
721
730
  const warnMessages = {
722
731
  [CoreWarnCodes.NOT_FOUND_KEY]: `Not found '{key}' key in '{locale}' locale messages.`,
@@ -725,7 +734,9 @@ const warnMessages = {
725
734
  [CoreWarnCodes.FALLBACK_TO_NUMBER_FORMAT]: `Fall back to number format '{key}' key with '{target}' locale.`,
726
735
  [CoreWarnCodes.CANNOT_FORMAT_DATE]: `Cannot format a date value due to not supported Intl.DateTimeFormat.`,
727
736
  [CoreWarnCodes.FALLBACK_TO_DATE_FORMAT]: `Fall back to datetime format '{key}' key with '{target}' locale.`,
728
- [CoreWarnCodes.EXPERIMENTAL_CUSTOM_MESSAGE_COMPILER]: `This project is using Custom Message Compiler, which is an experimental feature. It may receive breaking changes or be removed in the future.`
737
+ [CoreWarnCodes.EXPERIMENTAL_CUSTOM_MESSAGE_COMPILER]: `This project is using Custom Message Compiler, which is an experimental feature. It may receive breaking changes or be removed in the future.`,
738
+ [CoreWarnCodes.INVALID_NUMBER_ARGUMENT]: `Invalid argument for number formatting: expected a number but received '{value}'.`,
739
+ [CoreWarnCodes.INVALID_DATE_ARGUMENT]: `Invalid argument for datetime formatting: expected a Date, number, or ISO string but received '{value}'.`
729
740
  };
730
741
  function getWarnMessage(code, ...args) {
731
742
  return shared.format(warnMessages[code], ...args);
@@ -736,7 +747,7 @@ function getWarnMessage(code, ...args) {
736
747
  * Intlify core-base version
737
748
  * @internal
738
749
  */
739
- const VERSION = '11.2.8';
750
+ const VERSION = '11.3.0';
740
751
  const NOT_REOSLVED = -1;
741
752
  const DEFAULT_LOCALE = 'en-US';
742
753
  const MISSING_RESOLVE_VALUE = '';
@@ -1000,6 +1011,14 @@ function datetime(context, ...args) {
1000
1011
  onWarn(getWarnMessage(CoreWarnCodes.CANNOT_FORMAT_DATE));
1001
1012
  return MISSING_RESOLVE_VALUE;
1002
1013
  }
1014
+ if (!shared.isString(args[0]) && !shared.isDate(args[0]) && !shared.isNumber(args[0])) {
1015
+ {
1016
+ onWarn(getWarnMessage(CoreWarnCodes.INVALID_DATE_ARGUMENT, {
1017
+ value: String(args[0])
1018
+ }));
1019
+ }
1020
+ return MISSING_RESOLVE_VALUE;
1021
+ }
1003
1022
  const [key, value, options, overrides] = parseDateTimeArgs(...args);
1004
1023
  const missingWarn = shared.isBoolean(options.missingWarn)
1005
1024
  ? options.missingWarn
@@ -1012,7 +1031,7 @@ function datetime(context, ...args) {
1012
1031
  const locales = localeFallbacker(context, // eslint-disable-line @typescript-eslint/no-explicit-any
1013
1032
  fallbackLocale, locale);
1014
1033
  if (!shared.isString(key) || key === '') {
1015
- return new Intl.DateTimeFormat(locale, overrides).format(value);
1034
+ return new Intl.DateTimeFormat(locale.replace(/!/g, ''), overrides).format(value);
1016
1035
  }
1017
1036
  // resolve format
1018
1037
  let datetimeFormat = {};
@@ -1174,6 +1193,14 @@ function number(context, ...args) {
1174
1193
  onWarn(getWarnMessage(CoreWarnCodes.CANNOT_FORMAT_NUMBER));
1175
1194
  return MISSING_RESOLVE_VALUE;
1176
1195
  }
1196
+ if (!shared.isNumber(args[0])) {
1197
+ {
1198
+ onWarn(getWarnMessage(CoreWarnCodes.INVALID_NUMBER_ARGUMENT, {
1199
+ value: String(args[0])
1200
+ }));
1201
+ }
1202
+ return MISSING_RESOLVE_VALUE;
1203
+ }
1177
1204
  const [key, value, options, overrides] = parseNumberArgs(...args);
1178
1205
  const missingWarn = shared.isBoolean(options.missingWarn)
1179
1206
  ? options.missingWarn
@@ -1186,7 +1213,7 @@ function number(context, ...args) {
1186
1213
  const locales = localeFallbacker(context, // eslint-disable-line @typescript-eslint/no-explicit-any
1187
1214
  fallbackLocale, locale);
1188
1215
  if (!shared.isString(key) || key === '') {
1189
- return new Intl.NumberFormat(locale, overrides).format(value);
1216
+ return new Intl.NumberFormat(locale.replace(/!/g, ''), overrides).format(value);
1190
1217
  }
1191
1218
  // resolve format
1192
1219
  let numberFormat = {};
@@ -1315,59 +1342,43 @@ const DEFAULT_NORMALIZE = (values) => values.length === 0 ? '' : shared.join(val
1315
1342
  const DEFAULT_INTERPOLATE = shared.toDisplayString;
1316
1343
  function pluralDefault(choice, choicesLength) {
1317
1344
  choice = Math.abs(choice);
1345
+ // singular (0) | plural (1)
1318
1346
  if (choicesLength === 2) {
1319
1347
  // prettier-ignore
1320
- return choice
1321
- ? choice > 1
1322
- ? 1
1323
- : 0
1348
+ return choice === 1
1349
+ ? 0
1324
1350
  : 1;
1325
1351
  }
1326
- return choice ? Math.min(choice, 2) : 0;
1352
+ // zero (0) | singular (1) | plural (2)
1353
+ return Math.min(choice, 2);
1327
1354
  }
1328
1355
  function getPluralIndex(options) {
1329
1356
  // prettier-ignore
1330
1357
  const index = shared.isNumber(options.pluralIndex)
1331
1358
  ? options.pluralIndex
1332
1359
  : -1;
1333
- // prettier-ignore
1334
- return options.named && (shared.isNumber(options.named.count) || shared.isNumber(options.named.n))
1335
- ? shared.isNumber(options.named.count)
1336
- ? options.named.count
1337
- : shared.isNumber(options.named.n)
1338
- ? options.named.n
1339
- : index
1340
- : index;
1341
- }
1342
- function normalizeNamed(pluralIndex, props) {
1343
- if (!props.count) {
1344
- props.count = pluralIndex;
1345
- }
1346
- if (!props.n) {
1347
- props.n = pluralIndex;
1348
- }
1360
+ return shared.isNumber(options.named?.count)
1361
+ ? options.named.count
1362
+ : shared.isNumber(options.named?.n)
1363
+ ? options.named.n
1364
+ : index;
1349
1365
  }
1350
1366
  function createMessageContext(options = {}) {
1351
1367
  const locale = options.locale;
1352
1368
  const pluralIndex = getPluralIndex(options);
1353
- const pluralRule = shared.isObject(options.pluralRules) &&
1354
- shared.isString(locale) &&
1355
- shared.isFunction(options.pluralRules[locale])
1369
+ const pluralRule = shared.isString(locale) && shared.isFunction(options.pluralRules?.[locale])
1356
1370
  ? options.pluralRules[locale]
1357
1371
  : pluralDefault;
1358
- const orgPluralRule = shared.isObject(options.pluralRules) &&
1359
- shared.isString(locale) &&
1360
- shared.isFunction(options.pluralRules[locale])
1361
- ? pluralDefault
1362
- : undefined;
1363
- const plural = (messages) => {
1364
- return messages[pluralRule(pluralIndex, messages.length, orgPluralRule)];
1365
- };
1372
+ const orgPluralRule = pluralRule === pluralDefault ? undefined : pluralDefault;
1373
+ const plural = (messages) => messages[pluralRule(pluralIndex, messages.length, orgPluralRule)];
1366
1374
  const _list = options.list || [];
1367
1375
  const list = (index) => _list[index];
1368
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1369
- const _named = options.named || shared.create();
1370
- shared.isNumber(options.pluralIndex) && normalizeNamed(pluralIndex, _named);
1376
+ const _named = options.named || shared.create(); // eslint-disable-line @typescript-eslint/no-explicit-any
1377
+ // normalize named
1378
+ if (shared.isNumber(options.pluralIndex)) {
1379
+ _named.count ||= options.pluralIndex;
1380
+ _named.n ||= options.pluralIndex;
1381
+ }
1371
1382
  const named = (key) => _named[key];
1372
1383
  function message(key, useLinked) {
1373
1384
  // prettier-ignore
@@ -1385,14 +1396,13 @@ function createMessageContext(options = {}) {
1385
1396
  const _modifier = (name) => options.modifiers
1386
1397
  ? options.modifiers[name]
1387
1398
  : DEFAULT_MODIFIER;
1388
- const normalize = shared.isPlainObject(options.processor) && shared.isFunction(options.processor.normalize)
1399
+ const normalize = shared.isFunction(options.processor?.normalize)
1389
1400
  ? options.processor.normalize
1390
1401
  : DEFAULT_NORMALIZE;
1391
- const interpolate = shared.isPlainObject(options.processor) &&
1392
- shared.isFunction(options.processor.interpolate)
1402
+ const interpolate = shared.isFunction(options.processor?.interpolate)
1393
1403
  ? options.processor.interpolate
1394
1404
  : DEFAULT_INTERPOLATE;
1395
- const type = shared.isPlainObject(options.processor) && shared.isString(options.processor.type)
1405
+ const type = shared.isString(options.processor?.type)
1396
1406
  ? options.processor.type
1397
1407
  : DEFAULT_MESSAGE_DATA_TYPE;
1398
1408
  const linked = (key, ...args) => {
@@ -1417,11 +1427,13 @@ function createMessageContext(options = {}) {
1417
1427
  }
1418
1428
  }
1419
1429
  const ret = message(key, true)(ctx);
1430
+ // If the linked message is not resolved (empty string), fall back to the key itself
1431
+ const resolved = ret === '' || ret === undefined ? key : ret;
1420
1432
  const msg =
1421
1433
  // The message in vnode resolved with linked are returned as an array by processor.nomalize
1422
- type === 'vnode' && shared.isArray(ret) && modifier
1423
- ? ret[0]
1424
- : ret;
1434
+ type === 'vnode' && shared.isArray(resolved) && modifier
1435
+ ? resolved[0]
1436
+ : resolved;
1425
1437
  return modifier ? _modifier(modifier)(msg, type) : msg;
1426
1438
  };
1427
1439
  const ctx = {
@@ -1778,7 +1790,6 @@ function getCompileContext(context, locale, key, source, warnHtmlMessage, onErro
1778
1790
  onError && onError(err);
1779
1791
  {
1780
1792
  const _source = getSourceForCodeFrame(source);
1781
- const message = `Message compilation error: ${err.message}`;
1782
1793
  const codeFrame = err.location &&
1783
1794
  _source &&
1784
1795
  shared.generateCodeFrame(_source, err.location.start.offset, err.location.end.offset);
@@ -1792,7 +1803,8 @@ function getCompileContext(context, locale, key, source, warnHtmlMessage, onErro
1792
1803
  groupId: `${'translate'}:${key}`
1793
1804
  });
1794
1805
  }
1795
- console.error(codeFrame ? `${message}\n${codeFrame}` : message);
1806
+ const message = `Message compilation error: ${err.message}`;
1807
+ throw new SyntaxError(codeFrame ? `${message}\n${codeFrame}` : message);
1796
1808
  }
1797
1809
  },
1798
1810
  onCacheKey: (source) => shared.generateFormatCacheKey(locale, key, source)
@@ -27,7 +27,7 @@ export { CompileErrorCodes }
27
27
 
28
28
  export declare const CORE_ERROR_CODES_EXTEND_POINT = 24;
29
29
 
30
- export declare const CORE_WARN_CODES_EXTEND_POINT = 8;
30
+ export declare const CORE_WARN_CODES_EXTEND_POINT = 10;
31
31
 
32
32
  export declare interface CoreCommonContext<Message = string, Locales = 'en-US'> {
33
33
  cid: number;
@@ -170,6 +170,8 @@ export declare const CoreWarnCodes: {
170
170
  readonly CANNOT_FORMAT_DATE: 5;
171
171
  readonly FALLBACK_TO_DATE_FORMAT: 6;
172
172
  readonly EXPERIMENTAL_CUSTOM_MESSAGE_COMPILER: 7;
173
+ readonly INVALID_NUMBER_ARGUMENT: 8;
174
+ readonly INVALID_DATE_ARGUMENT: 9;
173
175
  };
174
176
 
175
177
  export declare type CoreWarnCodes = (typeof CoreWarnCodes)[keyof typeof CoreWarnCodes];
@@ -1,6 +1,6 @@
1
1
  /*!
2
- * core-base v11.2.8
3
- * (c) 2025 kazuya kawaguchi
2
+ * core-base v11.3.0
3
+ * (c) 2026 kazuya kawaguchi
4
4
  * Released under the MIT License.
5
5
  */
6
6
  function warn(msg, err) {
@@ -622,7 +622,24 @@ function createTokenizer(source, options = {}) {
622
622
  let buf = '';
623
623
  while (true) {
624
624
  const ch = scnr.currentChar();
625
- if (ch === "{" /* TokenChars.BraceLeft */ ||
625
+ if (ch === '\\') {
626
+ const nextCh = scnr.peek();
627
+ if (nextCh === "{" /* TokenChars.BraceLeft */ ||
628
+ nextCh === "}" /* TokenChars.BraceRight */ ||
629
+ nextCh === "@" /* TokenChars.LinkedAlias */ ||
630
+ nextCh === "|" /* TokenChars.Pipe */ ||
631
+ nextCh === '\\') {
632
+ buf += ch + nextCh;
633
+ scnr.next();
634
+ scnr.next();
635
+ }
636
+ else {
637
+ scnr.resetPeek();
638
+ buf += ch;
639
+ scnr.next();
640
+ }
641
+ }
642
+ else if (ch === "{" /* TokenChars.BraceLeft */ ||
626
643
  ch === "}" /* TokenChars.BraceRight */ ||
627
644
  ch === "@" /* TokenChars.LinkedAlias */ ||
628
645
  ch === "|" /* TokenChars.Pipe */ ||
@@ -1008,6 +1025,11 @@ function createTokenizer(source, options = {}) {
1008
1025
  const ERROR_DOMAIN$2 = 'parser';
1009
1026
  // Backslash backslash, backslash quote, uHHHH, UHHHHHH.
1010
1027
  const KNOWN_ESCAPES = /(?:\\\\|\\'|\\u([0-9a-fA-F]{4})|\\U([0-9a-fA-F]{6}))/g;
1028
+ // Text context escape sequences: \\, \@, \{, \}, \|
1029
+ const TEXT_ESCAPES = /\\([\\@{}|])/g;
1030
+ function fromTextEscapeSequence(_match, char) {
1031
+ return char;
1032
+ }
1011
1033
  function fromEscapeSequence(match, codePoint4, codePoint6) {
1012
1034
  switch (match) {
1013
1035
  case `\\\\`:
@@ -1063,7 +1085,7 @@ function createParser(options = {}) {
1063
1085
  function parseText(tokenizer, value) {
1064
1086
  const context = tokenizer.context();
1065
1087
  const node = startNode(3 /* NodeTypes.Text */, context.offset, context.startLoc);
1066
- node.value = value;
1088
+ node.value = value.replace(TEXT_ESCAPES, fromTextEscapeSequence);
1067
1089
  endNode(node, tokenizer.currentOffset(), tokenizer.currentPosition());
1068
1090
  return node;
1069
1091
  }
@@ -2374,6 +2396,7 @@ function resolveValue(obj, path) {
2374
2396
  }
2375
2397
  // resolve path value
2376
2398
  const len = hit.length;
2399
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- for generic object
2377
2400
  let last = obj;
2378
2401
  let i = 0;
2379
2402
  while (i < len) {
@@ -2386,6 +2409,12 @@ function resolveValue(obj, path) {
2386
2409
  if (AST_NODE_PROPS_KEYS.includes(key) && isMessageAST(last)) {
2387
2410
  return null;
2388
2411
  }
2412
+ if (!isObject(last)) {
2413
+ return null;
2414
+ }
2415
+ if (!hasOwn(last, key)) {
2416
+ return null;
2417
+ }
2389
2418
  const val = last[key];
2390
2419
  if (val === undefined) {
2391
2420
  return null;
@@ -2406,9 +2435,11 @@ const CoreWarnCodes = {
2406
2435
  FALLBACK_TO_NUMBER_FORMAT: 4,
2407
2436
  CANNOT_FORMAT_DATE: 5,
2408
2437
  FALLBACK_TO_DATE_FORMAT: 6,
2409
- EXPERIMENTAL_CUSTOM_MESSAGE_COMPILER: 7
2438
+ EXPERIMENTAL_CUSTOM_MESSAGE_COMPILER: 7,
2439
+ INVALID_NUMBER_ARGUMENT: 8,
2440
+ INVALID_DATE_ARGUMENT: 9
2410
2441
  };
2411
- const CORE_WARN_CODES_EXTEND_POINT = 8;
2442
+ const CORE_WARN_CODES_EXTEND_POINT = 10;
2412
2443
  /** @internal */
2413
2444
  const warnMessages = {
2414
2445
  [CoreWarnCodes.NOT_FOUND_KEY]: `Not found '{key}' key in '{locale}' locale messages.`,
@@ -2417,7 +2448,9 @@ const warnMessages = {
2417
2448
  [CoreWarnCodes.FALLBACK_TO_NUMBER_FORMAT]: `Fall back to number format '{key}' key with '{target}' locale.`,
2418
2449
  [CoreWarnCodes.CANNOT_FORMAT_DATE]: `Cannot format a date value due to not supported Intl.DateTimeFormat.`,
2419
2450
  [CoreWarnCodes.FALLBACK_TO_DATE_FORMAT]: `Fall back to datetime format '{key}' key with '{target}' locale.`,
2420
- [CoreWarnCodes.EXPERIMENTAL_CUSTOM_MESSAGE_COMPILER]: `This project is using Custom Message Compiler, which is an experimental feature. It may receive breaking changes or be removed in the future.`
2451
+ [CoreWarnCodes.EXPERIMENTAL_CUSTOM_MESSAGE_COMPILER]: `This project is using Custom Message Compiler, which is an experimental feature. It may receive breaking changes or be removed in the future.`,
2452
+ [CoreWarnCodes.INVALID_NUMBER_ARGUMENT]: `Invalid argument for number formatting: expected a number but received '{value}'.`,
2453
+ [CoreWarnCodes.INVALID_DATE_ARGUMENT]: `Invalid argument for datetime formatting: expected a Date, number, or ISO string but received '{value}'.`
2421
2454
  };
2422
2455
  function getWarnMessage(code, ...args) {
2423
2456
  return format$1(warnMessages[code], ...args);
@@ -2428,7 +2461,7 @@ function getWarnMessage(code, ...args) {
2428
2461
  * Intlify core-base version
2429
2462
  * @internal
2430
2463
  */
2431
- const VERSION = '11.2.8';
2464
+ const VERSION = '11.3.0';
2432
2465
  const NOT_REOSLVED = -1;
2433
2466
  const DEFAULT_LOCALE = 'en-US';
2434
2467
  const MISSING_RESOLVE_VALUE = '';
@@ -2692,6 +2725,14 @@ function datetime(context, ...args) {
2692
2725
  onWarn(getWarnMessage(CoreWarnCodes.CANNOT_FORMAT_DATE));
2693
2726
  return MISSING_RESOLVE_VALUE;
2694
2727
  }
2728
+ if (!isString(args[0]) && !isDate(args[0]) && !isNumber(args[0])) {
2729
+ {
2730
+ onWarn(getWarnMessage(CoreWarnCodes.INVALID_DATE_ARGUMENT, {
2731
+ value: String(args[0])
2732
+ }));
2733
+ }
2734
+ return MISSING_RESOLVE_VALUE;
2735
+ }
2695
2736
  const [key, value, options, overrides] = parseDateTimeArgs(...args);
2696
2737
  const missingWarn = isBoolean(options.missingWarn)
2697
2738
  ? options.missingWarn
@@ -2704,7 +2745,7 @@ function datetime(context, ...args) {
2704
2745
  const locales = localeFallbacker(context, // eslint-disable-line @typescript-eslint/no-explicit-any
2705
2746
  fallbackLocale, locale);
2706
2747
  if (!isString(key) || key === '') {
2707
- return new Intl.DateTimeFormat(locale, overrides).format(value);
2748
+ return new Intl.DateTimeFormat(locale.replace(/!/g, ''), overrides).format(value);
2708
2749
  }
2709
2750
  // resolve format
2710
2751
  let datetimeFormat = {};
@@ -2866,6 +2907,14 @@ function number(context, ...args) {
2866
2907
  onWarn(getWarnMessage(CoreWarnCodes.CANNOT_FORMAT_NUMBER));
2867
2908
  return MISSING_RESOLVE_VALUE;
2868
2909
  }
2910
+ if (!isNumber(args[0])) {
2911
+ {
2912
+ onWarn(getWarnMessage(CoreWarnCodes.INVALID_NUMBER_ARGUMENT, {
2913
+ value: String(args[0])
2914
+ }));
2915
+ }
2916
+ return MISSING_RESOLVE_VALUE;
2917
+ }
2869
2918
  const [key, value, options, overrides] = parseNumberArgs(...args);
2870
2919
  const missingWarn = isBoolean(options.missingWarn)
2871
2920
  ? options.missingWarn
@@ -2878,7 +2927,7 @@ function number(context, ...args) {
2878
2927
  const locales = localeFallbacker(context, // eslint-disable-line @typescript-eslint/no-explicit-any
2879
2928
  fallbackLocale, locale);
2880
2929
  if (!isString(key) || key === '') {
2881
- return new Intl.NumberFormat(locale, overrides).format(value);
2930
+ return new Intl.NumberFormat(locale.replace(/!/g, ''), overrides).format(value);
2882
2931
  }
2883
2932
  // resolve format
2884
2933
  let numberFormat = {};
@@ -3007,59 +3056,43 @@ const DEFAULT_NORMALIZE = (values) => values.length === 0 ? '' : join(values);
3007
3056
  const DEFAULT_INTERPOLATE = toDisplayString;
3008
3057
  function pluralDefault(choice, choicesLength) {
3009
3058
  choice = Math.abs(choice);
3059
+ // singular (0) | plural (1)
3010
3060
  if (choicesLength === 2) {
3011
3061
  // prettier-ignore
3012
- return choice
3013
- ? choice > 1
3014
- ? 1
3015
- : 0
3062
+ return choice === 1
3063
+ ? 0
3016
3064
  : 1;
3017
3065
  }
3018
- return choice ? Math.min(choice, 2) : 0;
3066
+ // zero (0) | singular (1) | plural (2)
3067
+ return Math.min(choice, 2);
3019
3068
  }
3020
3069
  function getPluralIndex(options) {
3021
3070
  // prettier-ignore
3022
3071
  const index = isNumber(options.pluralIndex)
3023
3072
  ? options.pluralIndex
3024
3073
  : -1;
3025
- // prettier-ignore
3026
- return options.named && (isNumber(options.named.count) || isNumber(options.named.n))
3027
- ? isNumber(options.named.count)
3028
- ? options.named.count
3029
- : isNumber(options.named.n)
3030
- ? options.named.n
3031
- : index
3032
- : index;
3033
- }
3034
- function normalizeNamed(pluralIndex, props) {
3035
- if (!props.count) {
3036
- props.count = pluralIndex;
3037
- }
3038
- if (!props.n) {
3039
- props.n = pluralIndex;
3040
- }
3074
+ return isNumber(options.named?.count)
3075
+ ? options.named.count
3076
+ : isNumber(options.named?.n)
3077
+ ? options.named.n
3078
+ : index;
3041
3079
  }
3042
3080
  function createMessageContext(options = {}) {
3043
3081
  const locale = options.locale;
3044
3082
  const pluralIndex = getPluralIndex(options);
3045
- const pluralRule = isObject(options.pluralRules) &&
3046
- isString(locale) &&
3047
- isFunction(options.pluralRules[locale])
3083
+ const pluralRule = isString(locale) && isFunction(options.pluralRules?.[locale])
3048
3084
  ? options.pluralRules[locale]
3049
3085
  : pluralDefault;
3050
- const orgPluralRule = isObject(options.pluralRules) &&
3051
- isString(locale) &&
3052
- isFunction(options.pluralRules[locale])
3053
- ? pluralDefault
3054
- : undefined;
3055
- const plural = (messages) => {
3056
- return messages[pluralRule(pluralIndex, messages.length, orgPluralRule)];
3057
- };
3086
+ const orgPluralRule = pluralRule === pluralDefault ? undefined : pluralDefault;
3087
+ const plural = (messages) => messages[pluralRule(pluralIndex, messages.length, orgPluralRule)];
3058
3088
  const _list = options.list || [];
3059
3089
  const list = (index) => _list[index];
3060
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
3061
- const _named = options.named || create();
3062
- isNumber(options.pluralIndex) && normalizeNamed(pluralIndex, _named);
3090
+ const _named = options.named || create(); // eslint-disable-line @typescript-eslint/no-explicit-any
3091
+ // normalize named
3092
+ if (isNumber(options.pluralIndex)) {
3093
+ _named.count ||= options.pluralIndex;
3094
+ _named.n ||= options.pluralIndex;
3095
+ }
3063
3096
  const named = (key) => _named[key];
3064
3097
  function message(key, useLinked) {
3065
3098
  // prettier-ignore
@@ -3077,14 +3110,13 @@ function createMessageContext(options = {}) {
3077
3110
  const _modifier = (name) => options.modifiers
3078
3111
  ? options.modifiers[name]
3079
3112
  : DEFAULT_MODIFIER;
3080
- const normalize = isPlainObject(options.processor) && isFunction(options.processor.normalize)
3113
+ const normalize = isFunction(options.processor?.normalize)
3081
3114
  ? options.processor.normalize
3082
3115
  : DEFAULT_NORMALIZE;
3083
- const interpolate = isPlainObject(options.processor) &&
3084
- isFunction(options.processor.interpolate)
3116
+ const interpolate = isFunction(options.processor?.interpolate)
3085
3117
  ? options.processor.interpolate
3086
3118
  : DEFAULT_INTERPOLATE;
3087
- const type = isPlainObject(options.processor) && isString(options.processor.type)
3119
+ const type = isString(options.processor?.type)
3088
3120
  ? options.processor.type
3089
3121
  : DEFAULT_MESSAGE_DATA_TYPE;
3090
3122
  const linked = (key, ...args) => {
@@ -3109,11 +3141,13 @@ function createMessageContext(options = {}) {
3109
3141
  }
3110
3142
  }
3111
3143
  const ret = message(key, true)(ctx);
3144
+ // If the linked message is not resolved (empty string), fall back to the key itself
3145
+ const resolved = ret === '' || ret === undefined ? key : ret;
3112
3146
  const msg =
3113
3147
  // The message in vnode resolved with linked are returned as an array by processor.nomalize
3114
- type === 'vnode' && isArray(ret) && modifier
3115
- ? ret[0]
3116
- : ret;
3148
+ type === 'vnode' && isArray(resolved) && modifier
3149
+ ? resolved[0]
3150
+ : resolved;
3117
3151
  return modifier ? _modifier(modifier)(msg, type) : msg;
3118
3152
  };
3119
3153
  const ctx = {
@@ -3470,7 +3504,6 @@ function getCompileContext(context, locale, key, source, warnHtmlMessage, onErro
3470
3504
  onError && onError(err);
3471
3505
  {
3472
3506
  const _source = getSourceForCodeFrame(source);
3473
- const message = `Message compilation error: ${err.message}`;
3474
3507
  const codeFrame = err.location &&
3475
3508
  _source &&
3476
3509
  generateCodeFrame(_source, err.location.start.offset, err.location.end.offset);
@@ -3484,7 +3517,8 @@ function getCompileContext(context, locale, key, source, warnHtmlMessage, onErro
3484
3517
  groupId: `${'translate'}:${key}`
3485
3518
  });
3486
3519
  }
3487
- console.error(codeFrame ? `${message}\n${codeFrame}` : message);
3520
+ const message = `Message compilation error: ${err.message}`;
3521
+ throw new SyntaxError(codeFrame ? `${message}\n${codeFrame}` : message);
3488
3522
  }
3489
3523
  },
3490
3524
  onCacheKey: (source) => generateFormatCacheKey(locale, key, source)