@fluenti/cli 0.3.3 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +9 -9
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +290 -251
- package/dist/cli.js.map +1 -1
- package/dist/compile-CX1b_JVQ.cjs +8 -0
- package/dist/compile-CX1b_JVQ.cjs.map +1 -0
- package/dist/{compile-B4y2UPsX.js → compile-CXReVuTG.js} +35 -32
- package/dist/compile-CXReVuTG.js.map +1 -0
- package/dist/compile-cache.d.ts.map +1 -1
- package/dist/compile-worker.cjs +1 -1
- package/dist/compile-worker.cjs.map +1 -1
- package/dist/compile-worker.d.ts +1 -0
- package/dist/compile-worker.d.ts.map +1 -1
- package/dist/compile-worker.js +19 -7
- package/dist/compile-worker.js.map +1 -1
- package/dist/compile.d.ts.map +1 -1
- package/dist/{extract-cache-DYoHe5P-.js → extract-cache-BTxWgic2.js} +57 -44
- package/dist/extract-cache-BTxWgic2.js.map +1 -0
- package/dist/{extract-cache-idNfsd7n.cjs → extract-cache-CGSKwh76.cjs} +5 -5
- package/dist/extract-cache-CGSKwh76.cjs.map +1 -0
- package/dist/extract-cache.d.ts.map +1 -1
- package/dist/extract-runner.d.ts.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +10 -4
- package/dist/index.js.map +1 -1
- package/dist/lint.d.ts.map +1 -1
- package/dist/parallel-compile.d.ts.map +1 -1
- package/dist/po-format.d.ts.map +1 -1
- package/dist/translate.d.ts +2 -1
- package/dist/translate.d.ts.map +1 -1
- package/dist/{tsx-extractor-C-9_TnCW.cjs → tsx-extractor-BOD7JJQK.cjs} +2 -2
- package/dist/tsx-extractor-BOD7JJQK.cjs.map +1 -0
- package/dist/{tsx-extractor-CjQIMvKo.js → tsx-extractor-C-HZNobu.js} +2 -2
- package/dist/tsx-extractor-C-HZNobu.js.map +1 -0
- package/dist/tsx-extractor.d.ts.map +1 -1
- package/dist/validation.d.ts +4 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/vue-extractor.cjs +2 -2
- package/dist/vue-extractor.cjs.map +1 -1
- package/dist/vue-extractor.d.ts.map +1 -1
- package/dist/vue-extractor.js +3 -3
- package/dist/vue-extractor.js.map +1 -1
- package/package.json +2 -2
- package/dist/compile-B4y2UPsX.js.map +0 -1
- package/dist/compile-Brygn0bn.cjs +0 -8
- package/dist/compile-Brygn0bn.cjs.map +0 -1
- package/dist/extract-cache-DYoHe5P-.js.map +0 -1
- package/dist/extract-cache-idNfsd7n.cjs.map +0 -1
- package/dist/tsx-extractor-C-9_TnCW.cjs.map +0 -1
- package/dist/tsx-extractor-CjQIMvKo.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,34 +1,65 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as e, n as t, r as n, t as r } from "./compile-
|
|
3
|
-
import { t as i } from "./tsx-extractor-
|
|
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-
|
|
5
|
-
import {
|
|
6
|
-
import { appendFileSync as
|
|
7
|
-
import { dirname as
|
|
8
|
-
import { fileURLToPath as
|
|
9
|
-
import
|
|
10
|
-
import { createHash as
|
|
11
|
-
import { defineCommand as
|
|
12
|
-
import
|
|
13
|
-
import { execFile as
|
|
14
|
-
import { promisify as
|
|
2
|
+
import { i as e, n as t, r as n, t as r } from "./compile-CXReVuTG.js";
|
|
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-BTxWgic2.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, writeFileSync as y } from "node:fs";
|
|
7
|
+
import { dirname as b, extname as x, join as S, resolve as C } from "node:path";
|
|
8
|
+
import { fileURLToPath as w } from "node:url";
|
|
9
|
+
import T from "fast-glob";
|
|
10
|
+
import { createHash as E } from "node:crypto";
|
|
11
|
+
import { defineCommand as D, runMain as O } from "citty";
|
|
12
|
+
import k from "consola";
|
|
13
|
+
import { execFile as A } from "node:child_process";
|
|
14
|
+
import { promisify as j } from "node:util";
|
|
15
|
+
import { setTimeout as ee } from "node:timers/promises";
|
|
15
16
|
//#region src/stats-format.ts
|
|
16
|
-
var
|
|
17
|
-
function
|
|
17
|
+
var te = "█", ne = "░";
|
|
18
|
+
function re(e, t = 20) {
|
|
18
19
|
let n = Math.round(Math.max(0, Math.min(100, e)) / 100 * t);
|
|
19
|
-
return
|
|
20
|
+
return te.repeat(n) + ne.repeat(t - n);
|
|
20
21
|
}
|
|
21
|
-
function
|
|
22
|
+
function M(e) {
|
|
22
23
|
let t = e.toFixed(1) + "%";
|
|
23
24
|
return e >= 90 ? `\x1b[32m${t}\x1b[0m` : e >= 70 ? `\x1b[33m${t}\x1b[0m` : `\x1b[31m${t}\x1b[0m`;
|
|
24
25
|
}
|
|
25
26
|
function ie(e, t, n) {
|
|
26
|
-
let r = t > 0 ? n / t * 100 : 0, i = t > 0 ?
|
|
27
|
+
let r = t > 0 ? n / t * 100 : 0, i = t > 0 ? M(r) : "—", a = t > 0 ? re(r) : "";
|
|
27
28
|
return ` ${e.padEnd(8)}│ ${String(t).padStart(5)} │ ${String(n).padStart(10)} │ ${a} ${i}`;
|
|
28
29
|
}
|
|
29
30
|
//#endregion
|
|
31
|
+
//#region src/validation.ts
|
|
32
|
+
function N(e) {
|
|
33
|
+
try {
|
|
34
|
+
let t = p(e), n = /* @__PURE__ */ new Set();
|
|
35
|
+
return P(t, n), [...n].sort();
|
|
36
|
+
} catch {
|
|
37
|
+
let t = /* @__PURE__ */ new Set(), n = 0, r = 0;
|
|
38
|
+
for (; r < e.length;) {
|
|
39
|
+
if (e[r] === "{") {
|
|
40
|
+
if (n++, n === 1) {
|
|
41
|
+
let n = e.indexOf("}", r + 1);
|
|
42
|
+
if (n !== -1) {
|
|
43
|
+
let i = e.slice(r + 1, n).trim(), a = /^(\w+)/.exec(i);
|
|
44
|
+
a && t.add(a[1]);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
} else e[r] === "}" && n--;
|
|
48
|
+
r++;
|
|
49
|
+
}
|
|
50
|
+
return [...t].sort();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function P(e, t) {
|
|
54
|
+
for (let n of e) if (n.type === "variable" && n.name !== "#") t.add(n.name);
|
|
55
|
+
else if (n.type === "plural" || n.type === "select") {
|
|
56
|
+
t.add(n.variable);
|
|
57
|
+
for (let e of Object.values(n.options)) P(e, t);
|
|
58
|
+
} else n.type === "function" && t.add(n.variable);
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
30
61
|
//#region src/lint.ts
|
|
31
|
-
function
|
|
62
|
+
function F(e, t) {
|
|
32
63
|
let n = [], { sourceLocale: r } = t, i = t.locales ?? Object.keys(e), a = e[r];
|
|
33
64
|
if (!a) return n.push({
|
|
34
65
|
rule: "missing-source",
|
|
@@ -60,7 +91,7 @@ function j(e, t) {
|
|
|
60
91
|
});
|
|
61
92
|
continue;
|
|
62
93
|
}
|
|
63
|
-
let s =
|
|
94
|
+
let s = N(r.message ?? e), c = N(o.translation), l = s.filter((e) => !c.includes(e)), u = c.filter((e) => !s.includes(e));
|
|
64
95
|
l.length > 0 && n.push({
|
|
65
96
|
rule: "inconsistent-placeholders",
|
|
66
97
|
severity: "error",
|
|
@@ -103,17 +134,9 @@ function j(e, t) {
|
|
|
103
134
|
});
|
|
104
135
|
return n;
|
|
105
136
|
}
|
|
106
|
-
function M(e) {
|
|
107
|
-
let t = [], n = /\{(\w+)(?:\s*,\s*(?:plural|select|selectordinal|number|date|time))?/g, r;
|
|
108
|
-
for (; (r = n.exec(e)) !== null;) {
|
|
109
|
-
let e = r[1];
|
|
110
|
-
t.includes(e) || t.push(e);
|
|
111
|
-
}
|
|
112
|
-
return t.sort();
|
|
113
|
-
}
|
|
114
137
|
function ae(e) {
|
|
115
138
|
if (e.length === 0) return " ✓ All checks passed";
|
|
116
|
-
let t = [], n =
|
|
139
|
+
let t = [], n = oe(e, (e) => e.rule);
|
|
117
140
|
for (let [e, r] of Object.entries(n)) {
|
|
118
141
|
t.push(` ${e} (${r.length}):`);
|
|
119
142
|
for (let e of r) {
|
|
@@ -124,7 +147,7 @@ function ae(e) {
|
|
|
124
147
|
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;
|
|
125
148
|
return t.push(""), t.push(` Summary: ${r} errors, ${i} warnings, ${a} info`), t.join("\n");
|
|
126
149
|
}
|
|
127
|
-
function
|
|
150
|
+
function oe(e, t) {
|
|
128
151
|
let n = {};
|
|
129
152
|
for (let r of e) {
|
|
130
153
|
let e = t(r);
|
|
@@ -134,7 +157,7 @@ function N(e, t) {
|
|
|
134
157
|
}
|
|
135
158
|
//#endregion
|
|
136
159
|
//#region src/check.ts
|
|
137
|
-
function
|
|
160
|
+
function se(e, t) {
|
|
138
161
|
let { sourceLocale: n, minCoverage: r, locale: i } = t, a = e[n];
|
|
139
162
|
if (!a) return {
|
|
140
163
|
results: [],
|
|
@@ -182,10 +205,10 @@ function P(e, t) {
|
|
|
182
205
|
passed: p,
|
|
183
206
|
minCoverage: r,
|
|
184
207
|
actualCoverage: f,
|
|
185
|
-
diagnostics:
|
|
208
|
+
diagnostics: F(e, m)
|
|
186
209
|
};
|
|
187
210
|
}
|
|
188
|
-
function
|
|
211
|
+
function ce(e) {
|
|
189
212
|
let t = [];
|
|
190
213
|
for (let n of e.results) {
|
|
191
214
|
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` : "";
|
|
@@ -195,7 +218,7 @@ function oe(e) {
|
|
|
195
218
|
let n = e.actualCoverage.toFixed(1), r = e.passed ? "PASSED" : "FAILED";
|
|
196
219
|
return t.push(`Coverage: ${n}% (min: ${e.minCoverage}%) — ${r}`), t.join("\n");
|
|
197
220
|
}
|
|
198
|
-
function
|
|
221
|
+
function le(e, t, n) {
|
|
199
222
|
let r = [], i = n === "json" ? ".json" : ".po";
|
|
200
223
|
for (let n of e.results) if (n.coverage < e.minCoverage) {
|
|
201
224
|
let a = `${t}/${n.locale}${i}`;
|
|
@@ -208,7 +231,7 @@ function se(e, t, n) {
|
|
|
208
231
|
}
|
|
209
232
|
return r.join("\n");
|
|
210
233
|
}
|
|
211
|
-
function
|
|
234
|
+
function ue(e) {
|
|
212
235
|
return JSON.stringify({
|
|
213
236
|
results: e.results,
|
|
214
237
|
passed: e.passed,
|
|
@@ -218,45 +241,76 @@ function ce(e) {
|
|
|
218
241
|
}
|
|
219
242
|
//#endregion
|
|
220
243
|
//#region src/compile-cache.ts
|
|
221
|
-
var
|
|
244
|
+
var I = "1", L = class {
|
|
222
245
|
data;
|
|
223
246
|
cachePath;
|
|
224
247
|
dirty = !1;
|
|
225
248
|
constructor(e, t) {
|
|
226
|
-
this.cachePath =
|
|
249
|
+
this.cachePath = C(t ? C(e, ".cache", t) : C(e, ".cache"), "compile-cache.json"), this.data = this.load();
|
|
227
250
|
}
|
|
228
251
|
isUpToDate(e, t) {
|
|
229
252
|
let n = this.data.entries[e];
|
|
230
253
|
if (!n) return !1;
|
|
231
|
-
let r =
|
|
254
|
+
let r = R(t);
|
|
232
255
|
return n.inputHash === r;
|
|
233
256
|
}
|
|
234
257
|
set(e, t) {
|
|
235
|
-
this.data.entries[e] = { inputHash:
|
|
258
|
+
this.data.entries[e] = { inputHash: R(t) }, this.dirty = !0;
|
|
236
259
|
}
|
|
237
260
|
save() {
|
|
238
|
-
|
|
261
|
+
if (this.dirty) try {
|
|
262
|
+
_(b(this.cachePath), { recursive: !0 }), y(this.cachePath, JSON.stringify(this.data), "utf-8"), this.dirty = !1;
|
|
263
|
+
} catch {}
|
|
239
264
|
}
|
|
240
265
|
load() {
|
|
241
266
|
try {
|
|
242
|
-
if (
|
|
243
|
-
let e =
|
|
244
|
-
if (t.version ===
|
|
267
|
+
if (g(this.cachePath)) {
|
|
268
|
+
let e = v(this.cachePath, "utf-8"), t = JSON.parse(e);
|
|
269
|
+
if (t.version === I) return t;
|
|
245
270
|
}
|
|
246
271
|
} catch {}
|
|
247
272
|
return {
|
|
248
|
-
version:
|
|
273
|
+
version: I,
|
|
249
274
|
entries: {}
|
|
250
275
|
};
|
|
251
276
|
}
|
|
252
277
|
};
|
|
253
|
-
function
|
|
254
|
-
return
|
|
278
|
+
function R(e) {
|
|
279
|
+
return E("md5").update(e).digest("hex");
|
|
280
|
+
}
|
|
281
|
+
//#endregion
|
|
282
|
+
//#region src/ai-provider.ts
|
|
283
|
+
var z = j(A), B = {
|
|
284
|
+
claude: "npm install -g @anthropic-ai/claude-code",
|
|
285
|
+
codex: "npm install -g @openai/codex"
|
|
286
|
+
};
|
|
287
|
+
function V(e, t) {
|
|
288
|
+
return e === "claude" ? ["-p", t] : [
|
|
289
|
+
"-p",
|
|
290
|
+
t,
|
|
291
|
+
"--full-auto"
|
|
292
|
+
];
|
|
293
|
+
}
|
|
294
|
+
function H(e) {
|
|
295
|
+
return e.code === "ENOENT";
|
|
296
|
+
}
|
|
297
|
+
async function U(e) {
|
|
298
|
+
let { provider: t, prompt: n, maxRetries: r = 3, initialDelayMs: i = 1e3, maxBuffer: a = 10 * 1024 * 1024 } = e, o = V(t, n), s = t === "claude" ? "claude" : "codex", c;
|
|
299
|
+
for (let e = 0; e <= r; e++) try {
|
|
300
|
+
let { stdout: t } = await z(s, [...o], { maxBuffer: a });
|
|
301
|
+
return {
|
|
302
|
+
stdout: t,
|
|
303
|
+
attempts: e + 1
|
|
304
|
+
};
|
|
305
|
+
} catch (n) {
|
|
306
|
+
if (H(n)) throw Error(`"${t}" CLI not found. Please install it first:\n ${B[t]}`);
|
|
307
|
+
c = n, e < r && await ee(i * 2 ** e);
|
|
308
|
+
}
|
|
309
|
+
throw c;
|
|
255
310
|
}
|
|
256
311
|
//#endregion
|
|
257
312
|
//#region src/translate.ts
|
|
258
|
-
|
|
259
|
-
function R(e, t, n, r) {
|
|
313
|
+
function de(e, t, n, r) {
|
|
260
314
|
let i = JSON.stringify(n, null, 2);
|
|
261
315
|
return [
|
|
262
316
|
`You are a professional translator. Translate the following messages from "${e}" to "${t}".`,
|
|
@@ -272,25 +326,7 @@ function R(e, t, n, r) {
|
|
|
272
326
|
"- Do not add any explanation or markdown formatting, output raw JSON only."
|
|
273
327
|
].join("\n");
|
|
274
328
|
}
|
|
275
|
-
|
|
276
|
-
let n = 10 * 1024 * 1024;
|
|
277
|
-
try {
|
|
278
|
-
if (e === "claude") {
|
|
279
|
-
let { stdout: e } = await L("claude", ["-p", t], { maxBuffer: n });
|
|
280
|
-
return e;
|
|
281
|
-
} else {
|
|
282
|
-
let { stdout: e } = await L("codex", [
|
|
283
|
-
"-p",
|
|
284
|
-
t,
|
|
285
|
-
"--full-auto"
|
|
286
|
-
], { maxBuffer: n });
|
|
287
|
-
return e;
|
|
288
|
-
}
|
|
289
|
-
} catch (t) {
|
|
290
|
-
throw t.code === "ENOENT" ? Error(`"${e}" CLI not found. Please install it first:\n` + (e === "claude" ? " npm install -g @anthropic-ai/claude-code" : " npm install -g @openai/codex")) : t;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
function B(e) {
|
|
329
|
+
function fe(e) {
|
|
294
330
|
let t = e.match(/\{[\s\S]*\}/);
|
|
295
331
|
if (!t) throw Error("No JSON object found in AI response");
|
|
296
332
|
let n;
|
|
@@ -302,12 +338,12 @@ function B(e) {
|
|
|
302
338
|
if (typeof n != "object" || !n || Array.isArray(n)) throw Error("AI response is not a valid JSON object");
|
|
303
339
|
return n;
|
|
304
340
|
}
|
|
305
|
-
function
|
|
341
|
+
function pe(e) {
|
|
306
342
|
let t = {};
|
|
307
343
|
for (let [n, r] of Object.entries(e)) r.obsolete || (!r.translation || r.translation.length === 0) && (t[n] = r.message ?? n);
|
|
308
344
|
return t;
|
|
309
345
|
}
|
|
310
|
-
function
|
|
346
|
+
function me(e, t) {
|
|
311
347
|
let n = Object.keys(e), r = [];
|
|
312
348
|
for (let i = 0; i < n.length; i += t) {
|
|
313
349
|
let a = {};
|
|
@@ -316,22 +352,25 @@ function H(e, t) {
|
|
|
316
352
|
}
|
|
317
353
|
return r;
|
|
318
354
|
}
|
|
319
|
-
async function
|
|
320
|
-
let { provider: t, sourceLocale: n, targetLocale: r, catalog: i, batchSize: a, context: o } = e, s =
|
|
355
|
+
async function he(e) {
|
|
356
|
+
let { provider: t, sourceLocale: n, targetLocale: r, catalog: i, batchSize: a, context: o } = e, s = pe(i), c = Object.keys(s).length;
|
|
321
357
|
if (c === 0) return {
|
|
322
358
|
catalog: { ...i },
|
|
323
359
|
translated: 0
|
|
324
360
|
};
|
|
325
|
-
|
|
326
|
-
let l = { ...i }, u =
|
|
361
|
+
k.info(` ${c} untranslated messages, translating with ${t}...`);
|
|
362
|
+
let l = { ...i }, u = me(s, a), d = 0;
|
|
327
363
|
for (let e = 0; e < u.length; e++) {
|
|
328
364
|
let i = u[e], a = Object.keys(i);
|
|
329
|
-
u.length > 1 &&
|
|
330
|
-
let s =
|
|
331
|
-
|
|
365
|
+
u.length > 1 && k.info(` Batch ${e + 1}/${u.length} (${a.length} messages)`);
|
|
366
|
+
let { stdout: s } = await U({
|
|
367
|
+
provider: t,
|
|
368
|
+
prompt: de(n, r, i, o)
|
|
369
|
+
}), c = fe(s);
|
|
370
|
+
for (let e of a) c[e] && typeof c[e] == "string" ? (l[e] = {
|
|
332
371
|
...l[e],
|
|
333
|
-
translation:
|
|
334
|
-
}, d++) :
|
|
372
|
+
translation: c[e]
|
|
373
|
+
}, d++) : k.warn(` Missing translation for key: ${e}`);
|
|
335
374
|
}
|
|
336
375
|
return {
|
|
337
376
|
catalog: l,
|
|
@@ -340,7 +379,7 @@ async function U(e) {
|
|
|
340
379
|
}
|
|
341
380
|
//#endregion
|
|
342
381
|
//#region src/migrate.ts
|
|
343
|
-
var W = A
|
|
382
|
+
var W = j(A), G = {
|
|
344
383
|
"vue-i18n": {
|
|
345
384
|
name: "vue-i18n",
|
|
346
385
|
framework: "Vue",
|
|
@@ -481,36 +520,36 @@ var W = A(k), G = {
|
|
|
481
520
|
migrationGuide: "react/llms-migration.txt"
|
|
482
521
|
}
|
|
483
522
|
}, K = Object.keys(G);
|
|
484
|
-
function
|
|
523
|
+
function ge(e) {
|
|
485
524
|
let t = e.toLowerCase().replace(/^@nuxtjs\//, "nuxt-").replace(/^@/, "");
|
|
486
525
|
return K.find((e) => e === t);
|
|
487
526
|
}
|
|
488
|
-
async function
|
|
527
|
+
async function _e(e) {
|
|
489
528
|
let t = {
|
|
490
529
|
configFiles: [],
|
|
491
530
|
localeFiles: [],
|
|
492
531
|
sampleSources: [],
|
|
493
532
|
packageJson: void 0
|
|
494
|
-
}, n =
|
|
495
|
-
|
|
533
|
+
}, n = C("package.json");
|
|
534
|
+
g(n) && (t.packageJson = v(n, "utf-8"));
|
|
496
535
|
for (let n of e.configPatterns) {
|
|
497
|
-
let e =
|
|
498
|
-
|
|
536
|
+
let e = C(n);
|
|
537
|
+
g(e) && t.configFiles.push({
|
|
499
538
|
path: n,
|
|
500
|
-
content:
|
|
539
|
+
content: v(e, "utf-8")
|
|
501
540
|
});
|
|
502
541
|
}
|
|
503
|
-
let r = await
|
|
542
|
+
let r = await T(e.localePatterns, { absolute: !1 });
|
|
504
543
|
for (let e of r.slice(0, 10)) {
|
|
505
|
-
let n =
|
|
544
|
+
let n = v(C(e), "utf-8");
|
|
506
545
|
t.localeFiles.push({
|
|
507
546
|
path: e,
|
|
508
547
|
content: n.length > 5e3 ? n.slice(0, 5e3) + "\n... (truncated)" : n
|
|
509
548
|
});
|
|
510
549
|
}
|
|
511
|
-
let i = await
|
|
550
|
+
let i = await T(e.sourcePatterns, { absolute: !1 });
|
|
512
551
|
for (let e of i.slice(0, 5)) {
|
|
513
|
-
let n =
|
|
552
|
+
let n = v(C(e), "utf-8");
|
|
514
553
|
t.sampleSources.push({
|
|
515
554
|
path: e,
|
|
516
555
|
content: n.length > 3e3 ? n.slice(0, 3e3) + "\n... (truncated)" : n
|
|
@@ -518,16 +557,16 @@ async function ue(e) {
|
|
|
518
557
|
}
|
|
519
558
|
return t;
|
|
520
559
|
}
|
|
521
|
-
function
|
|
522
|
-
let t = typeof __dirname < "u" ? __dirname :
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
560
|
+
function ve(e) {
|
|
561
|
+
let t = typeof __dirname < "u" ? __dirname : b(w(import.meta.url)), n = [
|
|
562
|
+
C("node_modules", "@fluenti", "cli", "..", "..", e),
|
|
563
|
+
S(t, "..", "..", "..", e),
|
|
564
|
+
S(t, "..", "..", e)
|
|
526
565
|
];
|
|
527
|
-
for (let e of n) if (
|
|
566
|
+
for (let e of n) if (g(e)) return v(e, "utf-8");
|
|
528
567
|
return "";
|
|
529
568
|
}
|
|
530
|
-
function
|
|
569
|
+
function ye(e, t, n) {
|
|
531
570
|
let r = [];
|
|
532
571
|
if (r.push(`You are a migration assistant helping convert a ${e.framework} project from "${e.name}" to Fluenti (@fluenti).`, "", "Your task:", "1. Generate a `fluenti.config.ts` file based on the existing i18n configuration", "2. Convert each locale/translation file to Fluenti PO format", "3. List the code changes needed (file by file) to migrate source code from the old API to Fluenti API", ""), n && r.push("=== MIGRATION GUIDE ===", n, ""), t.packageJson && r.push("=== package.json ===", t.packageJson, ""), t.configFiles.length > 0) {
|
|
533
572
|
r.push("=== EXISTING CONFIG FILES ===");
|
|
@@ -543,7 +582,7 @@ function fe(e, t, n) {
|
|
|
543
582
|
}
|
|
544
583
|
return r.push("", "=== OUTPUT FORMAT ===", "Respond with the following sections, each starting with the exact header shown:", "", "### FLUENTI_CONFIG", "```ts", "// The fluenti.config.ts content", "```", "", "### LOCALE_FILES", "For each locale file, output:", "#### LOCALE: {locale_code}", "```po", "// The PO file content", "```", "", "### MIGRATION_STEPS", "A numbered checklist of specific code changes needed, with before/after examples.", "", "### INSTALL_COMMANDS", "```bash", "// The install and uninstall commands", "```"), r.join("\n");
|
|
545
584
|
}
|
|
546
|
-
async function
|
|
585
|
+
async function be(e, t) {
|
|
547
586
|
let n = 10 * 1024 * 1024;
|
|
548
587
|
try {
|
|
549
588
|
if (e === "claude") {
|
|
@@ -562,7 +601,7 @@ async function pe(e, t) {
|
|
|
562
601
|
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;
|
|
563
602
|
}
|
|
564
603
|
}
|
|
565
|
-
function
|
|
604
|
+
function xe(e) {
|
|
566
605
|
let t = {
|
|
567
606
|
config: void 0,
|
|
568
607
|
localeFiles: [],
|
|
@@ -583,58 +622,58 @@ function me(e) {
|
|
|
583
622
|
let a = e.match(/### INSTALL_COMMANDS[\s\S]*?```(?:bash|sh)?\n([\s\S]*?)```/);
|
|
584
623
|
return a && (t.installCommands = a[1].trim()), t;
|
|
585
624
|
}
|
|
586
|
-
async function
|
|
587
|
-
let { from: t, provider: n, write: r } = e, i =
|
|
625
|
+
async function Se(e) {
|
|
626
|
+
let { from: t, provider: n, write: r } = e, i = ge(t);
|
|
588
627
|
if (!i) {
|
|
589
|
-
|
|
590
|
-
for (let e of K)
|
|
628
|
+
k.error(`Unsupported library "${t}". Supported libraries:`);
|
|
629
|
+
for (let e of K) k.log(` - ${e}`);
|
|
591
630
|
return;
|
|
592
631
|
}
|
|
593
632
|
let a = G[i];
|
|
594
|
-
|
|
595
|
-
let o = await
|
|
633
|
+
k.info(`Migrating from ${a.name} (${a.framework}) to Fluenti`), k.info("Scanning project for existing i18n files...");
|
|
634
|
+
let o = await _e(a);
|
|
596
635
|
if (o.configFiles.length === 0 && o.localeFiles.length === 0) {
|
|
597
|
-
|
|
636
|
+
k.warn(`No ${a.name} configuration or locale files found.`), k.info("Make sure you are running this command from the project root directory.");
|
|
598
637
|
return;
|
|
599
638
|
}
|
|
600
|
-
|
|
601
|
-
let s =
|
|
602
|
-
|
|
603
|
-
let c =
|
|
604
|
-
if (c.installCommands && (
|
|
639
|
+
k.info(`Found: ${o.configFiles.length} config file(s), ${o.localeFiles.length} locale file(s), ${o.sampleSources.length} source file(s)`);
|
|
640
|
+
let s = ve(a.migrationGuide);
|
|
641
|
+
k.info(`Generating migration plan with ${n}...`);
|
|
642
|
+
let c = xe(await be(n, ye(a, o, s)));
|
|
643
|
+
if (c.installCommands && (k.log(""), k.box({
|
|
605
644
|
title: "Install Commands",
|
|
606
645
|
message: c.installCommands
|
|
607
646
|
})), c.config) if (r) {
|
|
608
|
-
let { writeFileSync: e } = await import("node:fs"), t =
|
|
609
|
-
e(t, c.config, "utf-8"),
|
|
610
|
-
} else
|
|
647
|
+
let { writeFileSync: e } = await import("node:fs"), t = C("fluenti.config.ts");
|
|
648
|
+
e(t, c.config, "utf-8"), k.success(`Written: ${t}`);
|
|
649
|
+
} else k.log(""), k.box({
|
|
611
650
|
title: "fluenti.config.ts",
|
|
612
651
|
message: c.config
|
|
613
652
|
});
|
|
614
653
|
if (c.localeFiles.length > 0) if (r) {
|
|
615
654
|
let { writeFileSync: e, mkdirSync: t } = await import("node:fs"), n = "./locales";
|
|
616
|
-
t(
|
|
655
|
+
t(C(n), { recursive: !0 });
|
|
617
656
|
for (let t of c.localeFiles) {
|
|
618
|
-
let r =
|
|
619
|
-
e(r, t.content, "utf-8"),
|
|
657
|
+
let r = C(n, `${t.locale}.po`);
|
|
658
|
+
e(r, t.content, "utf-8"), k.success(`Written: ${r}`);
|
|
620
659
|
}
|
|
621
|
-
} else for (let e of c.localeFiles)
|
|
660
|
+
} else for (let e of c.localeFiles) k.log(""), k.box({
|
|
622
661
|
title: `locales/${e.locale}.po`,
|
|
623
662
|
message: e.content.length > 500 ? e.content.slice(0, 500) + "\n... (use --write to save full file)" : e.content
|
|
624
663
|
});
|
|
625
|
-
c.steps && (
|
|
664
|
+
c.steps && (k.log(""), k.box({
|
|
626
665
|
title: "Migration Steps",
|
|
627
666
|
message: c.steps
|
|
628
|
-
})), !r && (c.config || c.localeFiles.length > 0) && (
|
|
667
|
+
})), !r && (c.config || c.localeFiles.length > 0) && (k.log(""), k.info("Run with --write to save generated files to disk:"), k.log(` fluenti migrate --from ${t} --write`));
|
|
629
668
|
}
|
|
630
669
|
//#endregion
|
|
631
670
|
//#region src/init.ts
|
|
632
|
-
var
|
|
633
|
-
function
|
|
634
|
-
if (!
|
|
671
|
+
var Ce = /^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{1,8})*$/;
|
|
672
|
+
function q(e) {
|
|
673
|
+
if (!Ce.test(e)) throw Error(`Invalid locale format: "${e}"`);
|
|
635
674
|
return e;
|
|
636
675
|
}
|
|
637
|
-
var
|
|
676
|
+
var we = [
|
|
638
677
|
{
|
|
639
678
|
dep: "next",
|
|
640
679
|
name: "nextjs",
|
|
@@ -666,8 +705,8 @@ var _e = [
|
|
|
666
705
|
pluginPackage: "@fluenti/react"
|
|
667
706
|
}
|
|
668
707
|
];
|
|
669
|
-
function
|
|
670
|
-
for (let t of
|
|
708
|
+
function Te(e) {
|
|
709
|
+
for (let t of we) if (t.dep in e) return {
|
|
671
710
|
name: t.name,
|
|
672
711
|
pluginPackage: t.pluginPackage
|
|
673
712
|
};
|
|
@@ -676,7 +715,7 @@ function ve(e) {
|
|
|
676
715
|
pluginPackage: null
|
|
677
716
|
};
|
|
678
717
|
}
|
|
679
|
-
function
|
|
718
|
+
function Ee(e) {
|
|
680
719
|
let t = e.locales.map((e) => `'${e}'`).join(", ");
|
|
681
720
|
return `import { defineConfig } from '@fluenti/cli'
|
|
682
721
|
|
|
@@ -690,50 +729,50 @@ export default defineConfig({
|
|
|
690
729
|
})
|
|
691
730
|
`;
|
|
692
731
|
}
|
|
693
|
-
async function
|
|
694
|
-
let t =
|
|
695
|
-
if (!
|
|
696
|
-
|
|
732
|
+
async function De(e) {
|
|
733
|
+
let t = C(e.cwd, "package.json");
|
|
734
|
+
if (!g(t)) {
|
|
735
|
+
k.error("No package.json found in current directory.");
|
|
697
736
|
return;
|
|
698
737
|
}
|
|
699
|
-
let n = JSON.parse(
|
|
738
|
+
let n = JSON.parse(v(t, "utf-8")), r = Te({
|
|
700
739
|
...n.dependencies,
|
|
701
740
|
...n.devDependencies
|
|
702
741
|
});
|
|
703
|
-
|
|
704
|
-
let i =
|
|
705
|
-
if (
|
|
706
|
-
|
|
742
|
+
k.info(`Detected framework: ${r.name}`), r.pluginPackage && k.info(`Recommended plugin: ${r.pluginPackage}`);
|
|
743
|
+
let i = C(e.cwd, "fluenti.config.ts");
|
|
744
|
+
if (g(i)) {
|
|
745
|
+
k.warn("fluenti.config.ts already exists. Skipping config generation.");
|
|
707
746
|
return;
|
|
708
747
|
}
|
|
709
|
-
let a = await
|
|
748
|
+
let a = await k.prompt("Source locale?", {
|
|
710
749
|
type: "text",
|
|
711
750
|
default: "en",
|
|
712
751
|
placeholder: "en"
|
|
713
752
|
});
|
|
714
753
|
if (typeof a == "symbol") return;
|
|
715
|
-
let o = await
|
|
754
|
+
let o = await k.prompt("Target locales (comma-separated)?", {
|
|
716
755
|
type: "text",
|
|
717
756
|
default: "ja,zh-CN",
|
|
718
757
|
placeholder: "ja,zh-CN"
|
|
719
758
|
});
|
|
720
759
|
if (typeof o == "symbol") return;
|
|
721
|
-
let s = await
|
|
760
|
+
let s = await k.prompt("Catalog format?", {
|
|
722
761
|
type: "select",
|
|
723
762
|
options: ["po", "json"],
|
|
724
763
|
initial: "po"
|
|
725
764
|
});
|
|
726
765
|
if (typeof s == "symbol") return;
|
|
727
766
|
let c = o.split(",").map((e) => e.trim()).filter(Boolean);
|
|
728
|
-
|
|
729
|
-
for (let e of c)
|
|
730
|
-
|
|
767
|
+
q(a);
|
|
768
|
+
for (let e of c) q(e);
|
|
769
|
+
y(i, Ee({
|
|
731
770
|
sourceLocale: a,
|
|
732
771
|
locales: [a, ...c.filter((e) => e !== a)],
|
|
733
772
|
format: s
|
|
734
|
-
}), "utf-8"),
|
|
735
|
-
let l =
|
|
736
|
-
|
|
773
|
+
}), "utf-8"), k.success("Created fluenti.config.ts");
|
|
774
|
+
let l = C(e.cwd, ".gitignore"), u = "src/locales/compiled/";
|
|
775
|
+
g(l) ? v(l, "utf-8").includes(u) || (h(l, `\n# Fluenti compiled catalogs\n${u}\n`), k.success("Updated .gitignore")) : (y(l, `# Fluenti compiled catalogs\n${u}\n`), k.success("Created .gitignore"));
|
|
737
776
|
let d = n.scripts ?? {}, f = {}, p = !1;
|
|
738
777
|
if (d["i18n:extract"] || (f["i18n:extract"] = "fluenti extract", p = !0), d["i18n:compile"] || (f["i18n:compile"] = "fluenti compile", p = !0), p) {
|
|
739
778
|
let e = {
|
|
@@ -743,9 +782,9 @@ async function be(e) {
|
|
|
743
782
|
...f
|
|
744
783
|
}
|
|
745
784
|
};
|
|
746
|
-
|
|
785
|
+
y(t, JSON.stringify(e, null, 2) + "\n", "utf-8"), k.success("Added i18n:extract and i18n:compile scripts to package.json");
|
|
747
786
|
}
|
|
748
|
-
|
|
787
|
+
k.log(""), k.box({
|
|
749
788
|
title: "Next steps",
|
|
750
789
|
message: [
|
|
751
790
|
r.pluginPackage ? `1. Install: pnpm add -D ${r.pluginPackage} @fluenti/cli` : "1. Install: pnpm add -D @fluenti/cli",
|
|
@@ -758,27 +797,27 @@ async function be(e) {
|
|
|
758
797
|
}
|
|
759
798
|
//#endregion
|
|
760
799
|
//#region src/cli.ts
|
|
761
|
-
function
|
|
762
|
-
return
|
|
800
|
+
function J(e) {
|
|
801
|
+
return E("md5").update(e).digest("hex").slice(0, 8);
|
|
763
802
|
}
|
|
764
|
-
function
|
|
765
|
-
if (!
|
|
766
|
-
let n =
|
|
803
|
+
function Y(e, t) {
|
|
804
|
+
if (!g(e)) return {};
|
|
805
|
+
let n = v(e, "utf-8");
|
|
767
806
|
return t === "json" ? l(n) : s(n);
|
|
768
807
|
}
|
|
769
|
-
function
|
|
770
|
-
|
|
808
|
+
function X(e, t, n) {
|
|
809
|
+
_(b(e), { recursive: !0 }), y(e, n === "json" ? d(t) : a(t), "utf-8");
|
|
771
810
|
}
|
|
772
|
-
async function
|
|
773
|
-
if (
|
|
811
|
+
async function Oe(e, t, n) {
|
|
812
|
+
if (x(e) === ".vue") try {
|
|
774
813
|
let { extractFromVue: r } = await import("./vue-extractor.js");
|
|
775
814
|
return r(t, e, n);
|
|
776
815
|
} catch {
|
|
777
|
-
return
|
|
816
|
+
return k.warn(`Skipping ${e}: install @vue/compiler-sfc to extract from .vue files`), [];
|
|
778
817
|
}
|
|
779
818
|
return i(t, e, n);
|
|
780
819
|
}
|
|
781
|
-
var
|
|
820
|
+
var ke = D({
|
|
782
821
|
meta: {
|
|
783
822
|
name: "extract",
|
|
784
823
|
description: "Extract messages from source files"
|
|
@@ -805,9 +844,9 @@ var Se = E({
|
|
|
805
844
|
}
|
|
806
845
|
},
|
|
807
846
|
async run({ args: e }) {
|
|
808
|
-
let t = await u(e.config), n =
|
|
809
|
-
|
|
810
|
-
let r = await
|
|
847
|
+
let t = await u(e.config), n = m(t.locales);
|
|
848
|
+
k.info(`Extracting messages from ${t.include.join(", ")}`);
|
|
849
|
+
let r = await T(t.include, { ignore: t.exclude ?? [] }), i = [], a = e["no-cache"] ?? !1 ? null : new f(t.catalogDir, J(process.cwd())), s = 0;
|
|
811
850
|
for (let e of r) {
|
|
812
851
|
if (a) {
|
|
813
852
|
let t = a.get(e);
|
|
@@ -816,16 +855,16 @@ var Se = E({
|
|
|
816
855
|
continue;
|
|
817
856
|
}
|
|
818
857
|
}
|
|
819
|
-
let n = await
|
|
858
|
+
let n = await Oe(e, v(e, "utf-8"), t.idGenerator);
|
|
820
859
|
i.push(...n), a && a.set(e, n);
|
|
821
860
|
}
|
|
822
|
-
a && (a.prune(new Set(r)), a.save()), s > 0 ?
|
|
861
|
+
a && (a.prune(new Set(r)), a.save()), s > 0 ? k.info(`Found ${i.length} messages in ${r.length} files (${s} cached)`) : k.info(`Found ${i.length} messages in ${r.length} files`);
|
|
823
862
|
let c = t.format === "json" ? ".json" : ".po", l = e.clean ?? !1, d = e["no-fuzzy"] ?? !1;
|
|
824
863
|
for (let e of n) {
|
|
825
|
-
let n =
|
|
826
|
-
|
|
864
|
+
let n = C(t.catalogDir, `${e}${c}`), { catalog: r, result: a } = o(Y(n, t.format), i, { stripFuzzy: d });
|
|
865
|
+
X(n, l ? Object.fromEntries(Object.entries(r).filter(([, e]) => !e.obsolete)) : r, t.format);
|
|
827
866
|
let s = l ? `${a.obsolete} removed` : `${a.obsolete} obsolete`;
|
|
828
|
-
|
|
867
|
+
k.success(`${e}: ${a.added} added, ${a.unchanged} unchanged, ${s}`);
|
|
829
868
|
}
|
|
830
869
|
for (let e of t.plugins ?? []) await e.onAfterExtract?.({
|
|
831
870
|
messages: new Map(i.map((e) => [e.id, e])),
|
|
@@ -835,13 +874,13 @@ var Se = E({
|
|
|
835
874
|
});
|
|
836
875
|
}
|
|
837
876
|
});
|
|
838
|
-
function
|
|
877
|
+
function Ae(e) {
|
|
839
878
|
let t = {};
|
|
840
879
|
for (let [n, r] of Object.entries(e)) r.translation && r.translation.length > 0 ? t[n] = r.translation : r.message && (t[n] = r.message);
|
|
841
880
|
return t;
|
|
842
881
|
}
|
|
843
|
-
async function
|
|
844
|
-
let r =
|
|
882
|
+
async function Z(e, t, n) {
|
|
883
|
+
let r = Ae(e);
|
|
845
884
|
for (let e of n) e.transformMessages && (r = await e.transformMessages(r, t));
|
|
846
885
|
let i = {};
|
|
847
886
|
for (let [t, n] of Object.entries(e)) {
|
|
@@ -853,7 +892,7 @@ async function Q(e, t, n) {
|
|
|
853
892
|
}
|
|
854
893
|
return i;
|
|
855
894
|
}
|
|
856
|
-
function
|
|
895
|
+
function Q(e, t, n, r) {
|
|
857
896
|
let i = {};
|
|
858
897
|
for (let [e, n] of Object.entries(t)) n.translation && n.translation.length > 0 ? i[e] = n.translation : n.message && (i[e] = n.message);
|
|
859
898
|
return {
|
|
@@ -863,7 +902,7 @@ function $(e, t, n, r) {
|
|
|
863
902
|
config: r
|
|
864
903
|
};
|
|
865
904
|
}
|
|
866
|
-
var
|
|
905
|
+
var $ = D({
|
|
867
906
|
meta: {
|
|
868
907
|
name: "compile",
|
|
869
908
|
description: "Compile message catalogs to JS modules"
|
|
@@ -894,70 +933,70 @@ var we = E({
|
|
|
894
933
|
}
|
|
895
934
|
},
|
|
896
935
|
async run({ args: i }) {
|
|
897
|
-
let a = await u(i.config), o =
|
|
898
|
-
|
|
899
|
-
let f = {},
|
|
936
|
+
let a = await u(i.config), o = m(a.locales), d = a.format === "json" ? ".json" : ".po";
|
|
937
|
+
_(a.compileOutDir, { recursive: !0 });
|
|
938
|
+
let f = {}, p = {};
|
|
900
939
|
for (let e of o) {
|
|
901
|
-
let t =
|
|
902
|
-
if (
|
|
903
|
-
let n =
|
|
904
|
-
|
|
905
|
-
} else
|
|
940
|
+
let t = C(a.catalogDir, `${e}${d}`);
|
|
941
|
+
if (g(t)) {
|
|
942
|
+
let n = v(t, "utf-8");
|
|
943
|
+
p[e] = n, f[e] = a.format === "json" ? l(n) : s(n);
|
|
944
|
+
} else p[e] = "", f[e] = {};
|
|
906
945
|
}
|
|
907
|
-
let
|
|
908
|
-
|
|
909
|
-
let b = i["skip-fuzzy"] ?? !1, x = i["no-cache"] ?? !1 ? null : new
|
|
946
|
+
let h = r(f);
|
|
947
|
+
k.info(`Compiling ${h.length} messages across ${o.length} locales`);
|
|
948
|
+
let b = i["skip-fuzzy"] ?? !1, x = i["no-cache"] ?? !1 ? null : new L(a.catalogDir, J(process.cwd())), S = i.parallel ?? !1, w = i.concurrency ? parseInt(i.concurrency, 10) : void 0;
|
|
910
949
|
if (w !== void 0 && (isNaN(w) || w < 1)) {
|
|
911
|
-
|
|
950
|
+
k.error("Invalid --concurrency. Must be a positive integer."), process.exitCode = 1;
|
|
912
951
|
return;
|
|
913
952
|
}
|
|
914
953
|
let T = 0, E = !1, D = [];
|
|
915
954
|
for (let e of o) {
|
|
916
|
-
if (x && x.isUpToDate(e,
|
|
955
|
+
if (x && x.isUpToDate(e, p[e]) && g(C(a.compileOutDir, `${e}.js`))) {
|
|
917
956
|
T++;
|
|
918
957
|
continue;
|
|
919
958
|
}
|
|
920
959
|
D.push(e);
|
|
921
960
|
}
|
|
922
|
-
if (D.length > 0 && (E = !0),
|
|
961
|
+
if (D.length > 0 && (E = !0), S && D.length > 1) {
|
|
923
962
|
let e = a.plugins ?? [], t = {};
|
|
924
963
|
for (let n of D) {
|
|
925
|
-
for (let t of e) await t.onBeforeCompile?.(
|
|
926
|
-
t[n] = e.length > 0 ? await
|
|
964
|
+
for (let t of e) await t.onBeforeCompile?.(Q(n, f[n], a.compileOutDir, a));
|
|
965
|
+
t[n] = e.length > 0 ? await Z(f[n], n, e) : f[n];
|
|
927
966
|
}
|
|
928
967
|
let n = await c(D.map((e) => ({
|
|
929
968
|
locale: e,
|
|
930
969
|
catalog: t[e],
|
|
931
|
-
allIds:
|
|
970
|
+
allIds: h,
|
|
932
971
|
sourceLocale: a.sourceLocale,
|
|
933
972
|
options: { skipFuzzy: b }
|
|
934
973
|
})), w);
|
|
935
974
|
for (let e of n) {
|
|
936
|
-
let t =
|
|
937
|
-
if (
|
|
938
|
-
|
|
939
|
-
for (let t of e.stats.missing)
|
|
940
|
-
} else
|
|
975
|
+
let t = C(a.compileOutDir, `${e.locale}.js`);
|
|
976
|
+
if (y(t, e.code, "utf-8"), x && x.set(e.locale, p[e.locale]), e.stats.missing.length > 0) {
|
|
977
|
+
k.warn(`${e.locale}: ${e.stats.compiled} compiled, ${e.stats.missing.length} missing translations`);
|
|
978
|
+
for (let t of e.stats.missing) k.warn(` ⤷ ${t}`);
|
|
979
|
+
} else k.success(`Compiled ${e.locale}: ${e.stats.compiled} messages → ${t}`);
|
|
941
980
|
}
|
|
942
|
-
for (let n of D) for (let r of e) await r.onAfterCompile?.(
|
|
981
|
+
for (let n of D) for (let r of e) await r.onAfterCompile?.(Q(n, t[n], a.compileOutDir, a));
|
|
943
982
|
} else {
|
|
944
983
|
let e = a.plugins ?? [];
|
|
945
984
|
for (let n of D) {
|
|
946
|
-
let r =
|
|
947
|
-
for (let t of e) await t.onBeforeCompile?.(
|
|
948
|
-
let i = e.length > 0 ? await
|
|
949
|
-
if (
|
|
950
|
-
|
|
951
|
-
for (let e of s.missing)
|
|
952
|
-
} else
|
|
953
|
-
for (let t of e) await t.onAfterCompile?.(
|
|
985
|
+
let r = C(a.compileOutDir, `${n}.js`);
|
|
986
|
+
for (let t of e) await t.onBeforeCompile?.(Q(n, f[n], a.compileOutDir, a));
|
|
987
|
+
let i = e.length > 0 ? await Z(f[n], n, e) : f[n], { code: o, stats: s } = t(i, n, h, a.sourceLocale, { skipFuzzy: b });
|
|
988
|
+
if (y(r, o, "utf-8"), x && x.set(n, p[n]), s.missing.length > 0) {
|
|
989
|
+
k.warn(`${n}: ${s.compiled} compiled, ${s.missing.length} missing translations`);
|
|
990
|
+
for (let e of s.missing) k.warn(` ⤷ ${e}`);
|
|
991
|
+
} else k.success(`Compiled ${n}: ${s.compiled} messages → ${r}`);
|
|
992
|
+
for (let t of e) await t.onAfterCompile?.(Q(n, i, a.compileOutDir, a));
|
|
954
993
|
}
|
|
955
994
|
}
|
|
956
|
-
T > 0 &&
|
|
957
|
-
let
|
|
958
|
-
(E || !
|
|
995
|
+
T > 0 && k.info(`${T} locale(s) unchanged — skipped`), x && x.save();
|
|
996
|
+
let O = C(a.compileOutDir, "index.js"), A = C(a.compileOutDir, "messages.d.ts");
|
|
997
|
+
(E || !g(O)) && (y(O, n(o, a.compileOutDir), "utf-8"), k.success(`Generated index → ${O}`)), (E || !g(A)) && (y(A, e(h, f, a.sourceLocale), "utf-8"), k.success(`Generated types → ${A}`));
|
|
959
998
|
}
|
|
960
|
-
}),
|
|
999
|
+
}), je = D({
|
|
961
1000
|
meta: {
|
|
962
1001
|
name: "stats",
|
|
963
1002
|
description: "Show translation progress"
|
|
@@ -967,9 +1006,9 @@ var we = E({
|
|
|
967
1006
|
description: "Path to config file"
|
|
968
1007
|
} },
|
|
969
1008
|
async run({ args: e }) {
|
|
970
|
-
let t = await u(e.config), n =
|
|
1009
|
+
let t = await u(e.config), n = m(t.locales), r = t.format === "json" ? ".json" : ".po", i = [];
|
|
971
1010
|
for (let e of n) {
|
|
972
|
-
let n =
|
|
1011
|
+
let n = Y(C(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) + "%" : "—";
|
|
973
1012
|
i.push({
|
|
974
1013
|
locale: e,
|
|
975
1014
|
total: o,
|
|
@@ -977,11 +1016,11 @@ var we = E({
|
|
|
977
1016
|
pct: c
|
|
978
1017
|
});
|
|
979
1018
|
}
|
|
980
|
-
|
|
981
|
-
for (let e of i)
|
|
982
|
-
|
|
1019
|
+
k.log(""), k.log(" Locale │ Total │ Translated │ Progress"), k.log(" ────────┼───────┼────────────┼─────────────────────────────");
|
|
1020
|
+
for (let e of i) k.log(ie(e.locale, e.total, e.translated));
|
|
1021
|
+
k.log("");
|
|
983
1022
|
}
|
|
984
|
-
}),
|
|
1023
|
+
}), Me = D({
|
|
985
1024
|
meta: {
|
|
986
1025
|
name: "lint",
|
|
987
1026
|
description: "Check translation quality (missing, inconsistent placeholders, fuzzy)"
|
|
@@ -1002,21 +1041,21 @@ var we = E({
|
|
|
1002
1041
|
}
|
|
1003
1042
|
},
|
|
1004
1043
|
async run({ args: e }) {
|
|
1005
|
-
let t = await u(e.config), n =
|
|
1006
|
-
for (let e of n) i[e] =
|
|
1044
|
+
let t = await u(e.config), n = m(t.locales), r = t.format === "json" ? ".json" : ".po", i = {};
|
|
1045
|
+
for (let e of n) i[e] = Y(C(t.catalogDir, `${e}${r}`), t.format);
|
|
1007
1046
|
let a = e.locale ? [e.locale] : void 0;
|
|
1008
|
-
|
|
1047
|
+
k.info(`Linting ${a ? a.join(", ") : "all locales"} (source: ${t.sourceLocale})`);
|
|
1009
1048
|
let o = {
|
|
1010
1049
|
sourceLocale: t.sourceLocale,
|
|
1011
1050
|
strict: e.strict ?? !1
|
|
1012
1051
|
};
|
|
1013
1052
|
a && (o.locales = a);
|
|
1014
|
-
let s =
|
|
1015
|
-
|
|
1053
|
+
let s = F(i, o);
|
|
1054
|
+
k.log(""), k.log(ae(s)), k.log("");
|
|
1016
1055
|
let c = s.filter((e) => e.severity === "error"), l = s.filter((e) => e.severity === "warning");
|
|
1017
1056
|
(c.length > 0 || e.strict && l.length > 0) && (process.exitCode = 1);
|
|
1018
1057
|
}
|
|
1019
|
-
}),
|
|
1058
|
+
}), Ne = D({
|
|
1020
1059
|
meta: {
|
|
1021
1060
|
name: "check",
|
|
1022
1061
|
description: "Check translation coverage for CI"
|
|
@@ -1046,11 +1085,11 @@ var we = E({
|
|
|
1046
1085
|
}
|
|
1047
1086
|
},
|
|
1048
1087
|
async run({ args: e }) {
|
|
1049
|
-
let t = await u(e.config), n =
|
|
1050
|
-
for (let e of n) i[e] =
|
|
1088
|
+
let t = await u(e.config), n = m(t.locales), r = t.format === "json" ? ".json" : ".po", i = {};
|
|
1089
|
+
for (let e of n) i[e] = Y(C(t.catalogDir, `${e}${r}`), t.format);
|
|
1051
1090
|
let a = parseFloat(e["min-coverage"] ?? "100");
|
|
1052
1091
|
if (isNaN(a) || a < 0 || a > 100) {
|
|
1053
|
-
|
|
1092
|
+
k.error("Invalid --min-coverage. Must be a number between 0 and 100."), process.exitCode = 1;
|
|
1054
1093
|
return;
|
|
1055
1094
|
}
|
|
1056
1095
|
let o = e.format ?? (e.ci ? "github" : "text"), s = {
|
|
@@ -1059,21 +1098,21 @@ var we = E({
|
|
|
1059
1098
|
format: o
|
|
1060
1099
|
};
|
|
1061
1100
|
e.locale && (s.locale = e.locale);
|
|
1062
|
-
let c =
|
|
1101
|
+
let c = se(i, s);
|
|
1063
1102
|
switch (o) {
|
|
1064
1103
|
case "json":
|
|
1065
|
-
|
|
1104
|
+
k.log(ue(c));
|
|
1066
1105
|
break;
|
|
1067
1106
|
case "github":
|
|
1068
|
-
|
|
1107
|
+
k.log(le(c, t.catalogDir, t.format));
|
|
1069
1108
|
break;
|
|
1070
1109
|
default:
|
|
1071
|
-
|
|
1110
|
+
k.log(""), k.log(ce(c)), k.log("");
|
|
1072
1111
|
break;
|
|
1073
1112
|
}
|
|
1074
1113
|
c.passed || (process.exitCode = 1);
|
|
1075
1114
|
}
|
|
1076
|
-
}),
|
|
1115
|
+
}), Pe = D({
|
|
1077
1116
|
meta: {
|
|
1078
1117
|
name: "translate",
|
|
1079
1118
|
description: "Translate messages using AI (Claude Code or Codex CLI)"
|
|
@@ -1108,35 +1147,35 @@ var we = E({
|
|
|
1108
1147
|
}
|
|
1109
1148
|
},
|
|
1110
1149
|
async run({ args: e }) {
|
|
1111
|
-
let t = await u(e.config), n =
|
|
1150
|
+
let t = await u(e.config), n = m(t.locales), r = e.provider;
|
|
1112
1151
|
if (r !== "claude" && r !== "codex") {
|
|
1113
|
-
|
|
1152
|
+
k.error(`Invalid provider "${r}". Use "claude" or "codex".`);
|
|
1114
1153
|
return;
|
|
1115
1154
|
}
|
|
1116
1155
|
let i = parseInt(e["batch-size"] ?? "50", 10);
|
|
1117
1156
|
if (isNaN(i) || i < 1) {
|
|
1118
|
-
|
|
1157
|
+
k.error("Invalid batch-size. Must be a positive integer.");
|
|
1119
1158
|
return;
|
|
1120
1159
|
}
|
|
1121
1160
|
let a = e.locale ? [e.locale] : n.filter((e) => e !== t.sourceLocale);
|
|
1122
1161
|
if (a.length === 0) {
|
|
1123
|
-
|
|
1162
|
+
k.warn("No target locales to translate.");
|
|
1124
1163
|
return;
|
|
1125
1164
|
}
|
|
1126
|
-
|
|
1165
|
+
k.info(`Translating with ${r} (batch size: ${i})`);
|
|
1127
1166
|
let o = t.format === "json" ? ".json" : ".po";
|
|
1128
1167
|
for (let n of a) {
|
|
1129
|
-
|
|
1130
|
-
let a =
|
|
1168
|
+
k.info(`\n[${n}]`);
|
|
1169
|
+
let a = C(t.catalogDir, `${n}${o}`), s = Y(a, t.format);
|
|
1131
1170
|
if (e["dry-run"]) {
|
|
1132
1171
|
let e = Object.entries(s).filter(([, e]) => !e.obsolete && (!e.translation || e.translation.length === 0));
|
|
1133
1172
|
if (e.length > 0) {
|
|
1134
|
-
for (let [t, n] of e)
|
|
1135
|
-
|
|
1136
|
-
} else
|
|
1173
|
+
for (let [t, n] of e) k.log(` ${t}: ${n.message ?? t}`);
|
|
1174
|
+
k.success(` ${n}: ${e.length} messages would be translated (dry-run)`);
|
|
1175
|
+
} else k.success(` ${n}: already fully translated`);
|
|
1137
1176
|
continue;
|
|
1138
1177
|
}
|
|
1139
|
-
let { catalog: c, translated: l } = await
|
|
1178
|
+
let { catalog: c, translated: l } = await he({
|
|
1140
1179
|
provider: r,
|
|
1141
1180
|
sourceLocale: t.sourceLocale,
|
|
1142
1181
|
targetLocale: n,
|
|
@@ -1144,10 +1183,10 @@ var we = E({
|
|
|
1144
1183
|
batchSize: i,
|
|
1145
1184
|
...e.context ? { context: e.context } : {}
|
|
1146
1185
|
});
|
|
1147
|
-
l > 0 ? (
|
|
1186
|
+
l > 0 ? (X(a, c, t.format), k.success(` ${n}: ${l} messages translated`)) : k.success(` ${n}: already fully translated`);
|
|
1148
1187
|
}
|
|
1149
1188
|
}
|
|
1150
|
-
}),
|
|
1189
|
+
}), Fe = D({
|
|
1151
1190
|
meta: {
|
|
1152
1191
|
name: "migrate",
|
|
1153
1192
|
description: "Migrate from another i18n library using AI"
|
|
@@ -1172,40 +1211,40 @@ var we = E({
|
|
|
1172
1211
|
async run({ args: e }) {
|
|
1173
1212
|
let t = e.provider;
|
|
1174
1213
|
if (t !== "claude" && t !== "codex") {
|
|
1175
|
-
|
|
1214
|
+
k.error(`Invalid provider "${t}". Use "claude" or "codex".`);
|
|
1176
1215
|
return;
|
|
1177
1216
|
}
|
|
1178
|
-
await
|
|
1217
|
+
await Se({
|
|
1179
1218
|
from: e.from,
|
|
1180
1219
|
provider: t,
|
|
1181
1220
|
write: e.write ?? !1
|
|
1182
1221
|
});
|
|
1183
1222
|
}
|
|
1184
1223
|
});
|
|
1185
|
-
D(
|
|
1224
|
+
O(D({
|
|
1186
1225
|
meta: {
|
|
1187
1226
|
name: "fluenti",
|
|
1188
1227
|
version: "0.0.1",
|
|
1189
1228
|
description: "Compile-time i18n for modern frameworks"
|
|
1190
1229
|
},
|
|
1191
1230
|
subCommands: {
|
|
1192
|
-
init:
|
|
1231
|
+
init: D({
|
|
1193
1232
|
meta: {
|
|
1194
1233
|
name: "init",
|
|
1195
1234
|
description: "Initialize Fluenti in your project"
|
|
1196
1235
|
},
|
|
1197
1236
|
args: {},
|
|
1198
1237
|
async run() {
|
|
1199
|
-
await
|
|
1238
|
+
await De({ cwd: process.cwd() });
|
|
1200
1239
|
}
|
|
1201
1240
|
}),
|
|
1202
|
-
extract:
|
|
1203
|
-
compile:
|
|
1204
|
-
stats:
|
|
1205
|
-
lint:
|
|
1206
|
-
check:
|
|
1207
|
-
translate:
|
|
1208
|
-
migrate:
|
|
1241
|
+
extract: ke,
|
|
1242
|
+
compile: $,
|
|
1243
|
+
stats: je,
|
|
1244
|
+
lint: Me,
|
|
1245
|
+
check: Ne,
|
|
1246
|
+
translate: Pe,
|
|
1247
|
+
migrate: Fe
|
|
1209
1248
|
}
|
|
1210
1249
|
}));
|
|
1211
1250
|
//#endregion
|