@menapo/vi18n 0.0.0-stage → 0.1.1

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-BIKEuu6B.js +201 -0
  6. package/dist/check-HsmmpJZg.js +1 -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-BaDh0cHG.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-C2ENBRJe.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-C9Sy6UWv.js +103 -0
  25. package/dist/extract-C3S8btx0.js +298 -0
  26. package/dist/formats-9unqj_fZ.js +781 -0
  27. package/dist/i18next-9HAoxwUI.js +1 -0
  28. package/dist/i18next-BEJgv_S8.js +1596 -0
  29. package/dist/import-nANvQutw.js +225 -0
  30. package/dist/index.d.ts +156 -0
  31. package/dist/index.js +239 -0
  32. package/dist/init-C2ScuW6a.js +792 -0
  33. package/dist/library-D5CWvTQs.js +1882 -0
  34. package/dist/lingui-zUiCl9Um.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-RMMMc88P.js +31 -0
  41. package/dist/match-BGr8EXB3.js +89 -0
  42. package/dist/mcp-CHVwBoRP.js +28 -0
  43. package/dist/merge-driver-BTX87aVm.js +422 -0
  44. package/dist/messages-BfjIExyB.js +166 -0
  45. package/dist/migrate-IpLDMXmq.js +1484 -0
  46. package/dist/migrate-rCQuOGsf.js +1 -0
  47. package/dist/openai-CTaMDcl-.js +346 -0
  48. package/dist/output-DWE3SIUA.js +164 -0
  49. package/dist/parse.d.ts +62 -0
  50. package/dist/parse.js +156 -0
  51. package/dist/pipeline-C9Dbotkh.js +2031 -0
  52. package/dist/pipeline-DbX6rlo5.js +1 -0
  53. package/dist/project-Bw9UPIdx.js +1046 -0
  54. package/dist/project-C4GpaN-n.js +381 -0
  55. package/dist/pseudo-DRv30iN5.js +145 -0
  56. package/dist/raw-C4Y6vyGY.js +4708 -0
  57. package/dist/react-intl-BP0u-D5m.js +519 -0
  58. package/dist/restore-CiYv-jxS.js +175 -0
  59. package/dist/rewrite-EsIw_lsq.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-BEcnBbu-.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-Da1Ot9IN.js +10485 -0
  68. package/dist/server-deBOq2vq.js +11823 -0
  69. package/dist/server.d.ts +169 -0
  70. package/dist/server.js +1234 -0
  71. package/dist/sitemap-DJXzKoza.js +159 -0
  72. package/dist/sources-D19czDSA.js +2242 -0
  73. package/dist/status-zr4jY59Y.js +149 -0
  74. package/dist/stdio-CVaJfxA7.js +29 -0
  75. package/dist/stdio-FPOKQAgu.js +95 -0
  76. package/dist/sync-B9V2KFwb.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-CQnJfLjO.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-Byqi2guX.js +28 -0
  93. package/dist/vite-scan-CT_vsp4g.js +242 -0
  94. package/dist/vite.d.ts +34 -0
  95. package/dist/vite.js +7558 -0
  96. package/dist/vue-i18n-g0iGy6CZ.js +651 -0
  97. package/dist/which-BOemZ5Q8.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,694 @@
