@menapo/vi18n 0.0.0-stage → 0.1.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.
Files changed (100) hide show
  1. package/LICENSE +49 -0
  2. package/README.md +110 -2
  3. package/dist/apply-Bre-lY9J.js +228 -0
  4. package/dist/captured-Bj8bO6vd.js +1034 -0
  5. package/dist/check-D34P-S--.js +1 -0
  6. package/dist/check-DyrAbv73.js +201 -0
  7. package/dist/cli.d.ts +1 -0
  8. package/dist/cli.js +41 -0
  9. package/dist/codec-DuvU2VQy.js +207 -0
  10. package/dist/collect-Cx4_SKOe.js +13 -0
  11. package/dist/collisions-CfpZU8e4.js +217 -0
  12. package/dist/compile-B5Mz6WJm.js +245 -0
  13. package/dist/compile-Cc97JaW2.js +2502 -0
  14. package/dist/concurrency-C9UUWuDC.js +96 -0
  15. package/dist/concurrency-DONL7t4V.js +1 -0
  16. package/dist/config.d.ts +21 -0
  17. package/dist/config.js +39 -0
  18. package/dist/dir-zw6bGMKQ.js +290 -0
  19. package/dist/duplicates-yuRljQ6h.js +185 -0
  20. package/dist/engine-B7p40M__.js +531 -0
  21. package/dist/engine.d.ts +43 -0
  22. package/dist/engine.js +1241 -0
  23. package/dist/exclude-Du-Zd_qI.js +26 -0
  24. package/dist/export-DGsMbcTZ.js +103 -0
  25. package/dist/extract-Dujlsgnk.js +298 -0
  26. package/dist/formats-KPIg-08V.js +781 -0
  27. package/dist/i18next-CrUnKFfh.js +1596 -0
  28. package/dist/i18next-Rgo-v9l0.js +1 -0
  29. package/dist/import-Cwdhcywt.js +225 -0
  30. package/dist/index.d.ts +156 -0
  31. package/dist/index.js +239 -0
  32. package/dist/init-BecCTvKi.js +792 -0
  33. package/dist/library-D5CWvTQs.js +1882 -0
  34. package/dist/lingui--PcOOYry.js +833 -0
  35. package/dist/lint.d.ts +105 -0
  36. package/dist/lint.js +971 -0
  37. package/dist/load-CQRz4PpM.js +2950 -0
  38. package/dist/load-gW-oJt1Q.js +747 -0
  39. package/dist/locales-DX4MOBFE.js +827 -0
  40. package/dist/lsp-Cq1Nuu-Q.js +31 -0
  41. package/dist/match-BGr8EXB3.js +89 -0
  42. package/dist/mcp-Cf0T7qPW.js +28 -0
  43. package/dist/merge-driver-CpiiSVIc.js +422 -0
  44. package/dist/messages-Cxa-5tR0.js +166 -0
  45. package/dist/migrate-CntqvrW4.js +1 -0
  46. package/dist/migrate-dPgq9uzy.js +1484 -0
  47. package/dist/openai-CTaMDcl-.js +346 -0
  48. package/dist/output-DsaHKHEt.js +164 -0
  49. package/dist/parse.d.ts +62 -0
  50. package/dist/parse.js +156 -0
  51. package/dist/pipeline-C7g11THL.js +2031 -0
  52. package/dist/pipeline-wxS0SApG.js +1 -0
  53. package/dist/project-Dwx7iPm6.js +381 -0
  54. package/dist/project-pXipT8iG.js +1046 -0
  55. package/dist/pseudo-DRv30iN5.js +145 -0
  56. package/dist/raw-C4Y6vyGY.js +4708 -0
  57. package/dist/react-intl-CaBVwJ9u.js +519 -0
  58. package/dist/restore-Btq66Tid.js +175 -0
  59. package/dist/rewrite-CiTRAICl.js +803 -0
  60. package/dist/rich-DCUKl5HM.js +97 -0
  61. package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
  62. package/dist/run-DYD_VXKy.js +694 -0
  63. package/dist/runtime-DTMmliT1.js +64 -0
  64. package/dist/scan-worker.d.ts +62 -0
  65. package/dist/scan-worker.js +1 -0
  66. package/dist/scoped-BMYpSthj.js +44 -0
  67. package/dist/server-BDkykMjb.js +11823 -0
  68. package/dist/server-qv3mz70d.js +10485 -0
  69. package/dist/server.d.ts +169 -0
  70. package/dist/server.js +1234 -0
  71. package/dist/sitemap-DV3o8QD7.js +159 -0
  72. package/dist/sources-D19czDSA.js +2242 -0
  73. package/dist/status-DZArW5aE.js +149 -0
  74. package/dist/stdio-CVaJfxA7.js +29 -0
  75. package/dist/stdio-FPOKQAgu.js +95 -0
  76. package/dist/sync-CcVzger_.js +658 -0
  77. package/dist/t-DC-c9zE5.js +454 -0
  78. package/dist/test.d.ts +116 -0
  79. package/dist/test.js +319 -0
  80. package/dist/translate-DZR3MkSA.js +299 -0
  81. package/dist/ts-BaU5yOf0.js +4863 -0
  82. package/dist/types-CgUCkFU1.d.ts +707 -0
  83. package/dist/types-D3QGM8XO.js +146 -0
  84. package/dist/types-DsdWBG0b.d.ts +558 -0
  85. package/dist/types-DtvQ7UdD.js +1948 -0
  86. package/dist/types-wSh9fCBY.js +11 -0
  87. package/dist/units-CQ5Q99RC.js +150 -0
  88. package/dist/url-CRfCOhUI.js +172 -0
  89. package/dist/url-CsBkRaJy.js +225 -0
  90. package/dist/validate-BxpupdAf.js +2615 -0
  91. package/dist/validate-D6DJepZv.js +365 -0
  92. package/dist/version-BlyLGlOa.js +28 -0
  93. package/dist/vite-scan-B_YW4X_a.js +242 -0
  94. package/dist/vite.d.ts +34 -0
  95. package/dist/vite.js +7558 -0
  96. package/dist/vue-i18n-CEMnpv7k.js +651 -0
  97. package/dist/which-C6PFI1Dm.js +116 -0
  98. package/dist/worker-84U6uYB3.js +1656 -0
  99. package/package.json +97 -3
  100. package/schemas/catalog.json +57 -0
