@fluenti/cli 0.4.0-rc.4 → 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 +335 -405
  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 +66 -65
  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,9 +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, { ignore: t.exclude ?? [] }), i = [], a = e["no-cache"] ?? !1 ? null : new f(t.catalogDir, J(process.cwd())), s = 0;
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, {
864
+ ignore: t.exclude ?? [],
865
+ absolute: !1
866
+ }), i = [], a = e["no-cache"] ?? !1 ? null : new d(t.catalogDir, Y(process.cwd())), s = 0;
993
867
  for (let e of r) {
994
868
  if (a) {
995
869
  let t = a.get(e);
@@ -998,16 +872,16 @@ var Z = O({
998
872
  continue;
999
873
  }
1000
874
  }
1001
- let n = await ze(e, v(e, "utf-8"), t.idGenerator);
875
+ let n = await Fe(e, x(e, "utf-8"), t.idGenerator);
1002
876
  i.push(...n), a && a.set(e, n);
1003
877
  }
1004
- 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`);
1005
- 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;
1006
880
  for (let e of n) {
1007
- let n = w(t.catalogDir, `${e}${c}`), { catalog: r, result: a } = o(Y(n, t.format), i, { stripFuzzy: d });
1008
- X(n, l ? Object.fromEntries(Object.entries(r).filter(([, e]) => !e.obsolete)) : r, t.format);
1009
- let s = l ? `${a.obsolete} removed` : `${a.obsolete} obsolete`;
1010
- 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}`);
1011
885
  }
1012
886
  for (let e of t.plugins ?? []) await e.onAfterExtract?.({
1013
887
  messages: new Map(i.map((e) => [e.id, e])),
@@ -1017,13 +891,13 @@ var Z = O({
1017
891
  });
1018
892
  }
1019
893
  });
1020
- function Be(e) {
894
+ function Le(e) {
1021
895
  let t = {};
1022
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);
1023
897
  return t;
1024
898
  }
