@fluenti/cli 0.5.0 → 0.6.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 (118) hide show
  1. package/dist/cli/src/ai-provider.d.ts.map +1 -0
  2. package/dist/{catalog.d.ts → cli/src/catalog.d.ts} +1 -1
  3. package/dist/cli/src/catalog.d.ts.map +1 -0
  4. package/dist/cli/src/check.d.ts.map +1 -0
  5. package/dist/cli/src/cli.d.ts.map +1 -0
  6. package/dist/cli/src/codemod.d.ts +19 -0
  7. package/dist/cli/src/codemod.d.ts.map +1 -0
  8. package/dist/cli/src/compile-cache.d.ts.map +1 -0
  9. package/dist/cli/src/compile-runner.d.ts.map +1 -0
  10. package/dist/cli/src/compile-worker.d.ts.map +1 -0
  11. package/dist/cli/src/compile.d.ts.map +1 -0
  12. package/dist/cli/src/config-loader.d.ts +2 -0
  13. package/dist/cli/src/config-loader.d.ts.map +1 -0
  14. package/dist/{config.d.ts → cli/src/config.d.ts} +1 -1
  15. package/dist/cli/src/config.d.ts.map +1 -0
  16. package/dist/cli/src/doctor.d.ts +19 -0
  17. package/dist/cli/src/doctor.d.ts.map +1 -0
  18. package/dist/{extract-cache.d.ts → cli/src/extract-cache.d.ts} +1 -1
  19. package/dist/cli/src/extract-cache.d.ts.map +1 -0
  20. package/dist/cli/src/extract-runner.d.ts.map +1 -0
  21. package/dist/cli/src/glossary.d.ts.map +1 -0
  22. package/dist/{index.d.ts → cli/src/index.d.ts} +7 -3
  23. package/dist/cli/src/index.d.ts.map +1 -0
  24. package/dist/cli/src/init.d.ts.map +1 -0
  25. package/dist/cli/src/json-format.d.ts.map +1 -0
  26. package/dist/cli/src/lint.d.ts.map +1 -0
  27. package/dist/cli/src/migrate.d.ts.map +1 -0
  28. package/dist/cli/src/parallel-compile.d.ts.map +1 -0
  29. package/dist/cli/src/po-format.d.ts.map +1 -0
  30. package/dist/cli/src/stats-format.d.ts.map +1 -0
  31. package/dist/cli/src/translate-parse.d.ts.map +1 -0
  32. package/dist/cli/src/translate-prompt.d.ts.map +1 -0
  33. package/dist/cli/src/translate.d.ts.map +1 -0
  34. package/dist/{tsx-extractor.d.ts → cli/src/tsx-extractor.d.ts} +1 -1
  35. package/dist/cli/src/tsx-extractor.d.ts.map +1 -0
  36. package/dist/cli/src/validation.d.ts.map +1 -0
  37. package/dist/{vue-extractor.d.ts → cli/src/vue-extractor.d.ts} +1 -1
  38. package/dist/cli/src/vue-extractor.d.ts.map +1 -0
  39. package/dist/cli.cjs +10 -22
  40. package/dist/cli.cjs.map +1 -1
  41. package/dist/cli.js +333 -406
  42. package/dist/cli.js.map +1 -1
  43. package/dist/compile-C3VLvhUf.cjs +8 -0
  44. package/dist/{compile-kXClO6q4.cjs.map → compile-C3VLvhUf.cjs.map} +1 -1
  45. package/dist/{compile-CdA4EZ-p.js → compile-CZVpE5Md.js} +2 -2
  46. package/dist/{compile-CdA4EZ-p.js.map → compile-CZVpE5Md.js.map} +1 -1
  47. package/dist/compile-worker.cjs +1 -1
  48. package/dist/compile-worker.cjs.map +1 -1
  49. package/dist/compile-worker.js +1 -1
  50. package/dist/doctor-BqXXxyST.js +670 -0
  51. package/dist/doctor-BqXXxyST.js.map +1 -0
  52. package/dist/doctor-xp8WS8sr.cjs +24 -0
  53. package/dist/doctor-xp8WS8sr.cjs.map +1 -0
  54. package/dist/index.cjs +1 -1
  55. package/dist/index.cjs.map +1 -1
  56. package/dist/index.js +64 -64
  57. package/dist/index.js.map +1 -1
  58. package/dist/tsx-extractor-AOjsbOmp.cjs +2 -0
  59. package/dist/{tsx-extractor-B0vFXziu.cjs.map → tsx-extractor-AOjsbOmp.cjs.map} +1 -1
  60. package/dist/tsx-extractor-C-HZNobu.js.map +1 -1
  61. package/dist/vue-extractor.cjs +1 -1
  62. package/dist/vue-extractor.cjs.map +1 -1
  63. package/dist/vue-extractor.js.map +1 -1
  64. package/package.json +2 -2
  65. package/dist/ai-provider.d.ts.map +0 -1
  66. package/dist/catalog.d.ts.map +0 -1
  67. package/dist/check.d.ts.map +0 -1
  68. package/dist/cli.d.ts.map +0 -1
  69. package/dist/compile-cache.d.ts.map +0 -1
  70. package/dist/compile-kXClO6q4.cjs +0 -8
  71. package/dist/compile-runner.d.ts.map +0 -1
  72. package/dist/compile-worker.d.ts.map +0 -1
  73. package/dist/compile.d.ts.map +0 -1
  74. package/dist/config-loader.d.ts +0 -2
  75. package/dist/config-loader.d.ts.map +0 -1
  76. package/dist/config.d.ts.map +0 -1
  77. package/dist/extract-cache-BioSaoFo.cjs +0 -10
  78. package/dist/extract-cache-BioSaoFo.cjs.map +0 -1
  79. package/dist/extract-cache-C-MI1_ll.js +0 -325
  80. package/dist/extract-cache-C-MI1_ll.js.map +0 -1
  81. package/dist/extract-cache.d.ts.map +0 -1
  82. package/dist/extract-runner.d.ts.map +0 -1
  83. package/dist/glossary.d.ts.map +0 -1
  84. package/dist/index.d.ts.map +0 -1
  85. package/dist/init.d.ts.map +0 -1
  86. package/dist/json-format.d.ts.map +0 -1
  87. package/dist/lint.d.ts.map +0 -1
  88. package/dist/migrate.d.ts.map +0 -1
  89. package/dist/parallel-compile.d.ts.map +0 -1
  90. package/dist/po-format.d.ts.map +0 -1
  91. package/dist/stats-format.d.ts.map +0 -1
  92. package/dist/translate-parse.d.ts.map +0 -1
  93. package/dist/translate-prompt.d.ts.map +0 -1
  94. package/dist/translate.d.ts.map +0 -1
  95. package/dist/tsx-extractor-B0vFXziu.cjs +0 -2
  96. package/dist/tsx-extractor.d.ts.map +0 -1
  97. package/dist/validation.d.ts.map +0 -1
  98. package/dist/vue-extractor.d.ts.map +0 -1
  99. /package/dist/{ai-provider.d.ts → cli/src/ai-provider.d.ts} +0 -0
  100. /package/dist/{check.d.ts → cli/src/check.d.ts} +0 -0
  101. /package/dist/{cli.d.ts → cli/src/cli.d.ts} +0 -0
  102. /package/dist/{compile-cache.d.ts → cli/src/compile-cache.d.ts} +0 -0
  103. /package/dist/{compile-runner.d.ts → cli/src/compile-runner.d.ts} +0 -0
  104. /package/dist/{compile-worker.d.ts → cli/src/compile-worker.d.ts} +0 -0
  105. /package/dist/{compile.d.ts → cli/src/compile.d.ts} +0 -0
  106. /package/dist/{extract-runner.d.ts → cli/src/extract-runner.d.ts} +0 -0
  107. /package/dist/{glossary.d.ts → cli/src/glossary.d.ts} +0 -0
  108. /package/dist/{init.d.ts → cli/src/init.d.ts} +0 -0
  109. /package/dist/{json-format.d.ts → cli/src/json-format.d.ts} +0 -0
  110. /package/dist/{lint.d.ts → cli/src/lint.d.ts} +0 -0
  111. /package/dist/{migrate.d.ts → cli/src/migrate.d.ts} +0 -0
  112. /package/dist/{parallel-compile.d.ts → cli/src/parallel-compile.d.ts} +0 -0
  113. /package/dist/{po-format.d.ts → cli/src/po-format.d.ts} +0 -0
  114. /package/dist/{stats-format.d.ts → cli/src/stats-format.d.ts} +0 -0
  115. /package/dist/{translate-parse.d.ts → cli/src/translate-parse.d.ts} +0 -0
  116. /package/dist/{translate-prompt.d.ts → cli/src/translate-prompt.d.ts} +0 -0
  117. /package/dist/{translate.d.ts → cli/src/translate.d.ts} +0 -0
  118. /package/dist/{validation.d.ts → cli/src/validation.d.ts} +0 -0
package/dist/cli.js CHANGED
@@ -1,39 +1,39 @@
1
1
  #!/usr/bin/env node
2
- import { i as e, n as t, r as n, t as r } from "./compile-CdA4EZ-p.js";
2
+ import { i as e, n as t, r as n, t as r } from "./compile-CZVpE5Md.js";
3
3
  import { t as i } from "./tsx-extractor-C-HZNobu.js";