@@ -0,0 +1,454 @@
1
+ import { _ as DEV, b as trim, g as BUILD, l as quiet, n as S, t as textDirection, x as warnOnce, y as own } from "./dir-zw6bGMKQ.js";
2
+ //#region src/runtime/bidi.ts
3
+ /**
4
+ * Automatic bidi isolation of interpolated strings (INTERNALS §2.3, PLAN
5
+ * §4.2): a user-supplied name in Arabic inside an English sentence, or a Latin
6
+ * name in Hebrew UI, must not reorder the text around it.
7
+ */
8
+ const RTL_OR_CONTROL = /[\u0590-\u08FF\u200E\u200F\u202A-\u202E\u2066-\u2069\uFB1D-\uFDFF\uFE70-\uFEFE\u{10D00}-\u{10D3F}\u{1E900}-\u{1E95F}]/u;
9
+ /**
10
+ * Wraps `value` in FSI … PDI when the locale is RTL or the value carries RTL
11
+ * text or bidi controls. Isolates inside the value are balanced first (UAX #9
12
+ * X6a): a PDI without its initiator would close our FSI early and leave the
13
+ * rest of the sentence to an override in the value, and an initiator without
14
+ * its PDI would take our PDI and the rest of the sentence with it. `open`
15
+ * counts the value's open isolates (a parameter, not a local: no block body).
16
+ */
17
+ const isolate = (value, locale, open = 0) => value && (textDirection(locale) === "rtl" || RTL_OR_CONTROL.test(value)) ? "⁨" + value.replace(/[\u2066-\u2069]/g, (c) => (c < "⁩" ? ++open : open && open--) ? c : "") + "⁩".repeat(open + 1) : value;
18
+ //#endregion
19
+ //#region src/runtime/intl.ts
20
+ /**
21
+ * Intl formatter caches and the scope-taking implementations behind `fmt`
22
+ * (PLAN §5.12, §5.17). Constructing an Intl formatter costs 23–228 µs and a
23
+ * cached lookup under 1.5 µs (report 06), so no call path constructs one per
24
+ * call: IR options are stable objects and get an identity-keyed cache, while
25
+ * caller-supplied `fmt` options go through a bounded map keyed by value.
26
+ */
27
+ /** Whether date options render a time of day. */
28
+ const hasTime = (options) => [
29
+ "hour",
30
+ "minute",
31
+ "second",
32
+ "timeStyle",
33
+ "dayPeriod"
34
+ ].some((k) => own(options, k));
35
+ /**
36
+ * Maps U+202F to a plain space when `ssr.normalizeSpaces` is on, so server and
37
+ * browser ICU versions hydrate to the same text. Numbers never come here: the
38
+ * narrow no-break space is a real group separator there (`1 234,56`).
39
+ */
40
+ const normalize = (text) => S.registration?.config.ssr.normalizeSpaces === false ? text : text.replace(/\u202F/g, " ");
41
+ /** Time-of-day output: also marks the scope, so `/server` keeps per-user-zone responses private. */
42
+ const timeText = (text, scope) => {
43
+ scope.formattedTime = true;
44
+ return normalize(text);
45
+ };
46
+ const NO_OPTIONS = [
47
+ {},
48
+ {},
49
+ {
50
+ hour: "numeric",
51
+ minute: "numeric",
52
+ second: "numeric"
53
+ }
54
+ ];
55
+ const irCache = /* @__PURE__ */ new WeakMap();
56
+ const interned = /* @__PURE__ */ new Map();
57
+ const intern = (o) => {
58
+ const k = JSON.stringify(o);
59
+ let i = interned.get(k);
60
+ if (!i) {
61
+ trim(interned, 499);
62
+ interned.set(k, i = o);
63
+ }
64
+ return i;
65
+ };
66
+ /**
67
+ * `{x, number|date|time, …}` (opcodes 4, 5, 6) and `#` (4 without options).
68
+ * A missing value renders nothing, like a plain `{x}`: never the current
69
+ * time, 1970 or "NaN" (`#` gets null when its plural has no value).
70
+ */
71
+ function formatPart(op, options, value, scope) {
72
+ if (value == null) return "";
73
+ const o = options ? DEV ? intern(options) : options : NO_OPTIONS[op - 4];
74
+ let formatters = irCache.get(o);
75
+ if (!formatters) irCache.set(o, formatters = /* @__PURE__ */ new Map());
76
+ const { locale, timeZone: zone } = scope;
77
+ const k = locale + (op === 4 || !zone ? "" : "\0" + zone.toLowerCase());
78
+ let e = formatters.get(k);
79
+ if (!e) {
80
+ trim(formatters, 63);
81
+ formatters.set(k, e = op === 4 ? [new Intl.NumberFormat(locale, o)] : [new Intl.DateTimeFormat(locale, {
82
+ timeZone: zone,
83
+ ...o
84
+ }), op === 6 || hasTime(o)]);
85
+ }
86
+ try {
87
+ const s = e[0].format(value);
88
+ return e[1] ? timeText(s, scope) : s;
89
+ } catch {
90
+ return String(value);
91
+ }
92
+ }
93
+ const plurals = /* @__PURE__ */ new Map();
94
+ /** PluralRules in a fixed (locale, type) slot. */
95
+ const pluralRules = (locale, ordinal) => {
96
+ let slot = plurals.get(locale);
97
+ if (!slot) plurals.set(locale, slot = []);
98
+ return slot[+ordinal] ??= new Intl.PluralRules(locale, { type: ordinal ? "ordinal" : "cardinal" });
99
+ };
100
+ const memo = /* @__PURE__ */ new Map();
101
+ const stable = (o) => o ? Object.keys(o).sort().map((k) => k + ":" + JSON.stringify(o[k])).join() : "";
102
+ /**
103
+ * The formatter of `kind` for the scope's locale. Only date kinds depend on
104
+ * the zone, and `df` puts it into their `kind`: with per-user zones, a key
105
+ * holding it for every kind would churn the map and construct number
106
+ * formatters per call.
107
+ */
108
+ function cached(kind, scope, options, make) {
109
+ const key = kind + "\0" + scope.locale + "\0" + stable(options);
110
+ let f = memo.get(key);
111
+ if (f === void 0) {
112
+ trim(memo, 499);
113
+ memo.set(key, f = make(scope.locale));
114
+ }
115
+ return f;
116
+ }
117
+ const nf = (scope, kind, options, extra) => cached(kind, scope, options, (l) => new Intl.NumberFormat(l, {
118
+ ...options,
119
+ ...extra
120
+ }));
121
+ const df = (scope, kind, options) => cached(kind + (scope.timeZone ?? ""), scope, options, (l) => new Intl.DateTimeFormat(l, {
122
+ timeZone: scope.timeZone,
123
+ ...options
124
+ }));
125
+ /**
126
+ * A date input as every engine's Intl takes it, and the zone it is read in
127
+ * (PLAN §4.2, INTERNALS §6 D208). Temporal values are recognised by their
128
+ * tag, so native objects and a polyfill's alike, and converted everywhere:
129
+ * Intl takes them only where the engine has Temporal (not Node 22), never a
130
+ * ZonedDateTime, and only in the formatter's calendar, while a server and a
131
+ * browser must render the same text for hydration. Exact times (Instant,
132
+ * ZonedDateTime) become epoch ms, a ZonedDateTime read in its own zone;
133
+ * wall-clock values (PlainDate, PlainDateTime, PlainTime, PlainYearMonth,
134
+ * PlainMonthDay) become their ISO fields read in UTC, so no zone moves them.
135
+ */
136
+ const instant = (value) => {
137
+ let v = value;
138
+ if (!String(v[Symbol.toStringTag]).startsWith("Temporal.")) return [value];
139
+ if (v.epochMilliseconds != null) return [v.epochMilliseconds, v.timeZoneId];
140
+ v = v.withCalendar?.("iso8601") ?? v;
141
+ return [Date.UTC(v.year ?? 1972, (v.monthCode?.slice(1) ?? 1) - 1, v.day ?? 1, v.hour ?? 0, v.minute ?? 0, v.second ?? 0, v.millisecond ?? 0), "UTC"];
142
+ };
143
+ /** The cached formatter for `value` (its own zone wins) and the value it formats. */
144
+ const dt = (scope, kind, options, value) => {
145
+ const [v, zone] = instant(value);
146
+ return [df(scope, kind, zone ? {
147
+ ...options,
148
+ timeZone: zone
149
+ } : options), v];
150
+ };
151
+ const fNumber = (scope, value, options) => nf(scope, "n", options).format(value);
152
+ const fCurrency = (scope, value, currency, options) => nf(scope, "c" + currency, options, {
153
+ style: "currency",
154
+ currency
155
+ }).format(value);
156
+ /**
157
+ * ISO 4217 list one: the minor unit of every code whose minor unit is not 2.
158
+ * ISO gives the last group none (N.A.: precious metals, bond-market units,
159
+ * XDR, XSU, XUA, XTS for testing, XXX for no currency), so an amount of them
160
+ * counts whole units. Intl reports CLDR's display digits instead, which are
161
+ * not ISO's and change with the ICU version (CLDR 48 shows COP, HUF, IDR and
162
+ * PKR without decimals, CLDR 47 RSD), and browsers ship different ones: Intl
163
+ * only formats.
164
+ */
165
+ const MINOR_UNITS = {
166
+ BIF: 0,
167
+ CLP: 0,
168
+ DJF: 0,
169
+ GNF: 0,
170
+ ISK: 0,
171
+ JPY: 0,
172
+ KMF: 0,
173
+ KRW: 0,
174
+ PYG: 0,
175
+ RWF: 0,
176
+ UGX: 0,
177
+ UYI: 0,
178
+ VND: 0,
179
+ VUV: 0,
180
+ XAF: 0,
181
+ XOF: 0,
182
+ XPF: 0,
183
+ BHD: 3,
184
+ IQD: 3,
185
+ JOD: 3,
186
+ KWD: 3,
187
+ LYD: 3,
188
+ OMR: 3,
189
+ TND: 3,
190
+ CLF: 4,
191
+ UYW: 4,
192
+ XAG: 0,
193
+ XAU: 0,
194
+ XBA: 0,
195
+ XBB: 0,
196
+ XBC: 0,
197
+ XBD: 0,
198
+ XDR: 0,
199
+ XPD: 0,
200
+ XPT: 0,
201
+ XSU: 0,
202
+ XTS: 0,
203
+ XUA: 0,
204
+ XXX: 0
205
+ };
206
+ /**
207
+ * Minor units: divided by the currency's ISO 4217 minor unit (JPY 0, USD 2,
208
+ * BHD 3, `MINOR_UNITS`), never a fixed 100 nor CLDR's display digits, which
209
+ * only decide what the output shows. Bigints become an exact decimal string,
210
+ * which Intl formats without going through a double.
211
+ */
212
+ const fCurrencyMinor = (scope, minor, currency, options) => {
213
+ const d = MINOR_UNITS[currency.toUpperCase()] ?? 2;
214
+ let value;
215
+ if (typeof minor === "bigint") {
216
+ const s = (minor < 0n ? -minor : minor).toString().padStart(d + 1, "0");
217
+ value = (minor < 0n ? "-" : "") + s.slice(0, s.length - d) + (d ? "." + s.slice(-d) : "");
218
+ } else value = minor / 10 ** d;
219
+ return fCurrency(scope, value, currency, options);
220
+ };
221
+ const fUnit = (scope, value, unit, options) => nf(scope, "u" + unit, options, {
222
+ style: "unit",
223
+ unit
224
+ }).format(value);
225
+ const fCompact = (scope, value, options) => nf(scope, "k", options, { notation: "compact" }).format(value);
226
+ const fPercent = (scope, value, options) => nf(scope, "p", options, { style: "percent" }).format(value);
227
+ const fDate = (scope, value, options) => {
228
+ const [f, v] = dt(scope, "d", options, value);
229
+ const s = f.format(v);
230
+ return options && hasTime(options) ? timeText(s, scope) : s;
231
+ };
232
+ const SHORT_TIME = { timeStyle: "short" };
233
+ const fTime = (scope, value, options) => {
234
+ const [f, v] = dt(scope, "t", options ?? SHORT_TIME, value);
235
+ return timeText(f.format(v), scope);
236
+ };
237
+ const fDateRange = (scope, start, end, options) => {
238
+ const [f, a] = dt(scope, "d", options, start);
239
+ const s = f.formatRange(a, instant(end)[0]);
240
+ return options && hasTime(options) ? timeText(s, scope) : normalize(s);
241
+ };
242
+ const SECONDS = {
243
+ second: 1,
244
+ minute: 60,
245
+ hour: 3600,
246
+ day: 86400,
247
+ week: 604800,
248
+ month: 2629746,
249
+ quarter: 7889238,
250
+ year: 31556952
251
+ };
252
+ const AUTO_UNITS = [
253
+ "second",
254
+ "minute",
255
+ "hour",
256
+ "day",
257
+ "week",
258
+ "month",
259
+ "year"
260
+ ];
261
+ const fRelative = (scope, value, options) => {
262
+ const { now, unit, ...rest } = options ?? {};
263
+ const seconds = (+(value.epochMilliseconds ?? value) - (now ?? scope.now ?? Date.now())) / 1e3;
264
+ let u = unit ? unit.replace(/s$/, "") : "";
265
+ if (!own(SECONDS, u)) {
266
+ const a = Math.abs(seconds);
267
+ let i = 0;
268
+ for (; i < 6; i++) {
269
+ const s = SECONDS[AUTO_UNITS[i]];
270
+ if (Math.max(a, Math.round(a / s) * s) < SECONDS[AUTO_UNITS[i + 1]]) break;
271
+ }
272
+ u = AUTO_UNITS[i];
273
+ }
274
+ return cached("r", scope, rest, (l) => new Intl.RelativeTimeFormat(l, rest)).format(Math.round(seconds / SECONDS[u]) + 0, u);
275
+ };
276
+ const fList = (scope, items, options) => cached("l", scope, options, (l) => new Intl.ListFormat(l, options)).format(items);
277
+ const fCollator = (scope, options) => cached("o", scope, options, (l) => new Intl.Collator(l, options));
278
+ const fCompare = (scope, a, b, options) => fCollator(scope, options).compare(a, b);
279
+ const fSegment = (scope, text, options) => cached("g", scope, options, (l) => new Intl.Segmenter(l, options)).segment(text);
280
+ const fDisplayName = (scope, code, options) => {
281
+ try {
282
+ return cached("y", scope, options, (l) => new Intl.DisplayNames(l, options)).of(code);
283
+ } catch {
284
+ return;
285
+ }
286
+ };
287
+ const DURATION_UNITS = [
288
+ "year",
289
+ "month",
290
+ "week",
291
+ "day",
292
+ "hour",
293
+ "minute",
294
+ "second",
295
+ "millisecond"
296
+ ];
297
+ /**
298
+ * `Intl.DurationFormat` where the engine has it; elsewhere (Node 22) unit
299
+ * numbers joined by a unit list, which renders the same text for the common
300
+ * styles (`1 hr, 30 min`), and `h:mm:ss` for `digital`.
301
+ */
302
+ const fDuration = (scope, duration, options) => {
303
+ const style = options?.style ?? "short";
304
+ const DF = Intl.DurationFormat;
305
+ if (DF) return cached("D", scope, options, (l) => new DF(l, { style })).format(duration);
306
+ const parts = [];
307
+ const display = style === "digital" ? "short" : style;
308
+ const v = (u) => duration[u + "s"] ?? 0;
309
+ for (const u of DURATION_UNITS) {
310
+ if (style === "digital" && u === "hour") {
311
+ const two = nf(scope, "n", { minimumIntegerDigits: 2 });
312
+ parts.push(fNumber(scope, v("hour")) + ":" + two.format(v("minute")) + ":" + two.format(v("second") + v("millisecond") / 1e3));
313
+ break;
314
+ }
315
+ if (v(u)) parts.push(nf(scope, "U" + u + display, { unitDisplay: display }, {
316
+ style: "unit",
317
+ unit: u
318
+ }).format(v(u)));
319
+ }
320
+ if (!parts.length) parts.push(nf(scope, "Usecond" + display, { unitDisplay: display }, {
321
+ style: "unit",
322
+ unit: "second"
323
+ }).format(0));
324
+ return fList(scope, parts, {
325
+ type: "unit",
326
+ style: display === "long" ? "long" : display
327
+ });
328
+ };
329
+ //#endregion
330
+ //#region src/runtime/format.ts
331
+ /**
332
+ * The IR interpreter (INTERNALS §2.3 "Interpretation"). Normative: the server
333
+ * and the client must render byte-identical text, so every rule lives here
334
+ * once and `rich.ts` reuses the helpers. Only plain text is rendered here:
335
+ * `t()` ships this file, and the rich-text outputs (handlers, nodes, HTML
336
+ * escaping) are `rich.ts`'s.
337
+ */
338
+ /**
339
+ * A value by argument name. Own properties only: a message argument named
340
+ * `constructor` must not read `Object.prototype.constructor` off a values object.
341
+ */
342
+ const valueOf = (values, name) => values && own(values, name) ? values[name] : void 0;
343
+ /**
344
+ * The case of a select (`String(value)`) or plural (`=N`, then the category)
345
+ * part. `n` receives the `#` value of the case: the plural value minus the
346
+ * offset, or the enclosing one for a select.
347
+ */
348
+ function caseOf(part, value, locale) {
349
+ const cases = part[2];
350
+ let key = String(value);
351
+ if (part[1] > 1) {
352
+ const n = Number(value ?? NaN);
353
+ if (!own(cases, key = "=" + n)) key = pluralRules(locale, part[1] > 2).select(n - (part[3] ?? 0));
354
+ }
355
+ return own(cases, key) ? cases[key] : cases.other;
356
+ }
357
+ /** `#` value inside a plural case: the raw number minus the offset; null (nothing) without a value. */
358
+ const poundOf = (part, value) => value == null ? null : Number(value) - (part[3] ?? 0);
359
+ /** `{x, date, medium}`'s options (FormatJS's default `formats`), one object so its formatters are cached. */
360
+ const MEDIUM = {
361
+ month: "short",
362
+ day: "numeric",
363
+ year: "numeric"
364
+ };
365
+ /**
366
+ * Text of a plain `{arg}`: strings are bidi-isolated, numbers and bigints use
367
+ * `String()` (FormatJS parity; `{n, number}` localises), a `Date` renders as
368
+ * `{x, date, medium}` in the scope's locale and zone (`String(date)` is
369
+ * English in the host's zone, so server and browser would disagree), null
370
+ * and undefined render nothing.
371
+ */
372
+ function argText(value, scope) {
373
+ if (typeof value === "string") return isolate(value, scope.locale);
374
+ if (value instanceof Date) return formatPart(5, MEDIUM, value, scope);
375
+ return String(value ?? "");
376
+ }
377
+ /**
378
+ * Renders a compiled message as plain text in `scope` (tags render their
379
+ * children). Recursion passes `n`, the `#` value of the innermost plural, and
380
+ * `from`: tags reuse their own array from index 2, so no child list is ever
381
+ * sliced.
382
+ */
383
+ function formatMessage(message, values, scope, n = 0, from = 0) {
384
+ if (typeof message === "string") return message;
385
+ let out = "";
386
+ for (let i = from; i < message.length; i++) {
387
+ const p = message[i];
388
+ if (typeof p === "string") out += p;
389
+ else if (p === 0) out += formatPart(4, void 0, n, scope);
390
+ else {
391
+ const op = p[1];
392
+ if (op === 7) out += formatMessage(p, values, scope, n, 2);
393
+ else if (op !== 8) {
394
+ const value = valueOf(values, p[0]);
395
+ if (DEV && value == null && op !== 1) warnOnce(`no value for {${p[0]}} in ${JSON.stringify(message)}`);
396
+ if (op === void 0) out += argText(value, scope);
397
+ else if (op < 4) {
398
+ const c = caseOf(p, value, scope.locale);
399
+ if (c !== void 0) out += formatMessage(c, values, scope, op === 1 ? n : poundOf(p, value));
400
+ } else out += formatPart(op, p[2], value, scope);
401
+ }
402
+ }
403
+ }
404
+ return out;
405
+ }
406
+ //#endregion
407
+ //#region src/runtime/t.ts
408
+ /**
409
+ * `t` and `msg` (PLAN §5.3, §5.12; INTERNALS §2.11.2). The hot path for a
410
+ * plain string is: resolve the scope, one `Map.get`, one `typeof`.
411
+ */
412
+ const { resolve, track: notify } = S;
413
+ /**
414
+ * The message to render for `key` in `scope`. `track` notifies the framework
415
+ * trackers; translators bound to a fixed scope pass false.
416
+ */
417
+ function lookup(scope, key, track) {
418
+ let text;
419
+ let d;
420
+ if (key && typeof key === "object") {
421
+ d = key;
422
+ text = d.message ?? d.id ?? "";
423
+ } else text = String(key);
424
+ const id = d?.id ?? scope.sources?.get(text) ?? text;
425
+ if (!quiet) scope.used?.add(id);
426
+ if (track) notify();
427
+ if (DEV) scope.seen?.(id);
428
+ if (!BUILD && scope.test) return scope.test(id, d, text);
429
+ if (DEV && d && d.h !== void 0 && d.ir !== void 0) {
430
+ const label = scope.labels?.get(id);
431
+ if (label !== void 0 && label !== d.h) {
432
+ warnOnce(`${id}: translation is for other text than ${JSON.stringify(d.message)}; run vi18n extract`);
433
+ S.missing(scope, id, "source-mismatch");
434
+ return d.ir;
435
+ }
436
+ }
437
+ const m = scope.messages.get(id) ?? d?.ir;
438
+ if (m !== void 0) return m;
439
+ S.missing(scope, id, "unknown-id");
440
+ if (DEV) {
441
+ warnOnce(`no message ${JSON.stringify(text)} in ${scope.locale}: unknown id, or a call the plugin did not process`);
442
+ return text;
443
+ }
444
+ return scope.kind === "page" && !S.serverResolver && S.registration?.consumer !== "server" ? "" : text;
445
+ }
446
+ const t = ((key, values) => {
447
+ const scope = resolve();
448
+ return formatMessage(lookup(scope, key, true), values, scope);
449
+ });
450
+ function msg(message) {
451
+ return message;
452
+ }
453
+ //#endregion
454
+ export { fTime as C, fSegment as S, formatPart as T, fDuration as _, caseOf as a, fPercent as b, valueOf as c, fCompare as d, fCurrency as f, fDisplayName as g, fDateRange as h, argText as i, fCollator as l, fDate as m, msg as n, formatMessage as o, fCurrencyMinor as p, t as r, poundOf as s, lookup as t, fCompact as u, fList as v, fUnit as w, fRelative as x, fNumber as y };
package/dist/test.d.ts ADDED
@@ -0,0 +1,116 @@
1
+ import { a as Message, y as Translator } from "./types-DsdWBG0b.js";
2
+ //#region src/test/i18n.d.ts
3
+ /** What every message renders as: its translation or source, its id, or pseudo text. */
4
+ type TestMode = "source" | "ids" | "pseudo";
5
+ /** One locale's translations from the project's catalogs (`loadCatalog`). */
6
+ interface TestCatalog {
7
+ readonly locale: string;
8
+ /** Id → compiled message, fallbacks applied; untranslated ids are absent and render the source. */
9
+ readonly messages: ReadonlyMap<string, Message>;
10
+ }
11
+ interface TestI18nOptions {
12
+ /**
13
+ * The locale to render in (formatting, plural rules, `getLocale()`,
14
+ * `<html lang dir>`). Default: the catalog's, `en-XA` in pseudo mode, else
15
+ * the source locale.
16
+ */
17
+ locale?: string;
18
+ /** Default `"source"`: translations where given, the source text elsewhere. */
19
+ mode?: TestMode;
20
+ /** Real translations, from `await loadCatalog(locale)`. */
21
+ catalog?: TestCatalog;
22
+ /** Hand-written translations (ICU), keyed by id or by source text; they win over `catalog`. */
23
+ messages?: {
24
+ readonly [idOrSource: string]: string;
25
+ };
26
+ /** IANA zone for dates and times. Default `"UTC"`. */
27
+ timeZone?: string;
28
+ /** `fmt.relative`'s now. Default 2026-01-01T00:00:00Z. */
29
+ now?: number | Date;
30
+ /** Ids mode for calls the plugin did not rewrite: the generated id length (`ids.length`). Default 12. */
31
+ idLength?: number;
32
+ }
33
+ /** The API bound to the test scope, and the way back. */
34
+ interface TestI18n extends Translator {
35
+ readonly mode: TestMode;
36
+ /** Restores what the first `createTestI18n` found (the plugin's source-locale default). */
37
+ reset(): void;
38
+ }
39
+ /** Puts the page back as the first `createTestI18n` found it. */
40
+ export declare function resetTestI18n(): void;
41
+ /**
42
+ * Renders every `t()` of the test in `options.locale` and `options.mode`
43
+ * until `reset()` (or the next `createTestI18n`, which replaces it). Without
44
+ * a call, tests under the plugin render the source locale as before.
45
+ */
46
+ export declare function createTestI18n(options?: TestI18nOptions): TestI18n;
47
+ //#endregion
48
+ //#region src/test/catalog.d.ts
49
+ interface LoadCatalogOptions {
50
+ /**
51
+ * Where to look for `vi18n.config.ts` (walking up). Default: the project
52
+ * the plugin resolved under Vitest, else the working directory.
53
+ */
54
+ root?: string;
55
+ /** The config file, relative to `root`. */
56
+ configFile?: string;
57
+ }
58
+ /**
59
+ * The locale's translations, fallbacks applied; untranslated and unusable
60
+ * entries are left out, so those messages render their source text, as in
61
+ * `vite dev`. Memoised per project and locale for the test file.
62
+ */
63
+ export declare function loadCatalog(locale: string, options?: LoadCatalogOptions): Promise<TestCatalog>;
64
+ //#endregion
65
+ //#region src/test/pseudo.d.ts
66
+ /**
67
+ * Pseudo-locale checks for browser tests (PLAN §4.14, §5.21; INTERNALS §6
68
+ * D193): `en-XA` and `ar-XB` bracket every rendered message, so on a page
69
+ * switched to one, letters outside any bracket pair are text that never went
70
+ * through `t()`. The page is typed by the two methods used, so Playwright's
71
+ * `Page` fits and nothing here depends on Playwright. This module imports no
72
+ * runtime: the scanners run in the test runner, the page has its own copy.
73
+ */
74
+ /** What the helpers call on a Playwright `Page` (or anything with the same methods). */
75
+ interface PseudoPage {
76
+ evaluate(fn: (arg: unknown) => unknown, arg?: unknown): Promise<unknown>;
77
+ waitForFunction(fn: (arg: unknown) => unknown, arg?: unknown, options?: {
78
+ timeout?: number;
79
+ }): Promise<unknown>;
80
+ }
81
+ interface PseudoLeak {
82
+ /** The run of text found outside brackets, allowed parts included. */
83
+ text: string;
84
+ /** Up to 10 characters on either side of it, for the failure message. */
85
+ context: string;
86
+ }
87
+ interface PseudoScanOptions {
88
+ /**
89
+ * Text that may legitimately appear outside brackets: user data, brand
90
+ * names. Each occurrence is cut out of a run and the rest is still checked,
91
+ * so allowing `Ann` does not hide the hard-coded `Welcome back,` next to it.
92
+ */
93
+ allow?: readonly (string | RegExp)[];
94
+ /** `scanPseudo` only: CSS selectors whose text is not checked, beside `[data-pseudo-ignore]`. */
95
+ ignore?: readonly string[];
96
+ }
97
+ /**
98
+ * Runs of letters at bracket depth 0 in `text`. The depth is per line: page
99
+ * text joins blocks with newlines, and a stray `]` in one block must not hide
100
+ * a leak in the next.
101
+ */
102
+ export declare function findPseudoLeaks(text: string, options?: PseudoScanOptions): PseudoLeak[];
103
+ /** Text outside pseudo brackets on the page as it is now; empty means clean. */
104
+ export declare function scanPseudo(page: PseudoPage, options?: PseudoScanOptions): Promise<PseudoLeak[]>;
105
+ /**
106
+ * Switches the page to a pseudo-locale through its own vi18n runtime
107
+ * (`setLocale`, in place or by navigation, as the app's routing does it) and
108
+ * waits until `<html lang>` names it and its strings are loaded. Pseudo-locales
109
+ * exist in `vite dev` and non-production builds that list them in
110
+ * `pseudoLocales`.
111
+ */
112
+ export declare function setPseudoLocale(page: PseudoPage, locale?: "en-XA" | "ar-XB", options?: {
113
+ timeout?: number;
114
+ }): Promise<void>;
115
+ //#endregion
116
+ export type { LoadCatalogOptions, PseudoLeak, PseudoPage, PseudoScanOptions, TestCatalog, TestI18n, TestI18nOptions, TestMode };