1
+ import { n as DEFAULT_CONFIG, o as isDiagnosticError } from "./types-D3QGM8XO.js";
2
+ import { t as VERSION } from "./version-Byqi2guX.js";
3
+ import { t as PROVIDER_PRESETS } from "./openai-CTaMDcl-.js";
4
+ import { t as EXCHANGE_FORMATS } from "./units-CQ5Q99RC.js";
5
+ import { n as createOutput } from "./output-DWE3SIUA.js";
6
+ import { t as EXIT } from "./types-wSh9fCBY.js";
7
+ import { t as CliExit } from "./project-C4GpaN-n.js";
8
+ import { resolve } from "node:path";
9
+ import { parseArgs } from "node:util";
10
+ //#region src/cli/args.ts
11
+ /**
12
+ * Command-line parsing (INTERNALS §2.14): `node:util` `parseArgs`, strict, so
13
+ * an unknown flag or a stray positional is a usage error (exit 2) instead of
14
+ * a silently different run. Global flags may appear before or after the
15
+ * command name.
16
+ */
17
+ const COMMANDS = [
18
+ "init",
19
+ "extract",
20
+ "check",
21
+ "status",
22
+ "compile",
23
+ "translate",
24
+ "migrate",
25
+ "sitemap",
26
+ "export",
27
+ "import",
28
+ "restore",
29
+ "which",
30
+ "merge-driver",
31
+ "lsp",
32
+ "mcp"
33
+ ];
34
+ /** Commands that take positional arguments. */
35
+ const POSITIONAL = /* @__PURE__ */ new Set([
36
+ "check",
37
+ "sitemap",
38
+ "import",
39
+ "which",
40
+ "merge-driver"
41
+ ]);
42
+ const CATEGORIES = [
43
+ "missing",
44
+ "placeholders",
45
+ "syntax"
46
+ ];
47
+ /** What `migrate --from` converts (PLAN §5.19). */
48
+ const MIGRATIONS = [
49
+ "saas-i18n",
50
+ "i18next",
51
+ "react-intl",
52
+ "lingui",
53
+ "vue-i18n"
54
+ ];
55
+ const GLOBAL = {
56
+ config: { type: "string" },
57
+ json: { type: "boolean" },
58
+ cwd: { type: "string" },
59
+ help: {
60
+ type: "boolean",
61
+ short: "h"
62
+ },
63
+ version: {
64
+ type: "boolean",
65
+ short: "v"
66
+ }
67
+ };
68
+ const OPTIONS = {
69
+ init: {
70
+ yes: {
71
+ type: "boolean",
72
+ short: "y"
73
+ },
74
+ format: { type: "string" },
75
+ locales: { type: "string" },
76
+ "source-locale": { type: "string" },
77
+ rich: { type: "boolean" }
78
+ },
79
+ extract: {
80
+ check: { type: "boolean" },
81
+ watch: { type: "boolean" },
82
+ yes: {
83
+ type: "boolean",
84
+ short: "y"
85
+ },
86
+ vite: { type: "boolean" }
87
+ },
88
+ check: {
89
+ ci: { type: "boolean" },
90
+ format: { type: "string" }
91
+ },
92
+ status: { locale: {
93
+ type: "string",
94
+ multiple: true
95
+ } },
96
+ compile: {
97
+ out: { type: "string" },
98
+ assets: { type: "string" },
99
+ strict: { type: "boolean" },
100
+ pseudo: { type: "boolean" },
101
+ check: { type: "boolean" }
102
+ },
103
+ translate: {
104
+ locale: {
105
+ type: "string",
106
+ multiple: true
107
+ },
108
+ model: { type: "string" },
109
+ retranslate: { type: "string" },
110
+ provider: { type: "string" },
111
+ tier: { type: "string" },
112
+ "dry-run": { type: "boolean" },
113
+ "max-cost": { type: "string" }
114
+ },
115
+ migrate: {
116
+ from: { type: "string" },
117
+ catalog: {
118
+ type: "string",
119
+ multiple: true
120
+ },
121
+ "keep-ids": { type: "boolean" },
122
+ write: { type: "boolean" }
123
+ },
124
+ sitemap: {
125
+ routes: { type: "string" },
126
+ "base-url": { type: "string" },
127
+ out: { type: "string" },
128
+ check: { type: "boolean" }
129
+ },
130
+ export: {
131
+ format: { type: "string" },
132
+ locale: {
133
+ type: "string",
134
+ multiple: true
135
+ },
136
+ out: { type: "string" },
137
+ missing: { type: "boolean" },
138
+ ai: { type: "boolean" }
139
+ },
140
+ import: {
141
+ format: { type: "string" },
142
+ locale: { type: "string" },
143
+ "dry-run": { type: "boolean" }
144
+ },
145
+ restore: {
146
+ from: { type: "string" },
147
+ locale: {
148
+ type: "string",
149
+ multiple: true
150
+ },
151
+ "dry-run": { type: "boolean" }
152
+ },
153
+ which: {},
154
+ "merge-driver": {},
155
+ lsp: {
156
+ stdio: { type: "boolean" },
157
+ clientProcessId: { type: "string" }
158
+ },
159
+ mcp: {}
160
+ };
161
+ /** Thrown inside flag conversion; becomes a `usage` result. */
162
+ var UsageError = class extends Error {};
163
+ function isCommand(name) {
164
+ return COMMANDS.includes(name);
165
+ }
166
+ /**
167
+ * The command is the first token that is neither a flag nor the value of a
168
+ * global flag that takes one (`--config x`, `--cwd x`), so `vi18n --cwd app
169
+ * extract` and `vi18n extract --cwd app` mean the same.
170
+ */
171
+ function findCommand(argv) {
172
+ for (let i = 0; i < argv.length; i++) {
173
+ const token = argv[i];
174
+ if (token === "--") return null;
175
+ if (token === "--config" || token === "--cwd") {
176
+ i++;
177
+ continue;
178
+ }
179
+ if (!token.startsWith("-")) return {
180
+ index: i,
181
+ name: token
182
+ };
183
+ }
184
+ return null;
185
+ }
186
+ function parseCommandLine(argv, cwd) {
187
+ const found = findCommand(argv);
188
+ const json = argv.includes("--json");
189
+ if (found === null) {
190
+ const rest = tryParse(argv, GLOBAL, false);
191
+ if ("error" in rest) return {
192
+ kind: "usage",
193
+ command: null,
194
+ message: rest.error,
195
+ json
196
+ };
197
+ if (rest.values.version === true) return {
198
+ kind: "version",
199
+ command: null,
200
+ json
201
+ };
202
+ if (rest.values.help === true) return {
203
+ kind: "help",
204
+ command: null,
205
+ json
206
+ };
207
+ return {
208
+ kind: "usage",
209
+ command: null,
210
+ message: `missing command; expected one of ${COMMANDS.join(", ")}`,
211
+ json
212
+ };
213
+ }
214
+ if (!isCommand(found.name)) return {
215
+ kind: "usage",
216
+ command: null,
217
+ message: `unknown command "${found.name}"; expected one of ${COMMANDS.join(", ")}`,
218
+ json
219
+ };
220
+ const command = found.name;
221
+ const parsed = tryParse([...argv.slice(0, found.index), ...argv.slice(found.index + 1)], {
222
+ ...GLOBAL,
223
+ ...OPTIONS[command]
224
+ }, POSITIONAL.has(command));
225
+ if ("error" in parsed) return {
226
+ kind: "usage",
227
+ command,
228
+ message: parsed.error,
229
+ json
230
+ };
231
+ const { values, positionals } = parsed;
232
+ if (values.version === true) return {
233
+ kind: "version",
234
+ command,
235
+ json
236
+ };
237
+ if (values.help === true) return {
238
+ kind: "help",
239
+ command,
240
+ json
241
+ };
242
+ const global = {
243
+ config: str(values.config),
244
+ json: values.json === true,
245
+ cwd: resolve(cwd, str(values.cwd) ?? ".")
246
+ };
247
+ try {
248
+ return {
249
+ kind: "run",
250
+ ...commandFlags(command, values, positionals, global)
251
+ };
252
+ } catch (error) {
253
+ if (error instanceof UsageError) return {
254
+ kind: "usage",
255
+ command,
256
+ message: error.message,
257
+ json: global.json
258
+ };
259
+ throw error;
260
+ }
261
+ }
262
+ function tryParse(args, options, allowPositionals) {
263
+ try {
264
+ const { values, positionals } = parseArgs({
265
+ args: [...args],
266
+ options,
267
+ strict: true,
268
+ allowPositionals
269
+ });
270
+ return {
271
+ values,
272
+ positionals
273
+ };
274
+ } catch (error) {
275
+ return { error: error instanceof Error ? error.message : String(error) };
276
+ }
277
+ }
278
+ function str(value) {
279
+ return typeof value === "string" ? value : null;
280
+ }
281
+ function bool(value) {
282
+ return value === true;
283
+ }
284
+ function list(value) {
285
+ if (!Array.isArray(value)) return [];
286
+ return value.filter((item) => typeof item === "string");
287
+ }
288
+ function oneOf(flag, value, allowed) {
289
+ if (value === null) return null;
290
+ if (allowed.includes(value)) return value;
291
+ throw new UsageError(`--${flag} must be one of ${allowed.join(", ")}, not "${value}"`);
292
+ }
293
+ function commandFlags(command, values, positionals, global) {
294
+ switch (command) {
295
+ case "init": {
296
+ const locales = (str(values.locales) ?? "").split(",").map((locale) => locale.trim()).filter((locale) => locale !== "");
297
+ return {
298
+ command,
299
+ global,
300
+ flags: {
301
+ yes: bool(values.yes),
302
+ format: oneOf("format", str(values.format), ["po", "json"]),
303
+ locales,
304
+ sourceLocale: str(values["source-locale"]),
305
+ rich: bool(values.rich)
306
+ }
307
+ };
308
+ }
309
+ case "extract": {
310
+ const flags = {
311
+ check: bool(values.check),
312
+ watch: bool(values.watch),
313
+ yes: bool(values.yes),
314
+ vite: bool(values.vite)
315
+ };
316
+ if (flags.check && flags.watch) throw new UsageError("--check and --watch cannot be combined");
317
+ if (flags.vite && flags.watch) throw new UsageError("--vite and --watch cannot be combined; vite dev keeps the catalogs current");
318
+ return {
319
+ command,
320
+ global,
321
+ flags
322
+ };
323
+ }
324
+ case "check": {
325
+ const categories = [];
326
+ for (const positional of positionals) {
327
+ const category = oneOf("category", positional, CATEGORIES);
328
+ if (category !== null && !categories.includes(category)) categories.push(category);
329
+ }
330
+ const format = oneOf("format", str(values.format), [
331
+ "text",
332
+ "github",
333
+ "json"
334
+ ]) ?? "text";
335
+ const flags = {
336
+ categories,
337
+ ci: bool(values.ci),
338
+ format: global.json ? "json" : format
339
+ };
340
+ if (flags.format === "json") global.json = true;
341
+ return {
342
+ command,
343
+ global,
344
+ flags
345
+ };
346
+ }
347
+ case "status": return {
348
+ command,
349
+ global,
350
+ flags: { locales: list(values.locale) }
351
+ };
352
+ case "compile": {
353
+ const flags = {
354
+ out: str(values.out),
355
+ assets: str(values.assets),
356
+ strict: bool(values.strict),
357
+ pseudo: bool(values.pseudo),
358
+ check: bool(values.check)
359
+ };
360
+ if (flags.check && flags.out === null) throw new UsageError("--check needs --out <dir>: the directory to compare");
361
+ if (flags.assets !== null && flags.out === null) throw new UsageError("--assets needs --out <dir>: the directory for config.json, sources.json and catalogs.js");
362
+ return {
363
+ command,
364
+ global,
365
+ flags
366
+ };
367
+ }
368
+ case "translate": {
369
+ const provider = oneOf("provider", str(values.provider), PROVIDER_PRESETS);
370
+ const model = str(values.model);
371
+ if (provider !== null && provider !== "gemini" && model === null) throw new UsageError(`--provider ${provider} needs --model <id>`);
372
+ const rawCost = str(values["max-cost"]);
373
+ let maxCost = null;
374
+ if (rawCost !== null) {
375
+ maxCost = rawCost.trim() === "" ? NaN : Number(rawCost);
376
+ if (!Number.isFinite(maxCost) || maxCost < 0) throw new UsageError(`--max-cost must be a non-negative number of USD, not "${rawCost}"`);
377
+ }
378
+ return {
379
+ command,
380
+ global,
381
+ flags: {
382
+ locales: list(values.locale),
383
+ model,
384
+ retranslate: oneOf("retranslate", str(values.retranslate), ["ai", "all"]),
385
+ provider,
386
+ tier: oneOf("tier", str(values.tier), [
387
+ "standard",
388
+ "flex",
389
+ "batch"
390
+ ]) ?? "standard",
391
+ dryRun: bool(values["dry-run"]),
392
+ maxCost
393
+ }
394
+ };
395
+ }
396
+ case "migrate": {
397
+ const from = oneOf("from", str(values.from), MIGRATIONS);
398
+ if (from === null) throw new UsageError(`--from is required (${MIGRATIONS.join(", ")})`);
399
+ const catalogs = [];
400
+ for (const raw of list(values.catalog)) {
401
+ const eq = raw.indexOf("=");
402
+ if (eq === -1) catalogs.push({
403
+ locale: null,
404
+ file: raw
405
+ });
406
+ else {
407
+ const locale = raw.slice(0, eq).trim();
408
+ const file = raw.slice(eq + 1).trim();
409
+ if (locale === "" || file === "") throw new UsageError(`--catalog ${raw}: expected <file> or <locale>=<file>`);
410
+ catalogs.push({
411
+ locale,
412
+ file
413
+ });
414
+ }
415
+ }
416
+ const keepIds = bool(values["keep-ids"]);
417
+ if (from === "saas-i18n") {
418
+ if (keepIds) throw new UsageError("--keep-ids is for the libraries: saas-i18n keys always become manual ids");
419
+ if (catalogs.length === 0) throw new UsageError("--catalog <file> is required: the default locale's key catalogue");
420
+ for (const entry of catalogs.slice(1)) if (entry.locale === null) throw new UsageError(`--catalog ${entry.file}: only the first --catalog may omit the locale (<locale>=<file>)`);
421
+ }
422
+ return {
423
+ command,
424
+ global,
425
+ flags: {
426
+ from,
427
+ catalogs,
428
+ keepIds,
429
+ write: bool(values.write)
430
+ }
431
+ };
432
+ }
433
+ case "sitemap": {
434
+ const flags = {
435
+ routes: positionals,
436
+ routesFile: str(values.routes),
437
+ baseUrl: str(values["base-url"]),
438
+ out: str(values.out),
439
+ check: bool(values.check)
440
+ };
441
+ if (flags.check && flags.out === null) throw new UsageError("--check needs --out <file>: the sitemap to compare");
442
+ return {
443
+ command,
444
+ global,
445
+ flags
446
+ };
447
+ }
448
+ case "export": {
449
+ const format = oneOf("format", str(values.format), EXCHANGE_FORMATS);
450
+ if (format === null) throw new UsageError(`--format is required: one of ${EXCHANGE_FORMATS.join(", ")}`);
451
+ return {
452
+ command,
453
+ global,
454
+ flags: {
455
+ format,
456
+ locales: list(values.locale),
457
+ out: str(values.out),
458
+ missing: bool(values.missing),
459
+ ai: bool(values.ai)
460
+ }
461
+ };
462
+ }
463
+ case "import": {
464
+ if (positionals.length === 0) throw new UsageError("name the file(s) to import");
465
+ const flags = {
466
+ format: oneOf("format", str(values.format), EXCHANGE_FORMATS),
467
+ locale: str(values.locale),
468
+ dryRun: bool(values["dry-run"]),
469
+ files: positionals
470
+ };
471
+ if (flags.locale !== null && positionals.length > 1) throw new UsageError("--locale applies to one file; import the others separately");
472
+ return {
473
+ command,
474
+ global,
475
+ flags
476
+ };
477
+ }
478
+ case "restore": {
479
+ const from = str(values.from);
480
+ if (from === null || from.trim() === "") throw new UsageError("--from <git-rev> is required: the revision to restore from");
481
+ if (from.startsWith("-")) throw new UsageError(`--from ${from}: not a revision`);
482
+ return {
483
+ command,
484
+ global,
485
+ flags: {
486
+ from,
487
+ locales: list(values.locale),
488
+ dryRun: bool(values["dry-run"])
489
+ }
490
+ };
491
+ }
492
+ case "which": {
493
+ const query = positionals.join(" ");
494
+ if (query.trim() === "") throw new UsageError("name an id, the start of one, or source text");
495
+ return {
496
+ command,
497
+ global,
498
+ flags: { query }
499
+ };
500
+ }
501
+ case "merge-driver": {
502
+ if (positionals.length < 3 || positionals.length > 4) throw new UsageError("expected <base> <ours> <theirs> [<path>], as git passes %O %A %B %P");
503
+ const [base, ours, theirs, path] = positionals;
504
+ return {
505
+ command,
506
+ global,
507
+ flags: {
508
+ base,
509
+ ours,
510
+ theirs,
511
+ path: path ?? null
512
+ }
513
+ };
514
+ }
515
+ case "lsp":
516
+ case "mcp":
517
+ if (global.json) throw new UsageError(`--json does not apply: vi18n ${command} speaks its protocol on stdio`);
518
+ if (command === "mcp") return {
519
+ command,
520
+ global,
521
+ flags: {}
522
+ };
523
+ return {
524
+ command,
525
+ global,
526
+ flags: { stdio: true }
527
+ };
528
+ }
529
+ }
530
+ const GLOBAL_HELP = `Global flags:
531
+ --config <path> vi18n.config.ts to use (default: searched upward from the cwd)
532
+ --cwd <dir> run as if started in <dir>
533
+ --json print one JSON object (the command's result) on stdout
534
+ --help, --version`;
535
+ const COMMAND_HELP = {
536
+ init: `vi18n init [--yes] [--format po|json] [--locales en,de] [--source-locale en] [--rich]
537
+ Write vi18n.config.ts, add the plugin to vite.config.*, create the catalogs and vi18n.gen.ts.
538
+ --rich copies a <T> rich-text component for Vue and Svelte into the project (also when configured).`,
539
+ extract: `vi18n extract [--check] [--watch] [--yes] [--vite]
540
+ Sync the catalogs with the code. --check writes nothing and fails on drift; --watch only adds;
541
+ --yes allows clearing or deleting more translations than the safety limit.
542
+ --vite transforms each file through the project's Vite config and plugins instead of reading it,
543
+ for plugins that change code before vi18n sees it (auto-imports, macros).`,
544
+ check: `vi18n check [missing] [placeholders] [syntax] [--ci] [--format text|github|json]
545
+ Validate catalogs against the code. --ci fails on warnings of the selected categories too.`,
546
+ status: `vi18n status [--json] [--locale <l>]…
547
+ Translation progress per locale, and per bundle when a build report exists.`,
548
+ compile: `vi18n compile [--out <dir>] [--assets <dir>] [--strict] [--pseudo] [--check]
549
+ Compiled JSON per locale plus sources.json and config.json, for code not built by Vite.
550
+ --assets puts the catalogs into a static-assets directory (vi18n/<version>/<locale>/catalog.json)
551
+ and writes <out>/catalogs.js, which reads them through the Workers ASSETS binding.
552
+ --check writes nothing and fails when a file is out of date.`,
553
+ translate: `vi18n translate [--locale <l>]… [--provider gemini|openai|openrouter|ollama] [--model <id>] [--tier standard|flex|batch] [--retranslate ai|all] [--dry-run] [--max-cost <usd>]
554
+ AI-translate the referenced messages that have no translation.
555
+ --provider overrides ai.provider (all but gemini need --model); --tier flex|batch (Gemini) costs half.`,
556
+ migrate: `vi18n migrate --from i18next|react-intl|lingui|vue-i18n|saas-i18n [--catalog [<locale>=]<file|dir>]… [--keep-ids] [--write]
557
+ Rewrite another library's calls into t()/msg()/rich() and its catalogs into vi18n's;
558
+ prints the plan without --write. --keep-ids keeps every key a developer chose as a manual id.`,
559
+ sitemap: `vi18n sitemap [<route>…] [--routes <file>] [--base-url <url>] [--out <file>] [--check]
560
+ Write sitemap.xml: every route in every locale, with hreflang alternates and x-default.
561
+ --base-url is the site's origin and base path (not needed when routing.domains has every locale);
562
+ --routes reads route paths from a file; without --out the sitemap goes to stdout.`,
563
+ export: `vi18n export --format po|json|flat|xliff2|arb|csv [--locale <l>]… [--out <dir>] [--missing] [--ai]
564
+ Write each locale's catalog in an exchange format for a TMS or translators. --missing keeps the
565
+ entries without a translation that would ship, --ai the AI translations; without --out, one
566
+ locale goes to stdout.`,
567
+ import: `vi18n import [--format po|json|flat|xliff2|arb|csv] [--locale <l>] [--dry-run] <file>…
568
+ Take translations from exchange files into the catalogs, only where the row's source is the
569
+ entry's exact source; reports what changed and what was skipped. --dry-run writes nothing.`,
570
+ restore: `vi18n restore --from <git-rev> [--locale <l>]… [--dry-run]
571
+ Fill empty entries from that revision's catalogs by exact source text or manual id.`,
572
+ which: `vi18n which <id|prefix|text>
573
+ Print a message's id, source text, call sites and catalog entries.`,
574
+ "merge-driver": `vi18n merge-driver <base> <ours> <theirs> [<path>]
575
+ Git merge driver for catalogs (%O %A %B %P): merges entry by entry and leaves conflict
576
+ markers only where both sides changed one translation differently.`,
577
+ lsp: `vi18n lsp [--stdio]
578
+ Language server for editors: hovers with every locale's translation, diagnostics for missing
579
+ and invalid translations, go to the catalog entry. Speaks LSP on stdin/stdout.`,
580
+ mcp: `vi18n mcp
581
+ Model Context Protocol server for agents: status, check, extract and compile checks, translate
582
+ --dry-run, and reading and writing one translation. Speaks MCP on stdin/stdout.`
583
+ };
584
+ function helpText(command, version) {
585
+ if (command !== null) return `${COMMAND_HELP[command]}\n\n${GLOBAL_HELP}\n`;
586
+ return `vi18n ${version}\n\nUsage:\n${COMMANDS.map((name) => " " + COMMAND_HELP[name].split("\n")[0]).join("\n")}\n\n${GLOBAL_HELP}\n`;
587
+ }
588
+ //#endregion
589
+ //#region src/cli/run.ts
590
+ /**
591
+ * The `vi18n` binary minus the process (INTERNALS §2.14, §3.5): parse argv,
592
+ * run one command, map failures to exit codes, print the `--json` envelope.
593
+ * Tests call `run` in-process with recording `CliIo`s; `bin.ts` wires the
594
+ * real process to it.
595
+ */
596
+ async function run(argv, io) {
597
+ const parsed = parseCommandLine(argv, io.cwd);
598
+ switch (parsed.kind) {
599
+ case "version":
600
+ case "help": {
601
+ const text = parsed.kind === "version" ? VERSION + "\n" : helpText(parsed.command, VERSION);
602
+ if (parsed.json && parsed.command !== null) createOutput(io, "json").finish(parsed.command, EXIT.ok, parsed.kind === "version" ? { version: VERSION } : { help: text });
603
+ else io.stdout(text);
604
+ return EXIT.ok;
605
+ }
606
+ case "usage": {
607
+ const out = createOutput(io, parsed.json ? "json" : "text");
608
+ out.addSecret(io.env[DEFAULT_CONFIG.ai.apiKeyEnv]);
609
+ io.stderr(out.redact(`vi18n: ${parsed.message}\n${parsed.command === null ? "Run vi18n --help for usage.\n" : `Run vi18n ${parsed.command} --help for usage.\n`}`));
610
+ if (parsed.command !== null) out.finish(parsed.command, EXIT.usage, { usage: parsed.message });
611
+ return EXIT.usage;
612
+ }
613
+ }
614
+ const commandIo = {
615
+ ...io,
616
+ cwd: parsed.global.cwd
617
+ };
618
+ const out = createOutput(commandIo, parsed.global.json ? "json" : parsed.command === "check" ? parsed.flags.format : "text");
619
+ out.addSecret(io.env[DEFAULT_CONFIG.ai.apiKeyEnv]);
620
+ const ctx = {
621
+ io: commandIo,
622
+ out,
623
+ global: parsed.global
624
+ };
625
+ let exitCode;
626
+ let data = null;
627
+ try {
628
+ let result;
629
+ switch (parsed.command) {
630
+ case "init":
631
+ result = await (await import("./init-C2ScuW6a.js")).init(ctx, parsed.flags);
632
+ break;
633
+ case "extract":
634
+ result = await (await import("./extract-C3S8btx0.js")).extract(ctx, parsed.flags);
635
+ break;
636
+ case "check":
637
+ result = await (await import("./check-HsmmpJZg.js")).check(ctx, parsed.flags);
638
+ break;
639
+ case "status":
640
+ result = await (await import("./status-zr4jY59Y.js")).status(ctx, parsed.flags);
641
+ break;
642
+ case "compile":
643
+ result = await (await import("./compile-BaDh0cHG.js")).compile(ctx, parsed.flags);
644
+ break;
645
+ case "translate":
646
+ result = await (await import("./translate-CQnJfLjO.js")).translate(ctx, parsed.flags);
647
+ break;
648
+ case "migrate":
649
+ result = await (await import("./migrate-rCQuOGsf.js")).migrate(ctx, parsed.flags);
650
+ break;
651
+ case "sitemap":
652
+ result = await (await import("./sitemap-DJXzKoza.js")).sitemap(ctx, parsed.flags);
653
+ break;
654
+ case "export":
655
+ result = await (await import("./export-C9Sy6UWv.js")).exportCatalogs(ctx, parsed.flags);
656
+ break;
657
+ case "import":
658
+ result = await (await import("./import-nANvQutw.js")).importCatalogs(ctx, parsed.flags);
659
+ break;
660
+ case "restore":
661
+ result = await (await import("./restore-CiYv-jxS.js")).restore(ctx, parsed.flags);
662
+ break;
663
+ case "which":
664
+ result = await (await import("./which-BOemZ5Q8.js")).which(ctx, parsed.flags);
665
+ break;
666
+ case "merge-driver":
667
+ result = await (await import("./merge-driver-BTX87aVm.js")).mergeDriver(ctx, parsed.flags);
668
+ break;
669
+ case "lsp":
670
+ result = await (await import("./lsp-RMMMc88P.js")).lsp(ctx, parsed.flags);
671
+ break;
672
+ case "mcp": result = await (await import("./mcp-CHVwBoRP.js")).mcp(ctx, parsed.flags);
673
+ }
674
+ exitCode = result.exitCode;
675
+ data = result.data;
676
+ } catch (error) {
677
+ if (error instanceof CliExit) {
678
+ out.report(error.diagnostics);
679
+ exitCode = error.exitCode;
680
+ data = error.data;
681
+ } else if (isDiagnosticError(error)) {
682
+ out.report([error.diagnostic]);
683
+ exitCode = error.diagnostic.code.startsWith("config-") ? EXIT.usage : EXIT.failure;
684
+ } else {
685
+ const message = error instanceof Error ? error.stack ?? error.message : String(error);
686
+ io.stderr(out.redact(`vi18n ${parsed.command}: ${message}\n`));
687
+ exitCode = EXIT.failure;
688
+ }
689
+ }
690
+ out.finish(parsed.command, exitCode, data);
691
+ return exitCode;
692
+ }
693
+ //#endregion
694
+ export { run as t };