4
- import { a, c as o, i as s, n as c, o as l, r as u, s as d, t as f } from "./extract-cache-C-MI1_ll.js";
5
- import { parse as p, resolveLocaleCodes as m } from "@fluenti/core/internal";
6
- import { appendFileSync as h, existsSync as g, mkdirSync as _, readFileSync as v, statSync as y, writeFileSync as b } from "node:fs";
7
- import { dirname as x, extname as S, join as C, resolve as w } from "node:path";
8
- import { fileURLToPath as T } from "node:url";
9
- import E from "fast-glob";
10
- import { createHash as D } from "node:crypto";
11
- import { defineCommand as O, runMain as k } from "citty";
12
- import A from "consola";
13
- import { execFile as j } from "node:child_process";
14
- import { promisify as M } from "node:util";
15
- import { setTimeout as N } from "node:timers/promises";
4
+ import { a, c as o, d as s, f as c, l, n as u, o as d, p as f, r as p, s as m, t as h, u as g } from "./doctor-BqXXxyST.js";
5
+ import { parse as _, resolveLocaleCodes as v } from "@fluenti/core/compiler";
6
+ import { existsSync as y, mkdirSync as b, readFileSync as x, statSync as S, writeFileSync as C } from "node:fs";
7
+ import { dirname as w, extname as T, join as E, resolve as D } from "node:path";
8
+ import { fileURLToPath as O } from "node:url";
9
+ import k from "fast-glob";
10
+ import { createHash as A } from "node:crypto";
11
+ import j from "consola";
12
+ import { defineCommand as M, runMain as ee } from "citty";
13
+ import { execFile as N } from "node:child_process";
14
+ import { promisify as P } from "node:util";
15
+ import { setTimeout as F } from "node:timers/promises";
16
16
  //#region src/stats-format.ts
