@x18n/core 0.1.3 → 0.1.4

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/index.d.mts CHANGED
@@ -284,9 +284,13 @@ interface LocaleInfo {
284
284
  readonly region: string | undefined;
285
285
  readonly displayName: string | undefined;
286
286
  readonly flag: string | undefined;
287
+ /** The plural categories a message may have. */
287
288
  readonly pluralCategories: readonly Intl.LDMLPluralRule[];
288
289
  /** Categories of ordinal numbers (1st, 2nd), which differ from the cardinal ones. */
289
290
  readonly ordinalCategories: readonly Intl.LDMLPluralRule[];
291
+ /** The plural categories a message must have: those whole numbers select. French may leave out `many`, from 1,000,000 only. */
292
+ readonly requiredPluralCategories: readonly Intl.LDMLPluralRule[];
293
+ readonly requiredOrdinalCategories: readonly Intl.LDMLPluralRule[];
290
294
  }
291
295
  /** Embedded resource inside a file, for example a Vue `<i18n>` block. */
292
296
  interface ResourceBlock {
@@ -330,6 +334,10 @@ interface Usage {
330
334
  readonly range: TextRange;
331
335
  /** Range of the whole call that holds the key (`t('a', { n })`), when the scanner knows it. */
332
336
  readonly expression: TextRange | undefined;
337
+ /** Names of the params the call passes (`t('a', { n })` gives `['n']`); undefined when the scanner cannot tell, as with a spread or a variable. */
338
+ readonly params?: readonly string[] | undefined;
339
+ /** The default value written in code (`t('a', 'Default')`), when the scanner knows it. */
340
+ readonly defaultValue?: string | undefined;
333
341
  readonly scanner: string;
334
342
  readonly kind: UsageKind;
335
343
  /** `t(`pages.${id}.title`)` gives 'prefix' with key `pages`. */
@@ -623,6 +631,10 @@ interface UsageCandidate {
623
631
  readonly range: TextRange;
624
632
  /** Range of the whole call that holds the key, when the scanner knows it. */
625
633
  readonly expression?: TextRange | undefined;
634
+ /** Names of the params the call passes, when they are all static. */
635
+ readonly params?: readonly string[] | undefined;
636
+ /** The default value the call writes in code. */
637
+ readonly defaultValue?: string | undefined;
626
638
  readonly kind: UsageKind;
627
639
  readonly dynamic: false | "prefix" | "unknown";
628
640
  readonly namespace?: string | undefined;
@@ -782,10 +794,19 @@ interface ReportInput {
782
794
  readonly coverage: readonly LocaleCoverage[];
783
795
  readonly summary: ReportSummary;
784
796
  }
797
+ /** What a check baseline left out (`x18n check --baseline`). */
798
+ interface BaselineSummary {
799
+ /** Known diagnostics that the baseline left out of the report. */
800
+ readonly suppressed: number;
801
+ /** Baseline entries that no longer occur, to drop with `--update-baseline`. */
802
+ readonly fixed: number;
803
+ }
785
804
  interface ReporterContext {
786
805
  /** Turns a project path into the path to print (relative to the cwd). */
787
806
  readonly displayPath: (path: string, project: string) => string;
788
807
  readonly color: boolean;
808
+ /** Set when a baseline applies; the inputs then hold only new diagnostics. */
809
+ readonly baseline?: BaselineSummary | undefined;
789
810
  }
790
811
  interface Reporter {
791
812
  readonly id: string;
@@ -907,6 +928,13 @@ interface ArgSource {
907
928
  readonly arg: number;
908
929
  readonly property?: string | undefined;
909
930
  }
931
+ /** Where the params of a call are: the static keys of an object literal argument. */
932
+ interface ParamsSource {
933
+ /** Arguments that may hold the params object, in order; string arguments are skipped (`t(key, 'Default', { n })`). */
934
+ readonly arg: number | readonly number[];
935
+ /** Option names the library reads itself, which a message need not use (`ns`, `defaultValue`). */
936
+ readonly options?: readonly string[] | undefined;
937
+ }
910
938
  /** Data that drives the AST scanners: which calls are translate calls and where the key is. */
911
939
  interface CallSpec {
912
940
  /** Function or member path: 't', '$t', 'i18n.t', 'intl.formatMessage'. */
@@ -919,6 +947,10 @@ interface CallSpec {
919
947
  readonly namespace?: ArgSource | string | undefined;
920
948
  /** Where a key prefix comes from (`keyPrefix` option, or next-intl `useTranslations('a.b')`). */
921
949
  readonly keyPrefix?: ArgSource | undefined;
950
+ /** Where the params passed to the message are. For factories, those of the returned functions. */
951
+ readonly params?: ParamsSource | undefined;
952
+ /** Where a default value written in code comes from; the first string found wins. For factories, that of the returned functions. */
953
+ readonly defaultValue?: readonly ArgSource[] | undefined;
922
954
  /** The callee is a factory that returns the translate function, for example `useI18n()` returning `{ t }`. */
923
955
  readonly factory?: boolean | undefined;
924
956
  /** For factories: the names of the returned translate functions. Default `['t']`. */
@@ -1748,4 +1780,4 @@ export declare function rangeContains(range: TextRange, offset: number): boolean
1748
1780
  /** Fast non-cryptographic string hash (cyrb53), as hex. */
1749
1781
  export declare function hashText(text: string): string;
1750
1782
  //#endregion
1751
- export type { Analysis, Answers, ApplyResult, ArgSource, Builtins, CacheStore, CallSpec, CapabilityConflict, Check, CheckContext, CheckIds, CheckLevel, CheckSetting, ConfigContext, ConfigDraft, ConfigOrigin, CreateProjectOptions, Decision, DecisionOption, DetectContext, Diagnostic, DiagnosticInput, Disposable, EditOp, Envelope, ErrorCode, ErrorInfo, ExtractItem, ExtractOptions, ExtractTemplate, Extractor, FileChange, FileEvent, FileScan, FinderContext, Fix, Format, FormatContext, FormatEntry, GlobOptions, GlossaryEntry, HardString, HistoryConfigs, HistoryContext, HistoryOptions, HistoryProvider, HistorySettings, Host, JsonObject, JsonPrimitive, JsonValue, KeyContext, KeyId, KeyInfo, KeyQuery, KeyString, KeyTarget, LazyFormat, LazyScanner, LibraryOptions, LineIndex, LinePosition, LocaleCode, LocaleCoverage, LocaleFinder, LocaleInfo, Location, Logger, ManifestReader, MemoryHost, Message, MessageChange, MessageHistory, MessageLeaf, MessageOp, MessageTree, NamespaceFromPath, NamespaceOptions, NodeRange, OverlayHost, ParseResult, Placeholder, PlaceholderComparer, PlaceholderProblem, PlaceholderSyntax, Plan, Plugin, PluginConfigs, PluginFile, PluginLoader, PluginOption, PluginRegistry, PluginSpecifier, PluralForm, Preset, PresetDetection, PresetEntry, PresetExplanation, PresetFactory, PresetLoader, PresetOption, Project, ProjectChange, ProjectExplanation, PromptContext, PromptData, PromptDraft, PromptItem, PromptOptions, RegexScannerSpec, ReportInput, ReportSummary, Reporter, ReporterContext, ReporterLoader, ResolveConfigOptions, ResolvedConfig, Resource, ResourceBlock, ResourceCandidate, ResourceId, ResourceNode, ResourceSpec, ResourceTransform, ResourceTransformContext, ReviewAuthor, ReviewComment, ReviewData, ReviewFile, ReviewFilter, ReviewOptions, ReviewState, ReviewStore, ReviewThread, ReviewType, ScanContext, ScanResult, Scanner, ScannerEntry, SchemaIssue, Scope, SearchMatch, SearchOptions, SerializableConfig, Setup, Severity, SourceFile, TextEdit, TextRange, ThreadComment, TreeLeaf, Usage, UsageCandidate, UsageContext, UsageKind, UsageMatch, UsageOptions, UsageQuery, UserConfig, WireDiagnostic, WireRange, WriteOptions, X18nError };
1783
+ export type { Analysis, Answers, ApplyResult, ArgSource, BaselineSummary, Builtins, CacheStore, CallSpec, CapabilityConflict, Check, CheckContext, CheckIds, CheckLevel, CheckSetting, ConfigContext, ConfigDraft, ConfigOrigin, CreateProjectOptions, Decision, DecisionOption, DetectContext, Diagnostic, DiagnosticInput, Disposable, EditOp, Envelope, ErrorCode, ErrorInfo, ExtractItem, ExtractOptions, ExtractTemplate, Extractor, FileChange, FileEvent, FileScan, FinderContext, Fix, Format, FormatContext, FormatEntry, GlobOptions, GlossaryEntry, HardString, HistoryConfigs, HistoryContext, HistoryOptions, HistoryProvider, HistorySettings, Host, JsonObject, JsonPrimitive, JsonValue, KeyContext, KeyId, KeyInfo, KeyQuery, KeyString, KeyTarget, LazyFormat, LazyScanner, LibraryOptions, LineIndex, LinePosition, LocaleCode, LocaleCoverage, LocaleFinder, LocaleInfo, Location, Logger, ManifestReader, MemoryHost, Message, MessageChange, MessageHistory, MessageLeaf, MessageOp, MessageTree, NamespaceFromPath, NamespaceOptions, NodeRange, OverlayHost, ParamsSource, ParseResult, Placeholder, PlaceholderComparer, PlaceholderProblem, PlaceholderSyntax, Plan, Plugin, PluginConfigs, PluginFile, PluginLoader, PluginOption, PluginRegistry, PluginSpecifier, PluralForm, Preset, PresetDetection, PresetEntry, PresetExplanation, PresetFactory, PresetLoader, PresetOption, Project, ProjectChange, ProjectExplanation, PromptContext, PromptData, PromptDraft, PromptItem, PromptOptions, RegexScannerSpec, ReportInput, ReportSummary, Reporter, ReporterContext, ReporterLoader, ResolveConfigOptions, ResolvedConfig, Resource, ResourceBlock, ResourceCandidate, ResourceId, ResourceNode, ResourceSpec, ResourceTransform, ResourceTransformContext, ReviewAuthor, ReviewComment, ReviewData, ReviewFile, ReviewFilter, ReviewOptions, ReviewState, ReviewStore, ReviewThread, ReviewType, ScanContext, ScanResult, Scanner, ScannerEntry, SchemaIssue, Scope, SearchMatch, SearchOptions, SerializableConfig, Setup, Severity, SourceFile, TextEdit, TextRange, ThreadComment, TreeLeaf, Usage, UsageCandidate, UsageContext, UsageKind, UsageMatch, UsageOptions, UsageQuery, UserConfig, WireDiagnostic, WireRange, WriteOptions, X18nError };
package/dist/index.mjs CHANGED
@@ -492,6 +492,14 @@ function parseLocaleInfo(code) {
492
492
  const found = language ? safe(() => new Intl.PluralRules(tag, { type }).resolvedOptions().pluralCategories) : void 0;
493
493
  return found ? pluralOrder.filter((category) => found.includes(category)) : ["other"];
494
494
  };
495
+ const required = (type) => {
496
+ const found = /* @__PURE__ */ new Set(["other"]);
497
+ if (language && safe(() => Intl.PluralRules.supportedLocalesOf([tag]).length > 0)) {
498
+ const rules = new Intl.PluralRules(tag, { type });
499
+ for (let n = 0; n < 2e3; n++) found.add(rules.select(n));
500
+ }
501
+ return pluralOrder.filter((category) => found.has(category));
502
+ };
495
503
  const flagRegion = region ?? (language === "zh" && script === "Hant" ? "TW" : language ? defaultRegions[language] : void 0);
496
504
  return {
497
505
  code,
@@ -500,7 +508,9 @@ function parseLocaleInfo(code) {
500
508
  displayName,
501
509
  flag: flagRegion ? flagOf(flagRegion) : void 0,
502
510
  pluralCategories: categories("cardinal"),
503
- ordinalCategories: categories("ordinal")
511
+ ordinalCategories: categories("ordinal"),
512
+ requiredPluralCategories: required("cardinal"),
513
+ requiredOrdinalCategories: required("ordinal")
504
514
  };
505
515
  }
506
516
  /** Codes that differ only in spelling (`zh_CN` and `zh-cn`), the same after a lenient normalization. */
@@ -776,6 +786,10 @@ const argSource = v.strictObject({
776
786
  arg: v.number(),
777
787
  property: v.optional(v.string())
778
788
  });
789
+ const paramsSource = v.strictObject({
790
+ arg: v.union([v.number(), v.array(v.number())]),
791
+ options: v.optional(strings)
792
+ });
779
793
  const callSpec = v.strictObject({
780
794
  callee: v.union([v.string(), strings]),
781
795
  kind: v.optional(v.picklist([
@@ -786,6 +800,8 @@ const callSpec = v.strictObject({
786
800
  key: v.optional(v.union([v.number(), argSource])),
787
801
  namespace: v.optional(v.union([argSource, v.string()])),
788
802
  keyPrefix: v.optional(argSource),
803
+ params: v.optional(paramsSource),
804
+ defaultValue: v.optional(v.array(argSource)),
789
805
  factory: v.optional(v.boolean()),
790
806
  returns: v.optional(strings),
791
807
  types: v.optional(strings)
@@ -1167,6 +1183,8 @@ function createUsageResolver(config, namespaced, normalizers) {
1167
1183
  path,
1168
1184
  range: candidate.range,
1169
1185
  expression: candidate.expression,
1186
+ ...candidate.params ? { params: candidate.params } : {},
1187
+ ...candidate.defaultValue === void 0 ? {} : { defaultValue: candidate.defaultValue },
1170
1188
  scanner,
1171
1189
  kind: candidate.kind,
1172
1190
  dynamic: candidate.dynamic,
@@ -1202,8 +1220,12 @@ function suppressedRanges(text) {
1202
1220
  }
1203
1221
  return ranges;
1204
1222
  }
1205
- /** The plural categories of a locale's language: Japanese has only `other`. */
1206
- function categoriesOf(info, type) {
1223
+ /** The plural categories of a locale's language: Japanese has only `other`. `required` keeps those whole numbers select. */
1224
+ function categoriesOf(info, type, required = false) {
1225
+ if (required) {
1226
+ const allowed = categoriesOf(info, type);
1227
+ return (type === "ordinal" ? info.requiredOrdinalCategories : info.requiredPluralCategories).filter((category) => category === "other" || allowed.includes(category));
1228
+ }
1207
1229
  return type === "ordinal" ? info.ordinalCategories : info.pluralCategories;
1208
1230
  }
1209
1231
  const tokens = /(\{key\}|\{plural\}|\{ordinal\}|\*)/;
@@ -1282,7 +1304,7 @@ function levelOf(setting) {
1282
1304
  }
1283
1305
  /**
1284
1306
  * The keys a locale needs: the reference keys, with plural forms swapped for the categories of the locale's
1285
- * language. Forms the language lacks (`item_zero` in English) count only where they exist.
1307
+ * language. Forms the language lacks or may leave out (`item_zero` in English, `item_many` in French) count only where they exist.
1286
1308
  */
1287
1309
  function expectedKeys(state, options, reference, locale) {
1288
1310
  const info = options.localeInfo(locale);
@@ -1290,7 +1312,7 @@ function expectedKeys(state, options, reference, locale) {
1290
1312
  for (const key of reference) {
1291
1313
  const form = options.formOf(key);
1292
1314
  if (!form || state.messages.get(key)?.has(locale)) keys.add(key);
1293
- form && categoriesOf(info, form.type).forEach((category) => keys.add(form.keyOf(category)));
1315
+ form && categoriesOf(info, form.type, true).forEach((category) => keys.add(form.keyOf(category)));
1294
1316
  }
1295
1317
  return [...keys].sort();
1296
1318
  }
@@ -2605,10 +2627,10 @@ function createProject(options) {
2605
2627
  }
2606
2628
  return info;
2607
2629
  }
2608
- /** Whether `locale` needs `key`: not a plural form of a category its language lacks. */
2630
+ /** Whether `locale` needs `key`: not a plural form of a category its language lacks or may leave out. */
2609
2631
  function needs(key, locale) {
2610
2632
  const form = formOf(key);
2611
- return !form || categoriesOf(localeInfo(locale), form.type).includes(form.category);
2633
+ return !form || categoriesOf(localeInfo(locale), form.type, true).includes(form.category);
2612
2634
  }
2613
2635
  const reviewStore = config.review.enabled ? registry.reviewStores.at(-1) : void 0;
2614
2636
  async function loadReviews() {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@x18n/core",
3
3
  "type": "module",
4
- "version": "0.1.3",
4
+ "version": "0.1.4",
5
5
  "description": "Runtime-neutral engine of x18n: domain model, plugin API and project analysis",
6
6
  "author": "Anthony Fu <anthonyfu117@hotmail.com>",
7
7
  "license": "MIT",
@@ -272,6 +272,52 @@
272
272
  ],
273
273
  "additionalProperties": false
274
274
  },
275
+ "params": {
276
+ "type": "object",
277
+ "properties": {
278
+ "arg": {
279
+ "anyOf": [
280
+ {
281
+ "type": "number"
282
+ },
283
+ {
284
+ "type": "array",
285
+ "items": {
286
+ "type": "number"
287
+ }
288
+ }
289
+ ]
290
+ },
291
+ "options": {
292
+ "type": "array",
293
+ "items": {
294
+ "type": "string"
295
+ }
296
+ }
297
+ },
298
+ "required": [
299
+ "arg"
300
+ ],
301
+ "additionalProperties": false
302
+ },
303
+ "defaultValue": {
304
+ "type": "array",
305
+ "items": {
306
+ "type": "object",
307
+ "properties": {
308
+ "arg": {
309
+ "type": "number"
310
+ },
311
+ "property": {
312
+ "type": "string"
313
+ }
314
+ },
315
+ "required": [
316
+ "arg"
317
+ ],
318
+ "additionalProperties": false
319
+ }
320
+ },
275
321
  "factory": {
276
322
  "type": "boolean"
277
323
  },
@@ -217,6 +217,52 @@
217
217
  ],
218
218
  "additionalProperties": false
219
219
  },
220
+ "params": {
221
+ "type": "object",
222
+ "properties": {
223
+ "arg": {
224
+ "anyOf": [
225
+ {
226
+ "type": "number"
227
+ },
228
+ {
229
+ "type": "array",
230
+ "items": {
231
+ "type": "number"
232
+ }
233
+ }
234
+ ]
235
+ },
236
+ "options": {
237
+ "type": "array",
238
+ "items": {
239
+ "type": "string"
240
+ }
241
+ }
242
+ },
243
+ "required": [
244
+ "arg"
245
+ ],
246
+ "additionalProperties": false
247
+ },
248
+ "defaultValue": {
249
+ "type": "array",
250
+ "items": {
251
+ "type": "object",
252
+ "properties": {
253
+ "arg": {
254
+ "type": "number"
255
+ },
256
+ "property": {
257
+ "type": "string"
258
+ }
259
+ },
260
+ "required": [
261
+ "arg"
262
+ ],
263
+ "additionalProperties": false
264
+ }
265
+ },
220
266
  "factory": {
221
267
  "type": "boolean"
222
268
  },