1025
899
  async function Q(e, t, n) {
1026
- let r = Be(e);
900
+ let r = Le(e);
1027
901
  for (let e of n) e.transformMessages && (r = await e.transformMessages(r, t));
1028
902
  let i = {};
1029
903
  for (let [t, n] of Object.entries(e)) {
@@ -1045,7 +919,7 @@ function $(e, t, n, r) {
1045
919
  config: r
1046
920
  };
1047
921
  }
1048
- var Ve = O({
922
+ var Re = M({
1049
923
  meta: {
1050
924
  name: "compile",
1051
925
  description: "Compile message catalogs to JS modules"
@@ -1076,70 +950,70 @@ var Ve = O({
1076
950
  }
1077
951
  },
1078
952
  async run({ args: i }) {
1079
- let a = await u(i.config), o = m(a.locales), d = a.format === "json" ? ".json" : ".po";
1080
- _(a.compileOutDir, { recursive: !0 });
1081
- let f = {}, p = {};
1082
- for (let e of o) {
1083
- let t = w(a.catalogDir, `${e}${d}`);
1084
- if (g(t)) {
1085
- let n = v(t, "utf-8");
1086
- p[e] = n, f[e] = a.format === "json" ? l(n) : s(n);
1087
- } 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] = {};
1088
962
  }
1089
- let h = r(f);
1090
- A.info(`Compiling ${h.length} messages across ${o.length} locales`);
1091
- 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;
1092
- if (C !== void 0 && (isNaN(C) || C < 1)) {
1093
- 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;
1094
968
  return;
1095
969
  }
1096
- let T = 0, E = !1, D = [];
1097
- for (let e of o) {
1098
- if (x && x.isUpToDate(e, p[e]) && g(w(a.compileOutDir, `${e}.js`))) {
1099
- 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++;
1100
974
  continue;
1101
975
  }
1102
- D.push(e);
976
+ E.push(e);
1103
977
  }
1104
- if (D.length > 0 && (E = !0), S && D.length > 1) {
978
+ if (E.length > 0 && (T = !0), _ && E.length > 1) {
1105
979
  let e = a.plugins ?? [], t = {};
1106
- for (let n of D) {
1107
- for (let t of e) await t.onBeforeCompile?.($(n, f[n], a.compileOutDir, a));
1108
- 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];
1109
983
  }
1110
- let n = await c(D.map((e) => ({
984
+ let n = await m(E.map((e) => ({
1111
985
  locale: e,
1112
986
  catalog: t[e],
1113
- allIds: h,
987
+ allIds: p,
1114
988
  sourceLocale: a.sourceLocale,
1115
- options: { skipFuzzy: y }
1116
- })), C);
989
+ options: { skipFuzzy: h }
990
+ })), S);
1117
991
  for (let e of n) {
1118
- let t = w(a.compileOutDir, `${e.locale}.js`);
1119
- if (b(t, e.code, "utf-8"), x && x.set(e.locale, p[e.locale]), e.stats.missing.length > 0) {
1120
- A.warn(`${e.locale}: ${e.stats.compiled} compiled, ${e.stats.missing.length} missing translations`);
1121
- for (let t of e.stats.missing) A.warn(` ⤷ ${t}`);
1122
- } 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}`);
1123
997
  }
1124
- 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));
1125
999
  } else {
1126
1000
  let e = a.plugins ?? [];
1127
- for (let n of D) {
1128
- let r = w(a.compileOutDir, `${n}.js`);
1129
- for (let t of e) await t.onBeforeCompile?.($(n, f[n], a.compileOutDir, a));
1130
- 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 });
1131
- if (b(r, o, "utf-8"), x && x.set(n, p[n]), s.missing.length > 0) {
1132
- A.warn(`${n}: ${s.compiled} compiled, ${s.missing.length} missing translations`);
1133
- for (let e of s.missing) A.warn(` ⤷ ${e}`);
1134
- } 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}`);
1135
1009
  for (let t of e) await t.onAfterCompile?.($(n, i, a.compileOutDir, a));
1136
1010
  }
1137
1011
  }
1138
- T > 0 && A.info(`${T} locale(s) unchanged — skipped`), x && x.save();
1139
- let O = w(a.compileOutDir, "index.js"), k = w(a.compileOutDir, "messages.d.ts");
1140
- (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}`));
1141
1015
  }
1142
- }), He = O({
1016
+ }), ze = M({
1143
1017
  meta: {
1144
1018
  name: "stats",
1145
1019
  description: "Show translation progress"
@@ -1149,9 +1023,9 @@ var Ve = O({
1149
1023
  description: "Path to config file"
1150
1024
  } },
1151
1025
  async run({ args: e }) {
1152
- 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 = [];
1153
1027
  for (let e of n) {
1154
- 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) + "%" : "—";
1155
1029
  i.push({
1156
1030
  locale: e,
1157
1031
  total: o,
@@ -1159,11 +1033,11 @@ var Ve = O({
1159
1033
  pct: c
1160
1034
  });
1161
1035
  }
1162
- A.log(""), A.log(" Locale │ Total │ Translated │ Progress"), A.log(" ────────┼───────┼────────────┼─────────────────────────────");
1163
- for (let e of i) A.log(ie(e.locale, e.total, e.translated));
1164
- 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("");
1165
1039
  }
1166
- }), Ue = O({
1040
+ }), Be = M({
1167
1041
  meta: {
1168
1042
  name: "lint",
1169
1043
  description: "Check translation quality (missing, inconsistent placeholders, fuzzy)"
@@ -1184,21 +1058,21 @@ var Ve = O({
1184
1058
  }
1185
1059
  },
1186
1060
  async run({ args: e }) {
1187
- let t = await u(e.config), n = m(t.locales), r = t.format === "json" ? ".json" : ".po", i = {};
1188
- 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);
1189
1063
  let a = e.locale ? [e.locale] : void 0;
1190
- A.info(`Linting ${a ? a.join(", ") : "all locales"} (source: ${t.sourceLocale})`);
1191
- let o = {
1064
+ j.info(`Linting ${a ? a.join(", ") : "all locales"} (source: ${t.sourceLocale})`);
1065
+ let s = {
1192
1066
  sourceLocale: t.sourceLocale,
1193
1067
  strict: e.strict ?? !1
1194
1068
  };
1195
- a && (o.locales = a);
1196
- let s = R(i, o);
1197
- A.log(""), A.log(oe(s)), A.log("");
1198
- let c = s.filter((e) => e.severity === "error"), l = s.filter((e) => e.severity === "warning");
1199
- (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);
1200
1074
  }
1201
- }), We = O({
1075
+ }), Ve = M({
1202
1076
  meta: {
1203
1077
  name: "check",
1204
1078
  description: "Check translation coverage for CI"
@@ -1228,35 +1102,35 @@ var Ve = O({
1228
1102
  }
1229
1103
  },
1230
1104
  async run({ args: e }) {
1231
- let t = await u(e.config), n = m(t.locales), r = t.format === "json" ? ".json" : ".po", i = {};
1232
- 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);
1233
1107
  let a = parseFloat(e["min-coverage"] ?? "100");
1234
1108
  if (isNaN(a) || a < 0 || a > 100) {
1235
- 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;
1236
1110
  return;
1237
1111
  }
1238
- let o = e.format ?? (e.ci ? "github" : "text"), s = {
1112
+ let s = e.format ?? (e.ci ? "github" : "text"), c = {
1239
1113
  sourceLocale: t.sourceLocale,
1240
1114
  minCoverage: a,
1241
- format: o
1115
+ format: s
1242
1116
  };
1243
- e.locale && (s.locale = e.locale);
1244
- let c = ce(i, s);
1245
- switch (o) {
1117
+ e.locale && (c.locale = e.locale);
1118
+ let l = le(i, c);
1119
+ switch (s) {
1246
1120
  case "json":
1247
- A.log(de(c));
1121
+ j.log(fe(l));
1248
1122
  break;
1249
1123
  case "github":
1250
- A.log(ue(c, t.catalogDir, t.format));
1124
+ j.log(de(l, t.catalogDir, t.format));
1251
1125
  break;
1252
1126
  default:
1253
- A.log(""), A.log(le(c)), A.log("");
1127
+ j.log(""), j.log(ue(l)), j.log("");
1254
1128
  break;
1255
1129
  }
1256
- c.passed || (process.exitCode = 1);
1130
+ l.passed || (process.exitCode = 1);
1257
1131
  }
1258
1132
  });
1259
- async function Ge(e, t, n) {
1133
+ async function He(e, t, n) {
1260
1134
  let r = Array(e.length), i = 0, a = Array.from({ length: Math.min(n, e.length) }, async () => {
1261
1135
  for (; i < e.length;) {
1262
1136
  let n = i++;
@@ -1265,7 +1139,7 @@ async function Ge(e, t, n) {
1265
1139
  });
1266
1140
  return await Promise.all(a), r;
1267
1141
  }
1268
- var Ke = O({
1142
+ var Ue = M({
1269
1143
  meta: {
1270
1144
  name: "translate",
1271
1145
  description: "Translate messages using AI (Claude Code or Codex CLI)"
@@ -1312,58 +1186,58 @@ var Ke = O({
1312
1186
  }
1313
1187
  },
1314
1188
  async run({ args: e }) {
1315
- 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;
1316
1190
  if (r !== "claude" && r !== "codex") {
1317
- A.error(`Invalid provider "${r}". Use "claude" or "codex".`);
1191
+ j.error(`Invalid provider "${r}". Use "claude" or "codex".`);
1318
1192
  return;
1319
1193
  }
1320
1194
  let i = parseInt(e["batch-size"] ?? "50", 10);
1321
1195
  if (isNaN(i) || i < 1) {
1322
- A.error("Invalid batch-size. Must be a positive integer.");
1196
+ j.error("Invalid batch-size. Must be a positive integer.");
1323
1197
  return;
1324
1198
  }
1325
- let a = e.glossary ? _e(w(e.glossary)) : void 0, o = e.concurrency ? parseInt(e.concurrency, 10) : 3;
1326
- if (isNaN(o) || o < 1) {
1327
- 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.");
1328
1202
  return;
1329
1203
  }
1330
- let s = e.timeout ? parseInt(e.timeout, 10) : 120;
1331
- if (isNaN(s) || s < 1) {
1332
- 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).");
1333
1207
  return;
1334
1208
  }
1335
- let c = s * 1e3, l = e.locale ? [e.locale] : n.filter((e) => e !== t.sourceLocale);
1336
- if (l.length === 0) {
1337
- 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.");
1338
1212
  return;
1339
1213
  }
1340
- A.info(`Translating with ${r} (batch size: ${i})`);
1214
+ j.info(`Translating with ${r} (batch size: ${i})`);
1341
1215
  let d = t.format === "json" ? ".json" : ".po";
1342
- await Ge(l, async (n) => {
1343
- A.info(`\n[${n}]`);
1344
- 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);
1345
1219
  if (e["dry-run"]) {
1346
1220
  let e = Object.entries(s).filter(([, e]) => !e.obsolete && (!e.translation || e.translation.length === 0));
1347
1221
  if (e.length > 0) {
1348
- for (let [t, n] of e) A.log(` ${t}: ${n.message ?? t}`);
1349
- A.success(` ${n}: ${e.length} messages would be translated (dry-run)`);
1350
- } 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`);
1351
1225
  return;
1352
1226
  }
1353
- 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({
1354
1228
  provider: r,
1355
1229
  sourceLocale: t.sourceLocale,
1356
1230
  targetLocale: n,
1357
1231
  catalog: s,
1358
1232
  batchSize: i,
1359
- glossary: l,
1360
- timeoutMs: c,
1233
+ glossary: c,
1234
+ timeoutMs: l,
1361
1235
  ...e.context ? { context: e.context } : {}
1362
1236
  });
1363
- 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`);
1364
- }, 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);
1365
1239
  }
1366
- }), qe = O({
1240
+ }), We = M({
1367
1241
  meta: {
1368
1242
  name: "migrate",
1369
1243
  description: "Migrate from another i18n library using AI"
@@ -1388,40 +1262,96 @@ var Ke = O({
1388
1262
  async run({ args: e }) {
1389
1263
  let t = e.provider;
1390
1264
  if (t !== "claude" && t !== "codex") {
1391
- A.error(`Invalid provider "${t}". Use "claude" or "codex".`);
1265
+ j.error(`Invalid provider "${t}". Use "claude" or "codex".`);
1392
1266
  return;
1393
1267
  }
1394
- await Ne({
1268
+ await Pe({
1395
1269
  from: e.from,
1396
1270
  provider: t,
1397
1271
  write: e.write ?? !1
1398
1272
  });
1399
1273
  }
1400
1274
  });
1401
- k(O({
1275
+ ee(M({
1402
1276
  meta: {
1403
1277
  name: "fluenti",
1404
1278
  version: "0.0.1",
1405
1279
  description: "Compile-time i18n for modern frameworks"
1406
1280
  },
1407
1281
  subCommands: {
1408
- init: O({
1282
+ init: M({
1409
1283
  meta: {
1410
1284
  name: "init",
1411
1285
  description: "Initialize Fluenti in your project"
1412
1286
  },
1413
1287
  args: {},
1414
1288
  async run() {
1415
- 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.`);
1416
1346
  }
1417
1347
  }),
1418
- extract: Z,
1419
- compile: Ve,
1420
- stats: He,
1421
- lint: Ue,
1422
- check: We,
1423
- translate: Ke,
1424
- migrate: qe
1348
+ extract: Ie,
1349
+ compile: Re,
1350
+ stats: ze,
1351
+ lint: Be,
1352
+ check: Ve,
1353
+ translate: Ue,
1354
+ migrate: We
1425
1355
  }
1426
1356
  }));
1427
1357
  //#endregion