17
- var ee = "█", te = "░";
18
- function ne(e, t = 20) {
17
+ var te = "█", ne = "░";
18
+ function re(e, t = 20) {
19
19
  let n = Math.round(Math.max(0, Math.min(100, e)) / 100 * t);
20
- return ee.repeat(n) + te.repeat(t - n);
20
+ return te.repeat(n) + ne.repeat(t - n);
21
21
  }
22
- function re(e) {
22
+ function ie(e) {
23
23
  let t = e.toFixed(1) + "%";
24
24
  return e >= 90 ? `\x1b[32m${t}\x1b[0m` : e >= 70 ? `\x1b[33m${t}\x1b[0m` : `\x1b[31m${t}\x1b[0m`;
25
25
  }
26
- function ie(e, t, n) {
27
- let r = t > 0 ? n / t * 100 : 0, i = t > 0 ? re(r) : "—", a = t > 0 ? ne(r) : "";
26
+ function ae(e, t, n) {
27
+ let r = t > 0 ? n / t * 100 : 0, i = t > 0 ? ie(r) : "—", a = t > 0 ? re(r) : "";
28
28
  return ` ${e.padEnd(8)}│ ${String(t).padStart(5)} │ ${String(n).padStart(10)} │ ${a} ${i}`;
29
29
  }
30
30
  //#endregion
31
31
  //#region src/validation.ts
32
- var P = /\{(\w+),\s*(plural|select|selectordinal)\s*,/;
33
- function F(e) {
32
+ var I = /\{(\w+),\s*(plural|select|selectordinal)\s*,/;
33
+ function L(e) {
34
34
  try {
35
- let t = p(e), n = /* @__PURE__ */ new Set();
36
- return I(t, n), [...n].sort();
35
+ let t = _(e), n = /* @__PURE__ */ new Set();
36
+ return R(t, n), [...n].sort();
37
37
  } catch {
38
38
  let t = /* @__PURE__ */ new Set(), n = 0, r = 0;
39
39
  for (; r < e.length;) {
@@ -51,22 +51,22 @@ function F(e) {
51
51
  return [...t].sort();
52
52
  }
53
53
  }
54
- function I(e, t) {
54
+ function R(e, t) {
55
55
  for (let n of e) if (n.type === "variable" && n.name !== "#") t.add(n.name);
56
56
  else if (n.type === "plural" || n.type === "select") {
57
57
  t.add(n.variable);
58
- for (let e of Object.values(n.options)) I(e, t);
58
+ for (let e of Object.values(n.options)) R(e, t);
59
59
  } else n.type === "function" && t.add(n.variable);
60
60
  }
61
- function L(e) {
61
+ function z(e) {
62
62
  let t = /* @__PURE__ */ new Set(), n = /<\/?([a-zA-Z][\w-]*)[^>]*>/g, r;
63
63
  for (; (r = n.exec(e)) !== null;) t.add(r[1].toLowerCase());
64
64
  return [...t].sort();
65
65
  }
66
- function ae(e, t) {
67
- let n = F(e), r = F(t), i = L(e), a = L(t), o = n.filter((e) => !r.includes(e)), s = r.filter((e) => !n.includes(e)), c = i.filter((e) => !a.includes(e)), l = a.filter((e) => !i.includes(e)), u = [];
68
- if (P.test(t)) try {
69
- p(t);
66
+ function oe(e, t) {
67
+ let n = L(e), r = L(t), i = z(e), a = z(t), o = n.filter((e) => !r.includes(e)), s = r.filter((e) => !n.includes(e)), c = i.filter((e) => !a.includes(e)), l = a.filter((e) => !i.includes(e)), u = [];
68
+ if (I.test(t)) try {
69
+ _(t);
70
70
  } catch (e) {
71
71
  u.push(e.message);
72
72
  }
@@ -81,7 +81,7 @@ function ae(e, t) {
81
81
  }
82
82
  //#endregion
83
83
  //#region src/lint.ts
84
- function R(e, t) {
84
+ function B(e, t) {
85
85
  let n = [], { sourceLocale: r } = t, i = t.locales ?? Object.keys(e), a = e[r];
86
86
  if (!a) return n.push({
87
87
  rule: "missing-source",
@@ -113,7 +113,7 @@ function R(e, t) {
113
113
  });
114
114
  continue;
115
115
  }
116
- let s = F(r.message ?? e), c = F(o.translation), l = s.filter((e) => !c.includes(e)), u = c.filter((e) => !s.includes(e));
116
+ let s = L(r.message ?? e), c = L(o.translation), l = s.filter((e) => !c.includes(e)), u = c.filter((e) => !s.includes(e));
117
117
  l.length > 0 && n.push({
118
118
  rule: "inconsistent-placeholders",
119
119
  severity: "error",
@@ -156,9 +156,9 @@ function R(e, t) {
156
156
  });
157
157
  return n;
158
158
  }
159
- function oe(e) {
159
+ function se(e) {
160
160
  if (e.length === 0) return " ✓ All checks passed";
161
- let t = [], n = se(e, (e) => e.rule);
161
+ let t = [], n = ce(e, (e) => e.rule);
162
162
  for (let [e, r] of Object.entries(n)) {
163
163
  t.push(` ${e} (${r.length}):`);
164
164
  for (let e of r) {
@@ -169,7 +169,7 @@ function oe(e) {
169
169
  let r = e.filter((e) => e.severity === "error").length, i = e.filter((e) => e.severity === "warning").length, a = e.filter((e) => e.severity === "info").length;
170
170
  return t.push(""), t.push(` Summary: ${r} errors, ${i} warnings, ${a} info`), t.join("\n");
171
171
  }
172
- function se(e, t) {
172
+ function ce(e, t) {
173
173
  let n = {};
174
174
  for (let r of e) {
175
175
  let e = t(r);
@@ -179,7 +179,7 @@ function se(e, t) {
179
179
  }
180
180
  //#endregion
181
181
  //#region src/check.ts
182
- function ce(e, t) {
182
+ function le(e, t) {
183
183
  let { sourceLocale: n, minCoverage: r, locale: i } = t, a = e[n];
184
184
  if (!a) return {
185
185
  results: [],
@@ -227,10 +227,10 @@ function ce(e, t) {
227
227
  passed: p,
228
228
  minCoverage: r,
229
229
  actualCoverage: f,
230
- diagnostics: R(e, m)
230
+ diagnostics: B(e, m)
231
231
  };
232
232
  }
233
- function le(e) {
233
+ function ue(e) {
234
234
  let t = [];
235
235
  for (let n of e.results) {
236
236
  let r = n.coverage >= e.minCoverage ? "✓" : "✗", i = n.coverage.toFixed(1), a = n.missing > 0 ? ` — ${n.missing} missing` : "", o = n.fuzzy > 0 ? `, ${n.fuzzy} fuzzy` : "";
@@ -240,7 +240,7 @@ function le(e) {
240
240
  let n = e.actualCoverage.toFixed(1), r = e.passed ? "PASSED" : "FAILED";
241
241
  return t.push(`Coverage: ${n}% (min: ${e.minCoverage}%) — ${r}`), t.join("\n");
242
242
  }
243
- function ue(e, t, n) {
243
+ function de(e, t, n) {
244
244
  let r = [], i = n === "json" ? ".json" : ".po";
245
245
  for (let n of e.results) if (n.coverage < e.minCoverage) {
246
246
  let a = `${t}/${n.locale}${i}`;
@@ -253,7 +253,7 @@ function ue(e, t, n) {
253
253
  }
254
254
  return r.join("\n");
255
255
  }
256
- function de(e) {
256
+ function fe(e) {
257
257
  return JSON.stringify({
258
258
  results: e.results,
259
259
  passed: e.passed,
@@ -263,63 +263,63 @@ function de(e) {
263
263
  }
264
264
  //#endregion
265
265
  //#region src/compile-cache.ts
266
- var z = "1", fe = class {
266
+ var V = "1", pe = class {
267
267
  data;
268
268
  cachePath;
269
269
  dirty = !1;
270
270
  constructor(e, t) {
271
- this.cachePath = w(t ? w(e, ".cache", t) : w(e, ".cache"), "compile-cache.json"), this.data = this.load();
271
+ this.cachePath = D(t ? D(e, ".cache", t) : D(e, ".cache"), "compile-cache.json"), this.data = this.load();
272
272
  }
273
273
  isUpToDate(e, t) {
274
274
  let n = this.data.entries[e];
275
275
  if (!n) return !1;
276
- let r = B(t);
276
+ let r = H(t);
277
277
  return n.inputHash === r;
278
278
  }
279
279
  set(e, t) {
280
- this.data.entries[e] = { inputHash: B(t) }, this.dirty = !0;
280
+ this.data.entries[e] = { inputHash: H(t) }, this.dirty = !0;
281
281
  }
282
282
  save() {
283
283
  if (this.dirty) try {
284
- _(x(this.cachePath), { recursive: !0 }), b(this.cachePath, JSON.stringify(this.data), "utf-8"), this.dirty = !1;
284
+ b(w(this.cachePath), { recursive: !0 }), C(this.cachePath, JSON.stringify(this.data), "utf-8"), this.dirty = !1;
285
285
  } catch {}
286
286
  }
287
287
  load() {
288
288
  try {
289
- if (g(this.cachePath)) {
290
- let e = v(this.cachePath, "utf-8"), t = JSON.parse(e);
291
- if (t.version === z) return t;
289
+ if (y(this.cachePath)) {
290
+ let e = x(this.cachePath, "utf-8"), t = JSON.parse(e);
291
+ if (t.version === V) return t;
292
292
  }
293
293
  } catch {}
294
294
  return {
295
- version: z,
295
+ version: V,
296
296
  entries: {}
297
297
  };
298
298
  }
299
299
  };
300
- function B(e) {
301
- return D("md5").update(e).digest("hex");
300
+ function H(e) {
301
+ return A("md5").update(e).digest("hex");
302
302
  }
303
303
  //#endregion
304
304
  //#region src/ai-provider.ts
305
- var V = M(j), pe = {
305
+ var me = P(N), he = {
306
306
  claude: "npm install -g @anthropic-ai/claude-code",
307
307
  codex: "npm install -g @openai/codex"
308
308
  };
309
- function me(e, t) {
309
+ function ge(e, t) {
310
310
  return e === "claude" ? ["-p", t] : [
311
311
  "-p",
312
312
  t,
313
313
  "--full-auto"
314
314
  ];
315
315
  }
316
- function he(e) {
316
+ function _e(e) {
317
317
  return e.code === "ENOENT";
318
318
  }
319
- async function ge(e) {
320
- let { provider: t, prompt: n, maxRetries: r = 3, initialDelayMs: i = 1e3, maxBuffer: a = 10 * 1024 * 1024, timeoutMs: o = 12e4 } = e, s = me(t, n), c = t === "claude" ? "claude" : "codex", l;
319
+ async function ve(e) {
320
+ let { provider: t, prompt: n, maxRetries: r = 3, initialDelayMs: i = 1e3, maxBuffer: a = 10 * 1024 * 1024, timeoutMs: o = 12e4 } = e, s = ge(t, n), c = t === "claude" ? "claude" : "codex", l;
321
321
  for (let e = 0; e <= r; e++) try {
322
- let t = V(c, [...s], { maxBuffer: a }), { stdout: n } = await Promise.race([t, N(o).then(() => {
322
+ let t = me(c, [...s], { maxBuffer: a }), { stdout: n } = await Promise.race([t, F(o).then(() => {
323
323
  throw Error(`AI provider timed out after ${o}ms`);
324
324
  })]);
325
325
  return {
@@ -327,22 +327,22 @@ async function ge(e) {
327
327
  attempts: e + 1
328
328
  };
329
329
  } catch (n) {
330
- if (he(n)) throw Error(`"${t}" CLI not found. Please install it first:\n ${pe[t]}`);
330
+ if (_e(n)) throw Error(`"${t}" CLI not found. Please install it first:\n ${he[t]}`);
331
331
  if (n instanceof Error && n.message.includes("timed out")) throw n;
332
- l = n, e < r && await N(i * 2 ** e);
332
+ l = n, e < r && await F(i * 2 ** e);
333
333
  }
334
334
  throw l;
335
335
  }
336
336
  //#endregion
337
337
  //#region src/glossary.ts
338
- var H = 1048576;
339
- function _e(e) {
340
- if (!g(e)) return {};
341
- let t = y(e).size;
342
- if (t > H) throw Error(`Glossary file exceeds maximum size of ${H} bytes (got ${t} bytes)`);
338
+ var U = 1048576;
339
+ function ye(e) {
340
+ if (!y(e)) return {};
341
+ let t = S(e).size;
342
+ if (t > U) throw Error(`Glossary file exceeds maximum size of ${U} bytes (got ${t} bytes)`);
343
343
  let n;
344
344
  try {
345
- let t = v(e, "utf-8");
345
+ let t = x(e, "utf-8");
346
346
  n = JSON.parse(t);
347
347
  } catch (e) {
348
348
  let t = e instanceof Error ? e.message : String(e);
@@ -355,21 +355,21 @@ function _e(e) {
355
355
  }
356
356
  return n;
357
357
  }
358
- function ve(e, t) {
358
+ function be(e, t) {
359
359
  let n = {};
360
360
  for (let [r, i] of Object.entries(e)) t in i && (n[r] = i[t]);
361
361
  return n;
362
362
  }
363
- function U(e) {
363
+ function W(e) {
364
364
  return e.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/[\n\r]/g, " ").replace(/\t/g, " ");
365
365
  }
366
- function ye(e) {
366
+ function xe(e) {
367
367
  let t = Object.entries(e);
368
- return t.length === 0 ? "" : `=== GLOSSARY (use these exact translations) ===\n${[...t].sort(([e], [t]) => e.localeCompare(t)).map(([e, t]) => `"${U(e)}" → "${U(t)}"`).join("\n")}`;
368
+ return t.length === 0 ? "" : `=== GLOSSARY (use these exact translations) ===\n${[...t].sort(([e], [t]) => e.localeCompare(t)).map(([e, t]) => `"${W(e)}" → "${W(t)}"`).join("\n")}`;
369
369
  }
370
370
  //#endregion
371
371
  //#region src/translate-prompt.ts
372
- function be(e) {
372
+ function Se(e) {
373
373
  let { sourceLocale: t, targetLocale: n, messages: r, glossary: i, context: a } = e, o = JSON.stringify(r, null, 2), s = [
374
374
  `You are a professional translator. Translate the following messages from "${t}" to "${n}".`,
375
375
  "",
@@ -381,14 +381,14 @@ function be(e) {
381
381
  "5. Do not add any explanation, markdown formatting, or code fences — output raw JSON only."
382
382
  ];
383
383
  if (i && Object.keys(i).length > 0) {
384
- let e = ye(i);
384
+ let e = xe(i);
385
385
  e && s.push("", e);
386
386
  }
387
387
  return a && s.push("", "=== PROJECT CONTEXT ===", a), s.push("", "Input (JSON):", o), s.join("\n");
388
388
  }
389
389
  //#endregion
390
390
  //#region src/translate-parse.ts
391
- function xe(e) {
391
+ function Ce(e) {
392
392
  let t = e.indexOf("{");
393
393
  if (t === -1) throw Error("No JSON object found in AI response");
394
394
  let n = 0, r = !1, i = !1;
@@ -410,12 +410,12 @@ function xe(e) {
410
410
  }
411
411
  throw Error("Unterminated JSON object in AI response");
412
412
  }
413
- function Se(e) {
413
+ function we(e) {
414
414
  let t = e.match(/```(?:json)?\s*\n?([\s\S]*?)```/);
415
415
  return t ? t[1] : e;
416
416
  }
417
- function Ce(e, t) {
418
- let n = [], r = xe(Se(e)), i;
417
+ function Te(e, t) {
418
+ let n = [], r = Ce(we(e)), i;
419
419
  try {
420
420
  i = JSON.parse(r);
421
421
  } catch {
@@ -433,7 +433,7 @@ function Ce(e, t) {
433
433
  continue;
434
434
  }
435
435
  o[e] = r;
436
- let i = t[e], a = ae(i, r);
436
+ let i = t[e], a = oe(i, r);
437
437
  if (!a.valid) {
438
438
  let t = [];
439
439
  a.missingPlaceholders.length > 0 && t.push(`missing placeholders: ${a.missingPlaceholders.join(", ")}`), a.extraPlaceholders.length > 0 && t.push(`extra placeholders: ${a.extraPlaceholders.join(", ")}`), a.missingHtmlTags.length > 0 && t.push(`missing HTML tags: ${a.missingHtmlTags.join(", ")}`), a.extraHtmlTags.length > 0 && t.push(`extra HTML tags: ${a.extraHtmlTags.join(", ")}`), a.syntaxErrors.length > 0 && t.push(`ICU syntax errors: ${a.syntaxErrors.join("; ")}`), n.push(`QA issue for "${e}": ${t.join("; ")}`);
@@ -447,12 +447,12 @@ function Ce(e, t) {
447
447
  }
448
448
  //#endregion
449
449
  //#region src/translate.ts
450
- function we(e) {
450
+ function Ee(e) {
451
451
  let t = {};
452
452
  for (let [n, r] of Object.entries(e)) r.obsolete || (!r.translation || r.translation.length === 0) && (t[n] = r.message ?? n);
453
453
  return t;
454
454
  }
455
- function Te(e, t) {
455
+ function G(e, t) {
456
456
  let n = Object.keys(e), r = [];
457
457
  for (let i = 0; i < n.length; i += t) {
458
458
  let a = {};
@@ -461,22 +461,22 @@ function Te(e, t) {
461
461
  }
462
462
  return r;
463
463
  }
464
- async function Ee(e) {
465
- let { provider: t, sourceLocale: n, targetLocale: r, catalog: i, batchSize: a, context: o, glossary: s, timeoutMs: c } = e, l = we(i), u = Object.keys(l).length;
464
+ async function De(e) {
465
+ let { provider: t, sourceLocale: n, targetLocale: r, catalog: i, batchSize: a, context: o, glossary: s, timeoutMs: c } = e, l = Ee(i), u = Object.keys(l).length;
466
466
  if (u === 0) return {
467
467
  catalog: { ...i },
468
468
  translated: 0,
469
469
  warnings: []
470
470
  };
471
- A.info(` ${u} untranslated messages, translating with ${t}...`);
472
- let d = { ...i }, f = Te(l, a), p = 0, m = [];
471
+ j.info(` ${u} untranslated messages, translating with ${t}...`);
472
+ let d = { ...i }, f = G(l, a), p = 0, m = [];
473
473
  for (let e = 0; e < f.length; e++) {
474
474
  let i = f[e], a = Object.keys(i);
475
- f.length > 1 && A.info(` Batch ${e + 1}/${f.length} (${a.length} messages)`);
475
+ f.length > 1 && j.info(` Batch ${e + 1}/${f.length} (${a.length} messages)`);
476
476
  try {
477
- let { stdout: e } = await ge({
477
+ let { stdout: e } = await ve({
478
478
  provider: t,
479
- prompt: be({
479
+ prompt: Se({
480
480
  sourceLocale: n,
481
481
  targetLocale: r,
482
482
  messages: i,
@@ -484,15 +484,15 @@ async function Ee(e) {
484
484
  context: o
485
485
  }),
486
486
  timeoutMs: c
487
- }), { translations: l, warnings: u } = Ce(e, i);
488
- for (let e of u) m.push(`[${r}] ${e}`), A.warn(` ${e}`);
487
+ }), { translations: l, warnings: u } = Te(e, i);
488
+ for (let e of u) m.push(`[${r}] ${e}`), j.warn(` ${e}`);
489
489
  for (let e of a) e in l && (d[e] = {
490
490
  ...d[e],
491
491
  translation: l[e]
492
492
  }, p++);
493
493
  } catch (t) {
494
494
  let n = t instanceof Error ? t.message : String(t);
495
- m.push(`[${r}] Batch ${e + 1} failed: ${n}`), A.error(` Batch ${e + 1} failed: ${n}`);
495
+ m.push(`[${r}] Batch ${e + 1} failed: ${n}`), j.error(` Batch ${e + 1} failed: ${n}`);
496
496
  }
497
497
  }
498
498
  return {
@@ -503,7 +503,7 @@ async function Ee(e) {
503
503
  }
504
504
  //#endregion
505
505
  //#region src/migrate.ts
506
- var W = M(j), G = {
506
+ var K = P(N), q = {
507
507
  "vue-i18n": {
508
508
  name: "vue-i18n",
509
509
  framework: "Vue",
@@ -643,37 +643,37 @@ var W = M(j), G = {
643
643
  ],
644
644
  migrationGuide: "react/llms-migration.txt"
645
645
  }
646
- }, K = Object.keys(G);
647
- function De(e) {
646
+ }, J = Object.keys(q);
647
+ function Oe(e) {
648
648
  let t = e.toLowerCase().replace(/^@nuxtjs\//, "nuxt-").replace(/^@/, "");
649
- return K.find((e) => e === t);
649
+ return J.find((e) => e === t);
650
650
  }
651
- async function Oe(e) {
651
+ async function ke(e) {
652
652
  let t = {
653
653
  configFiles: [],
654
654
  localeFiles: [],
655
655
  sampleSources: [],
656
656
  packageJson: void 0
657
- }, n = w("package.json");
658
- g(n) && (t.packageJson = v(n, "utf-8"));
657
+ }, n = D("package.json");
658
+ y(n) && (t.packageJson = x(n, "utf-8"));
659
659
  for (let n of e.configPatterns) {
660
- let e = w(n);
661
- g(e) && t.configFiles.push({
660
+ let e = D(n);
661
+ y(e) && t.configFiles.push({
662
662
  path: n,
663
- content: v(e, "utf-8")
663
+ content: x(e, "utf-8")
664
664
  });
665
665
  }
666
- let r = await E(e.localePatterns, { absolute: !1 });
666
+ let r = await k(e.localePatterns, { absolute: !1 });
667
667
  for (let e of r.slice(0, 10)) {
668
- let n = v(w(e), "utf-8");
668
+ let n = x(D(e), "utf-8");
669
669
  t.localeFiles.push({
670
670
  path: e,
671
671
  content: n.length > 5e3 ? n.slice(0, 5e3) + "\n... (truncated)" : n
672
672
  });
673
673
  }
674
- let i = await E(e.sourcePatterns, { absolute: !1 });
674
+ let i = await k(e.sourcePatterns, { absolute: !1 });
675
675
  for (let e of i.slice(0, 5)) {
676
- let n = v(w(e), "utf-8");
676
+ let n = x(D(e), "utf-8");
677
677
  t.sampleSources.push({
678
678
  path: e,
679
679
  content: n.length > 3e3 ? n.slice(0, 3e3) + "\n... (truncated)" : n
@@ -681,16 +681,16 @@ async function Oe(e) {
681
681
  }
682
682
  return t;
683
683
  }
684
- function ke(e) {
685
- let t = typeof __dirname < "u" ? __dirname : x(T(import.meta.url)), n = [
686
- w("node_modules", "@fluenti", "cli", "..", "..", e),
687
- C(t, "..", "..", "..", e),
688
- C(t, "..", "..", e)
684
+ function Ae(e) {
685
+ let t = typeof __dirname < "u" ? __dirname : w(O(import.meta.url)), n = [
686
+ D("node_modules", "@fluenti", "cli", "..", "..", e),
687
+ E(t, "..", "..", "..", e),
688
+ E(t, "..", "..", e)
689
689
  ];
690
- for (let e of n) if (g(e)) return v(e, "utf-8");
690
+ for (let e of n) if (y(e)) return x(e, "utf-8");
691
691
  return "";
692
692
  }
693
- function Ae(e, t, n) {
693
+ function je(e, t, n) {
694
694
  let r = [];
695
695
  if (r.push(`You are a migration tool converting a ${e.framework} project from "${e.name}" to Fluenti (@fluenti).`, "", "Tasks:", "1. Generate a `fluenti.config.ts` based on the existing i18n configuration", "2. Convert each locale/translation file to standard gettext PO format", "3. Generate unified diff patches for every source file that needs changes", "4. Generate install/uninstall commands", "", "=== TRANSLATION API RULES ===", "Fluenti provides a compile-time `t` tagged template that does NOT require useI18n():", "", " import { t } from '@fluenti/react' // or @fluenti/vue, @fluenti/solid", " const name = \"World\"", " t`Hello, ${name}!`", "", "Use `import { t }` for ALL translation calls. Only use `useI18n()` when you need:", "- `d()` / `n()` for date/number formatting", "- `setLocale()` for locale switching", "- `locale` for reading the current locale reactively", "", "=== SOURCE CODE REWRITING RULES ===", "Imports:", `- ${e.name}: remove all imports from "${e.name}" (and related packages)`, `- Add: import { t } from '@fluenti/${e.framework === "Vue" ? "vue" : e.framework === "Next.js" ? "react" : e.framework.toLowerCase()}'`, "- Only add useI18n import if d()/n()/setLocale is needed in that file", "", "Translation calls:", "- t('key') → t`Source text` (tagged template with the actual source text, not the key)", "- t('key', { name }) → t`Hello, ${name}` (interpolate directly in template)", "- $t('key') → t`Source text` (Vue template)", "- Remove useI18n()/useTranslation()/useTranslations() destructuring if only t was used", "", "Components:", "- <i18n-t keypath=\"key\"> → <Trans>Source text</Trans>", "- <Trans i18nKey=\"key\"> → <Trans>Source text</Trans>", "", "ICU syntax conversion:", "- {{variable}} (double braces) → {variable} (single braces)", "- _one/_other suffixes → ICU {count, plural, one {...} other {...}}", "- @:key references → inline the referenced text directly", "- Pipe-separated plurals → ICU plural", "", "=== PO FORMAT RULES ===", "Each PO file must have a standard header:", " msgid \"\"", " msgstr \"\"", " \"Content-Type: text/plain; charset=UTF-8\\n\"", " \"Content-Transfer-Encoding: 8bit\\n\"", " \"Language: {locale}\\n\"", "", "Message entries: msgid is the source text (English), msgstr is the translation.", "Flatten nested JSON keys: \"home.title\" → use the actual source text as msgid.", ""), n && r.push("=== MIGRATION GUIDE ===", n, ""), t.packageJson && r.push("=== package.json ===", t.packageJson, ""), t.configFiles.length > 0) {
696
696
  r.push("=== EXISTING CONFIG FILES ===");
@@ -706,14 +706,14 @@ function Ae(e, t, n) {
706
706
  }
707
707
  return r.push("", "=== OUTPUT FORMAT ===", "Output ONLY the following sections. No explanations, no commentary.", "", "### FLUENTI_CONFIG", "```ts", "// Complete fluenti.config.ts", "```", "", "### LOCALE_FILES", "#### LOCALE: {locale_code}", "```po", "// Complete PO file with standard header", "```", "(repeat for each locale)", "", "### SOURCE_PATCHES", "#### FILE: {relative_file_path}", "```diff", "--- a/{file_path}", "+++ b/{file_path}", "@@ ... @@", " context line", "-removed line", "+added line", "```", "(repeat for each file that needs changes)", "", "### INSTALL_COMMANDS", "```bash", "// install + uninstall commands", "```"), r.join("\n");
708
708
  }
709
- async function je(e, t) {
709
+ async function Me(e, t) {
710
710
  let n = 10 * 1024 * 1024;
711
711
  try {
712
712
  if (e === "claude") {
713
- let { stdout: e } = await W("claude", ["-p", t], { maxBuffer: n });
713
+ let { stdout: e } = await K("claude", ["-p", t], { maxBuffer: n });
714
714
  return e;
715
715
  } else {
716
- let { stdout: e } = await W("codex", [
716
+ let { stdout: e } = await K("codex", [
717
717
  "-p",
718
718
  t,
719
719
  "--full-auto"
@@ -725,7 +725,7 @@ async function je(e, t) {
725
725
  throw n.code === "ENOENT" || n.code === "EPERM" || n.code === "EACCES" ? Error(`"${e}" CLI not found or not executable. Please install it first:\n` + (e === "claude" ? " npm install -g @anthropic-ai/claude-code" : " npm install -g @openai/codex")) : t;
726
726
  }
727
727
  }
728
- function Me(e) {
728
+ function Ne(e) {
729
729
  let t = 5e5, n = e.length > t ? e.slice(0, t) : e, r = {
730
730
  config: void 0,
731
731
  localeFiles: [],
@@ -755,212 +755,83 @@ function Me(e) {
755
755
  let c = n.match(/### INSTALL_COMMANDS[\s\S]*?```(?:bash|sh)?\n([\s\S]*?)```/);
756
756
  return c && (r.installCommands = c[1].trim()), r;
757
757
  }
758
- async function Ne(e) {
759
- let { from: t, provider: n, write: r } = e, i = De(t);
758
+ async function Pe(e) {
759
+ let { from: t, provider: n, write: r } = e, i = Oe(t);
760
760
  if (!i) {
761
- A.error(`Unsupported library "${t}". Supported libraries:`);
762
- for (let e of K) A.log(` - ${e}`);
761
+ j.error(`Unsupported library "${t}". Supported libraries:`);
762
+ for (let e of J) j.log(` - ${e}`);
763
763
  return;
764
764
  }
765
- let a = G[i];
766
- A.info(`Migrating from ${a.name} (${a.framework}) to Fluenti`), A.info("Scanning project for existing i18n files...");
767
- let o = await Oe(a);
765
+ let a = q[i];
766
+ j.info(`Migrating from ${a.name} (${a.framework}) to Fluenti`), j.info("Scanning project for existing i18n files...");
767
+ let o = await ke(a);
768
768
  if (o.configFiles.length === 0 && o.localeFiles.length === 0) {
769
- A.warn(`No ${a.name} configuration or locale files found.`), A.info("Make sure you are running this command from the project root directory.");
769
+ j.warn(`No ${a.name} configuration or locale files found.`), j.info("Make sure you are running this command from the project root directory.");
770
770
  return;
771
771
  }
772
- A.info(`Found: ${o.configFiles.length} config file(s), ${o.localeFiles.length} locale file(s), ${o.sampleSources.length} source file(s)`);
773
- let s = ke(a.migrationGuide);
774
- A.info(`Generating migration plan with ${n}...`);
775
- let c = Me(await je(n, Ae(a, o, s)));
776
- if (c.installCommands && (A.log(""), A.box({
772
+ j.info(`Found: ${o.configFiles.length} config file(s), ${o.localeFiles.length} locale file(s), ${o.sampleSources.length} source file(s)`);
773
+ let s = Ae(a.migrationGuide);
774
+ j.info(`Generating migration plan with ${n}...`);
775
+ let c = Ne(await Me(n, je(a, o, s)));
776
+ if (c.installCommands && (j.log(""), j.box({
777
777
  title: "Install Commands",
778
778
  message: c.installCommands
779
779
  })), c.config) if (r) {
780
- let { writeFileSync: e } = await import("node:fs"), t = w("fluenti.config.ts");
781
- e(t, c.config, "utf-8"), A.success(`Written: ${t}`);
782
- } else A.log(""), A.box({
780
+ let { writeFileSync: e } = await import("node:fs"), t = D("fluenti.config.ts");
781
+ e(t, c.config, "utf-8"), j.success(`Written: ${t}`);
782
+ } else j.log(""), j.box({
783
783
  title: "fluenti.config.ts",
784
784
  message: c.config
785
785
  });
786
786
  if (c.localeFiles.length > 0) if (r) {
787
787
  let { writeFileSync: e, mkdirSync: t } = await import("node:fs"), n = "./locales";
788
- t(w(n), { recursive: !0 });
788
+ t(D(n), { recursive: !0 });
789
789
  for (let t of c.localeFiles) {
790
- let r = w(n, `${t.locale}.po`);
791
- e(r, t.content, "utf-8"), A.success(`Written: ${r}`);
790
+ let r = D(n, `${t.locale}.po`);
791
+ e(r, t.content, "utf-8"), j.success(`Written: ${r}`);
792
792
  }
793
- } else for (let e of c.localeFiles) A.log(""), A.box({
793
+ } else for (let e of c.localeFiles) j.log(""), j.box({
794
794
  title: `locales/${e.locale}.po`,
795
795
  message: e.content.length > 500 ? e.content.slice(0, 500) + "\n... (use --write to save full file)" : e.content
796
796
  });
797
797
  if (c.sourcePatches.length > 0) if (r) {
798
- A.log(""), A.info(`Generated ${c.sourcePatches.length} source patch(es). Apply with:`);
798
+ j.log(""), j.info(`Generated ${c.sourcePatches.length} source patch(es). Apply with:`);
799
799
  for (let e of c.sourcePatches) {
800
- let t = w(`.fluenti-migrate-${e.file.replace(/[/\\]/g, "-")}.patch`), { writeFileSync: n } = await import("node:fs");
801
- n(t, e.patch, "utf-8"), A.success(`Patch written: ${t}`), A.log(` patch -p1 < ${t}`);
800
+ let t = D(`.fluenti-migrate-${e.file.replace(/[/\\]/g, "-")}.patch`), { writeFileSync: n } = await import("node:fs");
801
+ n(t, e.patch, "utf-8"), j.success(`Patch written: ${t}`), j.log(` patch -p1 < ${t}`);
802
802
  }
803
- } else for (let e of c.sourcePatches) A.log(""), A.box({
803
+ } else for (let e of c.sourcePatches) j.log(""), j.box({
804
804
  title: `Patch: ${e.file}`,
805
805
  message: e.patch.length > 800 ? e.patch.slice(0, 800) + "\n... (use --write to save full patch)" : e.patch
806
806
  });
807
- c.steps && c.sourcePatches.length === 0 && (A.log(""), A.box({
807
+ c.steps && c.sourcePatches.length === 0 && (j.log(""), j.box({
808
808
  title: "Migration Steps",
809
809
  message: c.steps
810
- })), !r && (c.config || c.localeFiles.length > 0 || c.sourcePatches.length > 0) && (A.log(""), A.info("Run with --write to save generated files and patches to disk:"), A.log(` fluenti migrate --from ${t} --write`));
811
- }
812
- //#endregion
813
- //#region src/init.ts
814
- var Pe = /^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{1,8})*$/;
815
- function q(e) {
816
- if (!Pe.test(e)) throw Error(`Invalid locale format: "${e}"`);
817
- return e;
818
- }
819
- var Fe = [
820
- {
821
- dep: "next",
822
- name: "nextjs",
823
- pluginPackage: "@fluenti/next"
824
- },
825
- {
826
- dep: "nuxt",
827
- name: "nuxt",
828
- pluginPackage: "@fluenti/vue"
829
- },
830
- {
831
- dep: "@solidjs/start",
832
- name: "solidstart",
833
- pluginPackage: "@fluenti/solid"
834
- },
835
- {
836
- dep: "vue",
837
- name: "vue",
838
- pluginPackage: "@fluenti/vue"
839
- },
840
- {
841
- dep: "solid-js",
842
- name: "solid",
843
- pluginPackage: "@fluenti/solid"
844
- },
845
- {
846
- dep: "react",
847
- name: "react",
848
- pluginPackage: "@fluenti/react"
849
- }
850
- ];
851
- function Ie(e) {
852
- for (let t of Fe) if (t.dep in e) return {
853
- name: t.name,
854
- pluginPackage: t.pluginPackage
855
- };
856
- return {
857
- name: "unknown",
858
- pluginPackage: null
859
- };
860
- }
861
- function Le(e) {
862
- let t = e.locales.map((e) => JSON.stringify(e)).join(", ");
863
- return `import { defineConfig } from '@fluenti/cli'
864
-
865
- export default defineConfig({
866
- sourceLocale: ${JSON.stringify(e.sourceLocale)},
867
- locales: [${t}],
868
- catalogDir: './locales',
869
- format: ${JSON.stringify(e.format)},
870
- include: ['./src/**/*.{vue,tsx,jsx,ts,js}'],
871
- compileOutDir: './src/locales/compiled',
872
- })
873
- `;
874
- }
875
- async function Re(e) {
876
- let t = w(e.cwd, "package.json");
877
- if (!g(t)) {
878
- A.error("No package.json found in current directory.");
879
- return;
880
- }
881
- let n = JSON.parse(v(t, "utf-8")), r = Ie({
882
- ...n.dependencies,
883
- ...n.devDependencies
884
- });
885
- A.info(`Detected framework: ${r.name}`), r.pluginPackage && A.info(`Recommended plugin: ${r.pluginPackage}`);
886
- let i = w(e.cwd, "fluenti.config.ts");
887
- if (g(i)) {
888
- A.warn("fluenti.config.ts already exists. Skipping config generation.");
889
- return;
890
- }
891
- let a = await A.prompt("Source locale?", {
892
- type: "text",
893
- default: "en",
894
- placeholder: "en"
895
- });
896
- if (typeof a == "symbol") return;
897
- let o = await A.prompt("Target locales (comma-separated)?", {
898
- type: "text",
899
- default: "ja,zh-CN",
900
- placeholder: "ja,zh-CN"
901
- });
902
- if (typeof o == "symbol") return;
903
- let s = await A.prompt("Catalog format?", {
904
- type: "select",
905
- options: ["po", "json"],
906
- initial: "po"
907
- });
908
- if (typeof s == "symbol") return;
909
- let c = o.split(",").map((e) => e.trim()).filter(Boolean);
910
- q(a);
911
- for (let e of c) q(e);
912
- b(i, Le({
913
- sourceLocale: a,
914
- locales: [a, ...c.filter((e) => e !== a)],
915
- format: s
916
- }), "utf-8"), A.success("Created fluenti.config.ts");
917
- let l = w(e.cwd, ".gitignore"), u = "src/locales/compiled/";
918
- g(l) ? v(l, "utf-8").includes(u) || (h(l, `\n# Fluenti compiled catalogs\n${u}\n`), A.success("Updated .gitignore")) : (b(l, `# Fluenti compiled catalogs\n${u}\n`), A.success("Created .gitignore"));
919
- let d = n.scripts ?? {}, f = {}, p = !1;
920
- if (d["i18n:extract"] || (f["i18n:extract"] = "fluenti extract", p = !0), d["i18n:compile"] || (f["i18n:compile"] = "fluenti compile", p = !0), p) {
921
- let e = {
922
- ...n,
923
- scripts: {
924
- ...d,
925
- ...f
926
- }
927
- };
928
- b(t, JSON.stringify(e, null, 2) + "\n", "utf-8"), A.success("Added i18n:extract and i18n:compile scripts to package.json");
929
- }
930
- A.log(""), A.box({
931
- title: "Next steps",
932
- message: [
933
- r.pluginPackage ? `1. Install: pnpm add -D ${r.pluginPackage} @fluenti/cli` : "1. Install: pnpm add -D @fluenti/cli",
934
- r.name === "nextjs" ? "2. Add withFluenti() to your next.config.ts" : r.name === "unknown" ? "2. Configure your build tool with the framework Vite plugin or @fluenti/next" : "2. Add the Vite plugin to your vite.config.ts (e.g. fluentiVue() from @fluenti/vue/vite-plugin)",
935
- "3. Run: npx fluenti extract",
936
- "4. Translate your messages",
937
- "5. Run: npx fluenti compile"
938
- ].join("\n")
939
- });
810
+ })), !r && (c.config || c.localeFiles.length > 0 || c.sourcePatches.length > 0) && (j.log(""), j.info("Run with --write to save generated files and patches to disk:"), j.log(` fluenti migrate --from ${t} --write`));
940
811
  }
941
812
  //#endregion
942
813
  //#region src/cli.ts
943
- function J(e) {
944
- return D("md5").update(e).digest("hex").slice(0, 8);
814
+ function Y(e) {
815
+ return A("md5").update(e).digest("hex").slice(0, 8);
945
816
  }
946
- function Y(e, t) {
947
- if (!g(e)) return {};
948
- let n = v(e, "utf-8");
949
- return t === "json" ? l(n) : s(n);
817
+ function X(e, t) {
818
+ if (!y(e)) return {};
819
+ let n = x(e, "utf-8");
820
+ return t === "json" ? s(n) : l(n);
950
821
  }
951
- function X(e, t, n) {
952
- _(x(e), { recursive: !0 }), b(e, n === "json" ? d(t) : a(t), "utf-8");
822
+ function Z(e, t, n) {
823
+ b(w(e), { recursive: !0 }), C(e, n === "json" ? c(t) : g(t), "utf-8");
953
824
  }
954
- async function ze(e, t, n) {
955
- if (S(e) === ".vue") try {
825
+ async function Fe(e, t, n) {
826
+ if (T(e) === ".vue") try {
956
827
  let { extractFromVue: r } = await import("./vue-extractor.js");
957
828
  return r(t, e, n);
958
829
  } catch {
959
- return A.warn(`Skipping ${e}: install @vue/compiler-sfc to extract from .vue files`), [];
830
+ return j.warn(`Skipping ${e}: install @vue/compiler-sfc to extract from .vue files`), [];
960
831
  }
961
832
  return i(t, e, n);
962
833
  }
963
- var Z = O({
834
+ var Ie = M({
964
835
  meta: {
965
836
  name: "extract",
966
837
  description: "Extract messages from source files"
@@ -987,12 +858,12 @@ var Z = O({
987
858
  }
988
859
  },
989
860
  async run({ args: e }) {
990
- let t = await u(e.config), n = m(t.locales);
991
- A.info(`Extracting messages from ${t.include.join(", ")}`);
992
- let r = await E(t.include, {
861
+ let t = await o(e.config), n = v(t.locales);
862
+ j.info(`Extracting messages from ${t.include.join(", ")}`);
863
+ let r = await k(t.include, {
993
864
  ignore: t.exclude ?? [],
994
865
  absolute: !1
995
- }), i = [], a = e["no-cache"] ?? !1 ? null : new f(t.catalogDir, J(process.cwd())), s = 0;
866
+ }), i = [], a = e["no-cache"] ?? !1 ? null : new d(t.catalogDir, Y(process.cwd())), s = 0;
996
867
  for (let e of r) {
997
868
  if (a) {
998
869
  let t = a.get(e);
@@ -1001,16 +872,16 @@ var Z = O({
1001
872
  continue;
1002
873
  }
1003
874
  }
1004
- let n = await ze(e, v(e, "utf-8"), t.idGenerator);
875
+ let n = await Fe(e, x(e, "utf-8"), t.idGenerator);
1005
876
  i.push(...n), a && a.set(e, n);
1006
877
  }
1007
- a && (a.prune(new Set(r)), a.save()), s > 0 ? A.info(`Found ${i.length} messages in ${r.length} files (${s} cached)`) : A.info(`Found ${i.length} messages in ${r.length} files`);
1008
- let c = t.format === "json" ? ".json" : ".po", l = e.clean ?? !1, d = e["no-fuzzy"] ?? !1;
878
+ a && (a.prune(new Set(r)), a.save()), s > 0 ? j.info(`Found ${i.length} messages in ${r.length} files (${s} cached)`) : j.info(`Found ${i.length} messages in ${r.length} files`);
879
+ let c = t.format === "json" ? ".json" : ".po", l = e.clean ?? !1, u = e["no-fuzzy"] ?? !1;
1009
880
  for (let e of n) {
1010
- let n = w(t.catalogDir, `${e}${c}`), { catalog: r, result: a } = o(Y(n, t.format), i, { stripFuzzy: d });
1011
- X(n, l ? Object.fromEntries(Object.entries(r).filter(([, e]) => !e.obsolete)) : r, t.format);
1012
- let s = l ? `${a.obsolete} removed` : `${a.obsolete} obsolete`;
1013
- A.success(`${e}: ${a.added} added, ${a.unchanged} unchanged, ${s}`);
881
+ let n = D(t.catalogDir, `${e}${c}`), { catalog: r, result: a } = f(X(n, t.format), i, { stripFuzzy: u });
882
+ Z(n, l ? Object.fromEntries(Object.entries(r).filter(([, e]) => !e.obsolete)) : r, t.format);
883
+ let o = l ? `${a.obsolete} removed` : `${a.obsolete} obsolete`;
884
+ j.success(`${e}: ${a.added} added, ${a.unchanged} unchanged, ${o}`);
1014
885
  }
1015
886
  for (let e of t.plugins ?? []) await e.onAfterExtract?.({
1016
887
  messages: new Map(i.map((e) => [e.id, e])),
@@ -1020,13 +891,13 @@ var Z = O({
1020
891
  });
1021
892
  }
1022
893
  });
1023
- function Be(e) {
894
+ function Le(e) {
1024
895
  let t = {};
1025
896
  for (let [n, r] of Object.entries(e)) r.translation && r.translation.length > 0 ? t[n] = r.translation : r.message && (t[n] = r.message);
1026
897
  return t;
1027
898
  }
1028
899
  async function Q(e, t, n) {
1029
- let r = Be(e);
900
+ let r = Le(e);
1030
901
  for (let e of n) e.transformMessages && (r = await e.transformMessages(r, t));
1031
902
  let i = {};
1032
903
  for (let [t, n] of Object.entries(e)) {
@@ -1048,7 +919,7 @@ function $(e, t, n, r) {
1048
919
  config: r
1049
920
  };
1050
921
  }
1051
- var Ve = O({
922
+ var Re = M({
1052
923
  meta: {
1053
924
  name: "compile",
1054
925
  description: "Compile message catalogs to JS modules"
@@ -1079,70 +950,70 @@ var Ve = O({
1079
950
  }
1080
951
  },
1081
952
  async run({ args: i }) {
1082
- let a = await u(i.config), o = m(a.locales), d = a.format === "json" ? ".json" : ".po";
1083
- _(a.compileOutDir, { recursive: !0 });
1084
- let f = {}, p = {};
1085
- for (let e of o) {
1086
- let t = w(a.catalogDir, `${e}${d}`);
1087
- if (g(t)) {
1088
- let n = v(t, "utf-8");
1089
- p[e] = n, f[e] = a.format === "json" ? l(n) : s(n);
1090
- } else p[e] = "", f[e] = {};
953
+ let a = await o(i.config), c = v(a.locales), u = a.format === "json" ? ".json" : ".po";
954
+ b(a.compileOutDir, { recursive: !0 });
955
+ let d = {}, f = {};
956
+ for (let e of c) {
957
+ let t = D(a.catalogDir, `${e}${u}`);
958
+ if (y(t)) {
959
+ let n = x(t, "utf-8");
960
+ f[e] = n, d[e] = a.format === "json" ? s(n) : l(n);
961
+ } else f[e] = "", d[e] = {};
1091
962
  }
1092
- let h = r(f);
1093
- A.info(`Compiling ${h.length} messages across ${o.length} locales`);
1094
- let y = i["skip-fuzzy"] ?? !1, x = i["no-cache"] ?? !1 ? null : new fe(a.catalogDir, J(process.cwd())), S = i.parallel ?? !1, C = i.concurrency ? parseInt(i.concurrency, 10) : void 0;
1095
- if (C !== void 0 && (isNaN(C) || C < 1)) {
1096
- A.error("Invalid --concurrency. Must be a positive integer."), process.exitCode = 1;
963
+ let p = r(d);
964
+ j.info(`Compiling ${p.length} messages across ${c.length} locales`);
965
+ let h = i["skip-fuzzy"] ?? !1, g = i["no-cache"] ?? !1 ? null : new pe(a.catalogDir, Y(process.cwd())), _ = i.parallel ?? !1, S = i.concurrency ? parseInt(i.concurrency, 10) : void 0;
966
+ if (S !== void 0 && (isNaN(S) || S < 1)) {
967
+ j.error("Invalid --concurrency. Must be a positive integer."), process.exitCode = 1;
1097
968
  return;
1098
969
  }
1099
- let T = 0, E = !1, D = [];
1100
- for (let e of o) {
1101
- if (x && x.isUpToDate(e, p[e]) && g(w(a.compileOutDir, `${e}.js`))) {
1102
- T++;
970
+ let w = 0, T = !1, E = [];
971
+ for (let e of c) {
972
+ if (g && g.isUpToDate(e, f[e]) && y(D(a.compileOutDir, `${e}.js`))) {
973
+ w++;
1103
974
  continue;
1104
975
  }
1105
- D.push(e);
976
+ E.push(e);
1106
977
  }
1107
- if (D.length > 0 && (E = !0), S && D.length > 1) {
978
+ if (E.length > 0 && (T = !0), _ && E.length > 1) {
1108
979
  let e = a.plugins ?? [], t = {};
1109
- for (let n of D) {
1110
- for (let t of e) await t.onBeforeCompile?.($(n, f[n], a.compileOutDir, a));
1111
- t[n] = e.length > 0 ? await Q(f[n], n, e) : f[n];
980
+ for (let n of E) {
981
+ for (let t of e) await t.onBeforeCompile?.($(n, d[n], a.compileOutDir, a));
982
+ t[n] = e.length > 0 ? await Q(d[n], n, e) : d[n];
1112
983
  }
1113
- let n = await c(D.map((e) => ({
984
+ let n = await m(E.map((e) => ({
1114
985
  locale: e,
1115
986
  catalog: t[e],
1116
- allIds: h,
987
+ allIds: p,
1117
988
  sourceLocale: a.sourceLocale,
1118
- options: { skipFuzzy: y }
1119
- })), C);
989
+ options: { skipFuzzy: h }
990
+ })), S);
1120
991
  for (let e of n) {
1121
- let t = w(a.compileOutDir, `${e.locale}.js`);
1122
- if (b(t, e.code, "utf-8"), x && x.set(e.locale, p[e.locale]), e.stats.missing.length > 0) {
1123
- A.warn(`${e.locale}: ${e.stats.compiled} compiled, ${e.stats.missing.length} missing translations`);
1124
- for (let t of e.stats.missing) A.warn(` ⤷ ${t}`);
1125
- } else A.success(`Compiled ${e.locale}: ${e.stats.compiled} messages → ${t}`);
992
+ let t = D(a.compileOutDir, `${e.locale}.js`);
993
+ if (C(t, e.code, "utf-8"), g && g.set(e.locale, f[e.locale]), e.stats.missing.length > 0) {
994
+ j.warn(`${e.locale}: ${e.stats.compiled} compiled, ${e.stats.missing.length} missing translations`);
995
+ for (let t of e.stats.missing) j.warn(` ⤷ ${t}`);
996
+ } else j.success(`Compiled ${e.locale}: ${e.stats.compiled} messages → ${t}`);
1126
997
  }
1127
- for (let n of D) for (let r of e) await r.onAfterCompile?.($(n, t[n], a.compileOutDir, a));
998
+ for (let n of E) for (let r of e) await r.onAfterCompile?.($(n, t[n], a.compileOutDir, a));
1128
999
  } else {
1129
1000
  let e = a.plugins ?? [];
1130
- for (let n of D) {
1131
- let r = w(a.compileOutDir, `${n}.js`);
1132
- for (let t of e) await t.onBeforeCompile?.($(n, f[n], a.compileOutDir, a));
1133
- let i = e.length > 0 ? await Q(f[n], n, e) : f[n], { code: o, stats: s } = t(i, n, h, a.sourceLocale, { skipFuzzy: y });
1134
- if (b(r, o, "utf-8"), x && x.set(n, p[n]), s.missing.length > 0) {
1135
- A.warn(`${n}: ${s.compiled} compiled, ${s.missing.length} missing translations`);
1136
- for (let e of s.missing) A.warn(` ⤷ ${e}`);
1137
- } else A.success(`Compiled ${n}: ${s.compiled} messages → ${r}`);
1001
+ for (let n of E) {
1002
+ let r = D(a.compileOutDir, `${n}.js`);
1003
+ for (let t of e) await t.onBeforeCompile?.($(n, d[n], a.compileOutDir, a));
1004
+ let i = e.length > 0 ? await Q(d[n], n, e) : d[n], { code: o, stats: s } = t(i, n, p, a.sourceLocale, { skipFuzzy: h });
1005
+ if (C(r, o, "utf-8"), g && g.set(n, f[n]), s.missing.length > 0) {
1006
+ j.warn(`${n}: ${s.compiled} compiled, ${s.missing.length} missing translations`);
1007
+ for (let e of s.missing) j.warn(` ⤷ ${e}`);
1008
+ } else j.success(`Compiled ${n}: ${s.compiled} messages → ${r}`);
1138
1009
  for (let t of e) await t.onAfterCompile?.($(n, i, a.compileOutDir, a));
1139
1010
  }
1140
1011
  }
1141
- T > 0 && A.info(`${T} locale(s) unchanged — skipped`), x && x.save();
1142
- let O = w(a.compileOutDir, "index.js"), k = w(a.compileOutDir, "messages.d.ts");
1143
- (E || !g(O)) && (b(O, n(o, a.compileOutDir), "utf-8"), A.success(`Generated index → ${O}`)), (E || !g(k)) && (b(k, e(h, f, a.sourceLocale), "utf-8"), A.success(`Generated types → ${k}`));
1012
+ w > 0 && j.info(`${w} locale(s) unchanged — skipped`), g && g.save();
1013
+ let O = D(a.compileOutDir, "index.js"), k = D(a.compileOutDir, "messages.d.ts");
1014
+ (T || !y(O)) && (C(O, n(c, a.compileOutDir), "utf-8"), j.success(`Generated index → ${O}`)), (T || !y(k)) && (C(k, e(p, d, a.sourceLocale), "utf-8"), j.success(`Generated types → ${k}`));
1144
1015
  }
1145
- }), He = O({
1016
+ }), ze = M({
1146
1017
  meta: {
1147
1018
  name: "stats",
1148
1019
  description: "Show translation progress"
@@ -1152,9 +1023,9 @@ var Ve = O({
1152
1023
  description: "Path to config file"
1153
1024
  } },
1154
1025
  async run({ args: e }) {
1155
- let t = await u(e.config), n = m(t.locales), r = t.format === "json" ? ".json" : ".po", i = [];
1026
+ let t = await o(e.config), n = v(t.locales), r = t.format === "json" ? ".json" : ".po", i = [];
1156
1027
  for (let e of n) {
1157
- let n = Y(w(t.catalogDir, `${e}${r}`), t.format), a = Object.values(n).filter((e) => !e.obsolete), o = a.length, s = a.filter((e) => e.translation && e.translation.length > 0).length, c = o > 0 ? (s / o * 100).toFixed(1) + "%" : "—";
1028
+ let n = X(D(t.catalogDir, `${e}${r}`), t.format), a = Object.values(n).filter((e) => !e.obsolete), o = a.length, s = a.filter((e) => e.translation && e.translation.length > 0).length, c = o > 0 ? (s / o * 100).toFixed(1) + "%" : "—";
1158
1029
  i.push({
1159
1030
  locale: e,
1160
1031
  total: o,
@@ -1162,11 +1033,11 @@ var Ve = O({
1162
1033
  pct: c
1163
1034
  });
1164
1035
  }
1165
- A.log(""), A.log(" Locale │ Total │ Translated │ Progress"), A.log(" ────────┼───────┼────────────┼─────────────────────────────");
1166
- for (let e of i) A.log(ie(e.locale, e.total, e.translated));
1167
- A.log("");
1036
+ j.log(""), j.log(" Locale │ Total │ Translated │ Progress"), j.log(" ────────┼───────┼────────────┼─────────────────────────────");
1037
+ for (let e of i) j.log(ae(e.locale, e.total, e.translated));
1038
+ j.log("");
1168
1039
  }
1169
- }), Ue = O({
1040
+ }), Be = M({
1170
1041
  meta: {
1171
1042
  name: "lint",
1172
1043
  description: "Check translation quality (missing, inconsistent placeholders, fuzzy)"
@@ -1187,21 +1058,21 @@ var Ve = O({
1187
1058
  }
1188
1059
  },
1189
1060
  async run({ args: e }) {
1190
- let t = await u(e.config), n = m(t.locales), r = t.format === "json" ? ".json" : ".po", i = {};
1191
- for (let e of n) i[e] = Y(w(t.catalogDir, `${e}${r}`), t.format);
1061
+ let t = await o(e.config), n = v(t.locales), r = t.format === "json" ? ".json" : ".po", i = {};
1062
+ for (let e of n) i[e] = X(D(t.catalogDir, `${e}${r}`), t.format);
1192
1063
  let a = e.locale ? [e.locale] : void 0;
1193
- A.info(`Linting ${a ? a.join(", ") : "all locales"} (source: ${t.sourceLocale})`);
1194
- let o = {
1064
+ j.info(`Linting ${a ? a.join(", ") : "all locales"} (source: ${t.sourceLocale})`);
1065
+ let s = {
1195
1066
  sourceLocale: t.sourceLocale,
1196
1067
  strict: e.strict ?? !1
1197
1068
  };
1198
- a && (o.locales = a);
1199
- let s = R(i, o);
1200
- A.log(""), A.log(oe(s)), A.log("");
1201
- let c = s.filter((e) => e.severity === "error"), l = s.filter((e) => e.severity === "warning");
1202
- (c.length > 0 || e.strict && l.length > 0) && (process.exitCode = 1);
1069
+ a && (s.locales = a);
1070
+ let c = B(i, s);
1071
+ j.log(""), j.log(se(c)), j.log("");
1072
+ let l = c.filter((e) => e.severity === "error"), u = c.filter((e) => e.severity === "warning");
1073
+ (l.length > 0 || e.strict && u.length > 0) && (process.exitCode = 1);
1203
1074
  }
1204
- }), We = O({
1075
+ }), Ve = M({
1205
1076
  meta: {
1206
1077
  name: "check",
1207
1078
  description: "Check translation coverage for CI"
@@ -1231,35 +1102,35 @@ var Ve = O({
1231
1102
  }
1232
1103
  },
1233
1104
  async run({ args: e }) {
1234
- let t = await u(e.config), n = m(t.locales), r = t.format === "json" ? ".json" : ".po", i = {};
1235
- for (let e of n) i[e] = Y(w(t.catalogDir, `${e}${r}`), t.format);
1105
+ let t = await o(e.config), n = v(t.locales), r = t.format === "json" ? ".json" : ".po", i = {};
1106
+ for (let e of n) i[e] = X(D(t.catalogDir, `${e}${r}`), t.format);
1236
1107
  let a = parseFloat(e["min-coverage"] ?? "100");
1237
1108
  if (isNaN(a) || a < 0 || a > 100) {
1238
- A.error("Invalid --min-coverage. Must be a number between 0 and 100."), process.exitCode = 1;
1109
+ j.error("Invalid --min-coverage. Must be a number between 0 and 100."), process.exitCode = 1;
1239
1110
  return;
1240
1111
  }
1241
- let o = e.format ?? (e.ci ? "github" : "text"), s = {
1112
+ let s = e.format ?? (e.ci ? "github" : "text"), c = {
1242
1113
  sourceLocale: t.sourceLocale,
1243
1114
  minCoverage: a,
1244
- format: o
1115
+ format: s
1245
1116
  };
1246
- e.locale && (s.locale = e.locale);
1247
- let c = ce(i, s);
1248
- switch (o) {
1117
+ e.locale && (c.locale = e.locale);
1118
+ let l = le(i, c);
1119
+ switch (s) {
1249
1120
  case "json":
1250
- A.log(de(c));
1121
+ j.log(fe(l));
1251
1122
  break;
1252
1123
  case "github":
1253
- A.log(ue(c, t.catalogDir, t.format));
1124
+ j.log(de(l, t.catalogDir, t.format));
1254
1125
  break;
1255
1126
  default:
1256
- A.log(""), A.log(le(c)), A.log("");
1127
+ j.log(""), j.log(ue(l)), j.log("");
1257
1128
  break;
1258
1129
  }
1259
- c.passed || (process.exitCode = 1);
1130
+ l.passed || (process.exitCode = 1);
1260
1131
  }
1261
1132
  });
1262
- async function Ge(e, t, n) {
1133
+ async function He(e, t, n) {
1263
1134
  let r = Array(e.length), i = 0, a = Array.from({ length: Math.min(n, e.length) }, async () => {
1264
1135
  for (; i < e.length;) {
1265
1136
  let n = i++;
@@ -1268,7 +1139,7 @@ async function Ge(e, t, n) {
1268
1139
  });
1269
1140
  return await Promise.all(a), r;
1270
1141
  }
1271
- var Ke = O({
1142
+ var Ue = M({
1272
1143
  meta: {
1273
1144
  name: "translate",
1274
1145
  description: "Translate messages using AI (Claude Code or Codex CLI)"
@@ -1315,58 +1186,58 @@ var Ke = O({
1315
1186
  }
1316
1187
  },
1317
1188
  async run({ args: e }) {
1318
- let t = await u(e.config), n = m(t.locales), r = e.provider;
1189
+ let t = await o(e.config), n = v(t.locales), r = e.provider;
1319
1190
  if (r !== "claude" && r !== "codex") {
1320
- A.error(`Invalid provider "${r}". Use "claude" or "codex".`);
1191
+ j.error(`Invalid provider "${r}". Use "claude" or "codex".`);
1321
1192
  return;
1322
1193
  }
1323
1194
  let i = parseInt(e["batch-size"] ?? "50", 10);
1324
1195
  if (isNaN(i) || i < 1) {
1325
- A.error("Invalid batch-size. Must be a positive integer.");
1196
+ j.error("Invalid batch-size. Must be a positive integer.");
1326
1197
  return;
1327
1198
  }
1328
- let a = e.glossary ? _e(w(e.glossary)) : void 0, o = e.concurrency ? parseInt(e.concurrency, 10) : 3;
1329
- if (isNaN(o) || o < 1) {
1330
- A.error("Invalid concurrency. Must be a positive integer.");
1199
+ let a = e.glossary ? ye(D(e.glossary)) : void 0, s = e.concurrency ? parseInt(e.concurrency, 10) : 3;
1200
+ if (isNaN(s) || s < 1) {
1201
+ j.error("Invalid concurrency. Must be a positive integer.");
1331
1202
  return;
1332
1203
  }
1333
- let s = e.timeout ? parseInt(e.timeout, 10) : 120;
1334
- if (isNaN(s) || s < 1) {
1335
- A.error("Invalid timeout. Must be a positive integer (seconds).");
1204
+ let c = e.timeout ? parseInt(e.timeout, 10) : 120;
1205
+ if (isNaN(c) || c < 1) {
1206
+ j.error("Invalid timeout. Must be a positive integer (seconds).");
1336
1207
  return;
1337
1208
  }
1338
- let c = s * 1e3, l = e.locale ? [e.locale] : n.filter((e) => e !== t.sourceLocale);
1339
- if (l.length === 0) {
1340
- A.warn("No target locales to translate.");
1209
+ let l = c * 1e3, u = e.locale ? [e.locale] : n.filter((e) => e !== t.sourceLocale);
1210
+ if (u.length === 0) {
1211
+ j.warn("No target locales to translate.");
1341
1212
  return;
1342
1213
  }
1343
- A.info(`Translating with ${r} (batch size: ${i})`);
1214
+ j.info(`Translating with ${r} (batch size: ${i})`);
1344
1215
  let d = t.format === "json" ? ".json" : ".po";
1345
- await Ge(l, async (n) => {
1346
- A.info(`\n[${n}]`);
1347
- let o = w(t.catalogDir, `${n}${d}`), s = Y(o, t.format);
1216
+ await He(u, async (n) => {
1217
+ j.info(`\n[${n}]`);
1218
+ let o = D(t.catalogDir, `${n}${d}`), s = X(o, t.format);
1348
1219
  if (e["dry-run"]) {
1349
1220
  let e = Object.entries(s).filter(([, e]) => !e.obsolete && (!e.translation || e.translation.length === 0));
1350
1221
  if (e.length > 0) {
1351
- for (let [t, n] of e) A.log(` ${t}: ${n.message ?? t}`);
1352
- A.success(` ${n}: ${e.length} messages would be translated (dry-run)`);
1353
- } else A.success(` ${n}: already fully translated`);
1222
+ for (let [t, n] of e) j.log(` ${t}: ${n.message ?? t}`);
1223
+ j.success(` ${n}: ${e.length} messages would be translated (dry-run)`);
1224
+ } else j.success(` ${n}: already fully translated`);
1354
1225
  return;
1355
1226
  }
1356
- let l = a ? ve(a, n) : void 0, { catalog: u, translated: f, warnings: p } = await Ee({
1227
+ let c = a ? be(a, n) : void 0, { catalog: u, translated: f, warnings: p } = await De({
1357
1228
  provider: r,
1358
1229
  sourceLocale: t.sourceLocale,
1359
1230
  targetLocale: n,
1360
1231
  catalog: s,
1361
1232
  batchSize: i,
1362
- glossary: l,
1363
- timeoutMs: c,
1233
+ glossary: c,
1234
+ timeoutMs: l,
1364
1235
  ...e.context ? { context: e.context } : {}
1365
1236
  });
1366
- f > 0 ? (X(o, u, t.format), A.success(` ${n}: ${f} messages translated`)) : A.success(` ${n}: already fully translated`), p.length > 0 && A.warn(` ${n}: ${p.length} QA warnings`);
1367
- }, o);
1237
+ f > 0 ? (Z(o, u, t.format), j.success(` ${n}: ${f} messages translated`)) : j.success(` ${n}: already fully translated`), p.length > 0 && j.warn(` ${n}: ${p.length} QA warnings`);
1238
+ }, s);
1368
1239
  }
1369
- }), qe = O({
1240
+ }), We = M({
1370
1241
  meta: {
1371
1242
  name: "migrate",
1372
1243
  description: "Migrate from another i18n library using AI"
@@ -1391,40 +1262,96 @@ var Ke = O({
1391
1262
  async run({ args: e }) {
1392
1263
  let t = e.provider;
1393
1264
  if (t !== "claude" && t !== "codex") {
1394
- A.error(`Invalid provider "${t}". Use "claude" or "codex".`);
1265
+ j.error(`Invalid provider "${t}". Use "claude" or "codex".`);
1395
1266
  return;
1396
1267
  }
1397
- await Ne({
1268
+ await Pe({
1398
1269
  from: e.from,
1399
1270
  provider: t,
1400
1271
  write: e.write ?? !1
1401
1272
  });
1402
1273
  }
1403
1274
  });
1404
- k(O({
1275
+ ee(M({
1405
1276
  meta: {
1406
1277
  name: "fluenti",
1407
1278
  version: "0.0.1",
1408
1279
  description: "Compile-time i18n for modern frameworks"
1409
1280
  },
1410
1281
  subCommands: {
1411
- init: O({
1282
+ init: M({
1412
1283
  meta: {
1413
1284
  name: "init",
1414
1285
  description: "Initialize Fluenti in your project"
1415
1286
  },
1416
1287
  args: {},
1417
1288
  async run() {
1418
- await Re({ cwd: process.cwd() });
1289
+ await p({ cwd: process.cwd() });
1290
+ }
1291
+ }),
1292
+ doctor: M({
1293
+ meta: {
1294
+ name: "doctor",
1295
+ description: "Diagnose Fluenti setup and vNext import issues"
1296
+ },
1297
+ args: {
1298
+ config: {
1299
+ type: "string",
1300
+ description: "Optional path to fluenti config file"
1301
+ },
1302
+ strict: {
1303
+ type: "boolean",
1304
+ description: "Treat warnings as errors",
1305
+ default: !1
1306
+ }
1307
+ },
1308
+ async run({ args: e }) {
1309
+ let t = await u({
1310
+ cwd: process.cwd(),
1311
+ ...e.config ? { config: e.config } : {}
1312
+ }), n = h(t);
1313
+ j.log(n);
1314
+ let r = t.findings.some((e) => e.severity === "error"), i = t.findings.some((e) => e.severity === "warning");
1315
+ (r || (e.strict ?? !1) && i) && (process.exitCode = 1);
1316
+ }
1317
+ }),
1318
+ codemod: M({
1319
+ meta: {
1320
+ name: "codemod",
1321
+ description: "Rewrite imports to the vNext Fluenti entry layout"
1322
+ },
1323
+ args: {
1324
+ write: {
1325
+ type: "boolean",
1326
+ description: "Write modified files to disk",
1327
+ default: !1
1328
+ },
1329
+ include: {
1330
+ type: "string",
1331
+ description: "Comma-separated glob list to scan"
1332
+ }
1333
+ },
1334
+ async run({ args: e }) {
1335
+ let t = e.include ? e.include.split(",").map((e) => e.trim()).filter(Boolean) : void 0, n = await a({
1336
+ cwd: process.cwd(),
1337
+ ...t ? { include: t } : {},
1338
+ write: e.write ?? !1
1339
+ });
1340
+ if (n.changedCount === 0) {
1341
+ j.success("No files need import rewrites.");
1342
+ return;
1343
+ }
1344
+ for (let t of n.changedFiles) j.log(`${e.write ? "updated" : "would update"} ${t.file}`);
1345
+ j.success(`${e.write ? "Updated" : "Detected"} ${n.changedCount} file(s) with Fluenti import rewrites.`);
1419
1346
  }
1420
1347
  }),
1421
- extract: Z,
1422
- compile: Ve,
1423
- stats: He,
1424
- lint: Ue,
1425
- check: We,
1426
- translate: Ke,
1427
- migrate: qe
1348
+ extract: Ie,
1349
+ compile: Re,
1350
+ stats: ze,
1351
+ lint: Be,
1352
+ check: Ve,
1353
+ translate: Ue,
1354
+ migrate: We
1428
1355
  }
1429
1356
  }));
1430
1357
  //#endregion