@fluenti/cli 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/src/ai-provider.d.ts.map +1 -0
- package/dist/{catalog.d.ts → cli/src/catalog.d.ts} +1 -1
- package/dist/cli/src/catalog.d.ts.map +1 -0
- package/dist/cli/src/check.d.ts.map +1 -0
- package/dist/cli/src/cli.d.ts.map +1 -0
- package/dist/cli/src/codemod.d.ts +19 -0
- package/dist/cli/src/codemod.d.ts.map +1 -0
- package/dist/cli/src/compile-cache.d.ts.map +1 -0
- package/dist/cli/src/compile-runner.d.ts.map +1 -0
- package/dist/cli/src/compile-worker.d.ts.map +1 -0
- package/dist/cli/src/compile.d.ts.map +1 -0
- package/dist/cli/src/config-loader.d.ts +2 -0
- package/dist/cli/src/config-loader.d.ts.map +1 -0
- package/dist/{config.d.ts → cli/src/config.d.ts} +1 -1
- package/dist/cli/src/config.d.ts.map +1 -0
- package/dist/cli/src/doctor.d.ts +19 -0
- package/dist/cli/src/doctor.d.ts.map +1 -0
- package/dist/{extract-cache.d.ts → cli/src/extract-cache.d.ts} +1 -1
- package/dist/cli/src/extract-cache.d.ts.map +1 -0
- package/dist/cli/src/extract-runner.d.ts.map +1 -0
- package/dist/cli/src/glossary.d.ts.map +1 -0
- package/dist/{index.d.ts → cli/src/index.d.ts} +7 -3
- package/dist/cli/src/index.d.ts.map +1 -0
- package/dist/cli/src/init.d.ts.map +1 -0
- package/dist/cli/src/json-format.d.ts.map +1 -0
- package/dist/cli/src/lint.d.ts.map +1 -0
- package/dist/cli/src/migrate.d.ts.map +1 -0
- package/dist/cli/src/parallel-compile.d.ts.map +1 -0
- package/dist/cli/src/po-format.d.ts.map +1 -0
- package/dist/cli/src/stats-format.d.ts.map +1 -0
- package/dist/cli/src/translate-parse.d.ts.map +1 -0
- package/dist/cli/src/translate-prompt.d.ts.map +1 -0
- package/dist/cli/src/translate.d.ts.map +1 -0
- package/dist/{tsx-extractor.d.ts → cli/src/tsx-extractor.d.ts} +1 -1
- package/dist/cli/src/tsx-extractor.d.ts.map +1 -0
- package/dist/cli/src/validation.d.ts.map +1 -0
- package/dist/{vue-extractor.d.ts → cli/src/vue-extractor.d.ts} +1 -1
- package/dist/cli/src/vue-extractor.d.ts.map +1 -0
- package/dist/cli.cjs +10 -22
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +333 -406
- package/dist/cli.js.map +1 -1
- package/dist/compile-C3VLvhUf.cjs +8 -0
- package/dist/{compile-kXClO6q4.cjs.map → compile-C3VLvhUf.cjs.map} +1 -1
- package/dist/{compile-CdA4EZ-p.js → compile-CZVpE5Md.js} +2 -2
- package/dist/{compile-CdA4EZ-p.js.map → compile-CZVpE5Md.js.map} +1 -1
- package/dist/compile-worker.cjs +1 -1
- package/dist/compile-worker.cjs.map +1 -1
- package/dist/compile-worker.js +1 -1
- package/dist/doctor-BqXXxyST.js +670 -0
- package/dist/doctor-BqXXxyST.js.map +1 -0
- package/dist/doctor-xp8WS8sr.cjs +24 -0
- package/dist/doctor-xp8WS8sr.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +64 -64
- package/dist/index.js.map +1 -1
- package/dist/tsx-extractor-AOjsbOmp.cjs +2 -0
- package/dist/{tsx-extractor-B0vFXziu.cjs.map → tsx-extractor-AOjsbOmp.cjs.map} +1 -1
- package/dist/tsx-extractor-C-HZNobu.js.map +1 -1
- package/dist/vue-extractor.cjs +1 -1
- package/dist/vue-extractor.cjs.map +1 -1
- package/dist/vue-extractor.js.map +1 -1
- package/package.json +2 -2
- package/dist/ai-provider.d.ts.map +0 -1
- package/dist/catalog.d.ts.map +0 -1
- package/dist/check.d.ts.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/compile-cache.d.ts.map +0 -1
- package/dist/compile-kXClO6q4.cjs +0 -8
- package/dist/compile-runner.d.ts.map +0 -1
- package/dist/compile-worker.d.ts.map +0 -1
- package/dist/compile.d.ts.map +0 -1
- package/dist/config-loader.d.ts +0 -2
- package/dist/config-loader.d.ts.map +0 -1
- package/dist/config.d.ts.map +0 -1
- package/dist/extract-cache-BioSaoFo.cjs +0 -10
- package/dist/extract-cache-BioSaoFo.cjs.map +0 -1
- package/dist/extract-cache-C-MI1_ll.js +0 -325
- package/dist/extract-cache-C-MI1_ll.js.map +0 -1
- package/dist/extract-cache.d.ts.map +0 -1
- package/dist/extract-runner.d.ts.map +0 -1
- package/dist/glossary.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/init.d.ts.map +0 -1
- package/dist/json-format.d.ts.map +0 -1
- package/dist/lint.d.ts.map +0 -1
- package/dist/migrate.d.ts.map +0 -1
- package/dist/parallel-compile.d.ts.map +0 -1
- package/dist/po-format.d.ts.map +0 -1
- package/dist/stats-format.d.ts.map +0 -1
- package/dist/translate-parse.d.ts.map +0 -1
- package/dist/translate-prompt.d.ts.map +0 -1
- package/dist/translate.d.ts.map +0 -1
- package/dist/tsx-extractor-B0vFXziu.cjs +0 -2
- package/dist/tsx-extractor.d.ts.map +0 -1
- package/dist/validation.d.ts.map +0 -1
- package/dist/vue-extractor.d.ts.map +0 -1
- /package/dist/{ai-provider.d.ts → cli/src/ai-provider.d.ts} +0 -0
- /package/dist/{check.d.ts → cli/src/check.d.ts} +0 -0
- /package/dist/{cli.d.ts → cli/src/cli.d.ts} +0 -0
- /package/dist/{compile-cache.d.ts → cli/src/compile-cache.d.ts} +0 -0
- /package/dist/{compile-runner.d.ts → cli/src/compile-runner.d.ts} +0 -0
- /package/dist/{compile-worker.d.ts → cli/src/compile-worker.d.ts} +0 -0
- /package/dist/{compile.d.ts → cli/src/compile.d.ts} +0 -0
- /package/dist/{extract-runner.d.ts → cli/src/extract-runner.d.ts} +0 -0
- /package/dist/{glossary.d.ts → cli/src/glossary.d.ts} +0 -0
- /package/dist/{init.d.ts → cli/src/init.d.ts} +0 -0
- /package/dist/{json-format.d.ts → cli/src/json-format.d.ts} +0 -0
- /package/dist/{lint.d.ts → cli/src/lint.d.ts} +0 -0
- /package/dist/{migrate.d.ts → cli/src/migrate.d.ts} +0 -0
- /package/dist/{parallel-compile.d.ts → cli/src/parallel-compile.d.ts} +0 -0
- /package/dist/{po-format.d.ts → cli/src/po-format.d.ts} +0 -0
- /package/dist/{stats-format.d.ts → cli/src/stats-format.d.ts} +0 -0
- /package/dist/{translate-parse.d.ts → cli/src/translate-parse.d.ts} +0 -0
- /package/dist/{translate-prompt.d.ts → cli/src/translate-prompt.d.ts} +0 -0
- /package/dist/{translate.d.ts → cli/src/translate.d.ts} +0 -0
- /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-
|
|
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,
|
|
5
|
-
import { parse as
|
|
6
|
-
import {
|
|
7
|
-
import { dirname as
|
|
8
|
-
import { fileURLToPath as
|
|
9
|
-
import
|
|
10
|
-
import { createHash as
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import { execFile as
|
|
14
|
-
import { promisify as
|
|
15
|
-
import { setTimeout as
|
|
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
|
|
18
|
-
function
|
|
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
|
|
20
|
+
return te.repeat(n) + ne.repeat(t - n);
|
|
21
21
|
}
|
|
22
|
-
function
|
|
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
|
|
27
|
-
let r = t > 0 ? n / t * 100 : 0, i = t > 0 ?
|
|
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
|
|
33
|
-
function
|
|
32
|
+
var I = /\{(\w+),\s*(plural|select|selectordinal)\s*,/;
|
|
33
|
+
function L(e) {
|
|
34
34
|
try {
|
|
35
|
-
let t =
|
|
36
|
-
return
|
|
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
|
|
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))
|
|
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
|
|
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
|
|
67
|
-
let n =
|
|
68
|
-
if (
|
|
69
|
-
|
|
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
|
|
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 =
|
|
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
|
|
159
|
+
function se(e) {
|
|
160
160
|
if (e.length === 0) return " ✓ All checks passed";
|
|
161
|
-
let t = [], n =
|
|
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
|
|
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
|
|
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:
|
|
230
|
+
diagnostics: B(e, m)
|
|
231
231
|
};
|
|
232
232
|
}
|
|
233
|
-
function
|
|
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
|
|
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
|
|
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
|
|
266
|
+
var V = "1", pe = class {
|
|
267
267
|
data;
|
|
268
268
|
cachePath;
|
|
269
269
|
dirty = !1;
|
|
270
270
|
constructor(e, t) {
|
|
271
|
-
this.cachePath =
|
|
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 =
|
|
276
|
+
let r = H(t);
|
|
277
277
|
return n.inputHash === r;
|
|
278
278
|
}
|
|
279
279
|
set(e, t) {
|
|
280
|
-
this.data.entries[e] = { inputHash:
|
|
280
|
+
this.data.entries[e] = { inputHash: H(t) }, this.dirty = !0;
|
|
281
281
|
}
|
|
282
282
|
save() {
|
|
283
283
|
if (this.dirty) try {
|
|
284
|
-
|
|
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 (
|
|
290
|
-
let e =
|
|
291
|
-
if (t.version ===
|
|
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:
|
|
295
|
+
version: V,
|
|
296
296
|
entries: {}
|
|
297
297
|
};
|
|
298
298
|
}
|
|
299
299
|
};
|
|
300
|
-
function
|
|
301
|
-
return
|
|
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
|
|
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
|
|
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
|
|
316
|
+
function _e(e) {
|
|
317
317
|
return e.code === "ENOENT";
|
|
318
318
|
}
|
|
319
|
-
async function
|
|
320
|
-
let { provider: t, prompt: n, maxRetries: r = 3, initialDelayMs: i = 1e3, maxBuffer: a = 10 * 1024 * 1024, timeoutMs: o = 12e4 } = e, s =
|
|
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 =
|
|
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 (
|
|
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
|
|
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
|
|
339
|
-
function
|
|
340
|
-
if (!
|
|
341
|
-
let t =
|
|
342
|
-
if (t >
|
|
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 =
|
|
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
|
|
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
|
|
363
|
+
function W(e) {
|
|
364
364
|
return e.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/[\n\r]/g, " ").replace(/\t/g, " ");
|
|
365
365
|
}
|
|
366
|
-
function
|
|
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]) => `"${
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
418
|
-
let n = [], r =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
465
|
-
let { provider: t, sourceLocale: n, targetLocale: r, catalog: i, batchSize: a, context: o, glossary: s, timeoutMs: c } = e, l =
|
|
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
|
-
|
|
472
|
-
let d = { ...i }, f =
|
|
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 &&
|
|
475
|
+
f.length > 1 && j.info(` Batch ${e + 1}/${f.length} (${a.length} messages)`);
|
|
476
476
|
try {
|
|
477
|
-
let { stdout: e } = await
|
|
477
|
+
let { stdout: e } = await ve({
|
|
478
478
|
provider: t,
|
|
479
|
-
prompt:
|
|
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 } =
|
|
488
|
-
for (let e of u) m.push(`[${r}] ${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}`),
|
|
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
|
|
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
|
-
},
|
|
647
|
-
function
|
|
646
|
+
}, J = Object.keys(q);
|
|
647
|
+
function Oe(e) {
|
|
648
648
|
let t = e.toLowerCase().replace(/^@nuxtjs\//, "nuxt-").replace(/^@/, "");
|
|
649
|
-
return
|
|
649
|
+
return J.find((e) => e === t);
|
|
650
650
|
}
|
|
651
|
-
async function
|
|
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 =
|
|
658
|
-
|
|
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 =
|
|
661
|
-
|
|
660
|
+
let e = D(n);
|
|
661
|
+
y(e) && t.configFiles.push({
|
|
662
662
|
path: n,
|
|
663
|
-
content:
|
|
663
|
+
content: x(e, "utf-8")
|
|
664
664
|
});
|
|
665
665
|
}
|
|
666
|
-
let r = await
|
|
666
|
+
let r = await k(e.localePatterns, { absolute: !1 });
|
|
667
667
|
for (let e of r.slice(0, 10)) {
|
|
668
|
-
let n =
|
|
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
|
|
674
|
+
let i = await k(e.sourcePatterns, { absolute: !1 });
|
|
675
675
|
for (let e of i.slice(0, 5)) {
|
|
676
|
-
let n =
|
|
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
|
|
685
|
-
let t = typeof __dirname < "u" ? __dirname :
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
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 (
|
|
690
|
+
for (let e of n) if (y(e)) return x(e, "utf-8");
|
|
691
691
|
return "";
|
|
692
692
|
}
|
|
693
|
-
function
|
|
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
|
|
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
|
|
713
|
+
let { stdout: e } = await K("claude", ["-p", t], { maxBuffer: n });
|
|
714
714
|
return e;
|
|
715
715
|
} else {
|
|
716
|
-
let { stdout: e } = await
|
|
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
|
|
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
|
|
759
|
-
let { from: t, provider: n, write: r } = e, i =
|
|
758
|
+
async function Pe(e) {
|
|
759
|
+
let { from: t, provider: n, write: r } = e, i = Oe(t);
|
|
760
760
|
if (!i) {
|
|
761
|
-
|
|
762
|
-
for (let e of
|
|
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 =
|
|
766
|
-
|
|
767
|
-
let o = await
|
|
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
|
-
|
|
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
|
-
|
|
773
|
-
let s =
|
|
774
|
-
|
|
775
|
-
let c =
|
|
776
|
-
if (c.installCommands && (
|
|
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 =
|
|
781
|
-
e(t, c.config, "utf-8"),
|
|
782
|
-
} else
|
|
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(
|
|
788
|
+
t(D(n), { recursive: !0 });
|
|
789
789
|
for (let t of c.localeFiles) {
|
|
790
|
-
let r =
|
|
791
|
-
e(r, t.content, "utf-8"),
|
|
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)
|
|
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
|
-
|
|
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 =
|
|
801
|
-
n(t, e.patch, "utf-8"),
|
|
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)
|
|
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 && (
|
|
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) && (
|
|
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
|
|
944
|
-
return
|
|
814
|
+
function Y(e) {
|
|
815
|
+
return A("md5").update(e).digest("hex").slice(0, 8);
|
|
945
816
|
}
|
|
946
|
-
function
|
|
947
|
-
if (!
|
|
948
|
-
let n =
|
|
949
|
-
return t === "json" ?
|
|
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
|
|
952
|
-
|
|
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
|
|
955
|
-
if (
|
|
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
|
|
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
|
|
834
|
+
var Ie = M({
|
|
964
835
|
meta: {
|
|
965
836
|
name: "extract",
|
|
966
837
|
description: "Extract messages from source files"
|
|
@@ -987,12 +858,12 @@ var Z = O({
|
|
|
987
858
|
}
|
|
988
859
|
},
|
|
989
860
|
async run({ args: e }) {
|
|
990
|
-
let t = await
|
|
991
|
-
|
|
992
|
-
let r = await
|
|
861
|
+
let t = await o(e.config), n = v(t.locales);
|
|
862
|
+
j.info(`Extracting messages from ${t.include.join(", ")}`);
|
|
863
|
+
let r = await k(t.include, {
|
|
993
864
|
ignore: t.exclude ?? [],
|
|
994
865
|
absolute: !1
|
|
995
|
-
}), i = [], a = e["no-cache"] ?? !1 ? null : new
|
|
866
|
+
}), i = [], a = e["no-cache"] ?? !1 ? null : new d(t.catalogDir, Y(process.cwd())), s = 0;
|
|
996
867
|
for (let e of r) {
|
|
997
868
|
if (a) {
|
|
998
869
|
let t = a.get(e);
|
|
@@ -1001,16 +872,16 @@ var Z = O({
|
|
|
1001
872
|
continue;
|
|
1002
873
|
}
|
|
1003
874
|
}
|
|
1004
|
-
let n = await
|
|
875
|
+
let n = await Fe(e, x(e, "utf-8"), t.idGenerator);
|
|
1005
876
|
i.push(...n), a && a.set(e, n);
|
|
1006
877
|
}
|
|
1007
|
-
a && (a.prune(new Set(r)), a.save()), s > 0 ?
|
|
1008
|
-
let c = t.format === "json" ? ".json" : ".po", l = e.clean ?? !1,
|
|
878
|
+
a && (a.prune(new Set(r)), a.save()), s > 0 ? j.info(`Found ${i.length} messages in ${r.length} files (${s} cached)`) : j.info(`Found ${i.length} messages in ${r.length} files`);
|
|
879
|
+
let c = t.format === "json" ? ".json" : ".po", l = e.clean ?? !1, u = e["no-fuzzy"] ?? !1;
|
|
1009
880
|
for (let e of n) {
|
|
1010
|
-
let n =
|
|
1011
|
-
|
|
1012
|
-
let
|
|
1013
|
-
|
|
881
|
+
let n = D(t.catalogDir, `${e}${c}`), { catalog: r, result: a } = f(X(n, t.format), i, { stripFuzzy: u });
|
|
882
|
+
Z(n, l ? Object.fromEntries(Object.entries(r).filter(([, e]) => !e.obsolete)) : r, t.format);
|
|
883
|
+
let o = l ? `${a.obsolete} removed` : `${a.obsolete} obsolete`;
|
|
884
|
+
j.success(`${e}: ${a.added} added, ${a.unchanged} unchanged, ${o}`);
|
|
1014
885
|
}
|
|
1015
886
|
for (let e of t.plugins ?? []) await e.onAfterExtract?.({
|
|
1016
887
|
messages: new Map(i.map((e) => [e.id, e])),
|
|
@@ -1020,13 +891,13 @@ var Z = O({
|
|
|
1020
891
|
});
|
|
1021
892
|
}
|
|
1022
893
|
});
|
|
1023
|
-
function
|
|
894
|
+
function Le(e) {
|
|
1024
895
|
let t = {};
|
|
1025
896
|
for (let [n, r] of Object.entries(e)) r.translation && r.translation.length > 0 ? t[n] = r.translation : r.message && (t[n] = r.message);
|
|
1026
897
|
return t;
|
|
1027
898
|
}
|
|
1028
899
|
async function Q(e, t, n) {
|
|
1029
|
-
let r =
|
|
900
|
+
let r = Le(e);
|
|
1030
901
|
for (let e of n) e.transformMessages && (r = await e.transformMessages(r, t));
|
|
1031
902
|
let i = {};
|
|
1032
903
|
for (let [t, n] of Object.entries(e)) {
|
|
@@ -1048,7 +919,7 @@ function $(e, t, n, r) {
|
|
|
1048
919
|
config: r
|
|
1049
920
|
};
|
|
1050
921
|
}
|
|
1051
|
-
var
|
|
922
|
+
var Re = M({
|
|
1052
923
|
meta: {
|
|
1053
924
|
name: "compile",
|
|
1054
925
|
description: "Compile message catalogs to JS modules"
|
|
@@ -1079,70 +950,70 @@ var Ve = O({
|
|
|
1079
950
|
}
|
|
1080
951
|
},
|
|
1081
952
|
async run({ args: i }) {
|
|
1082
|
-
let a = await
|
|
1083
|
-
|
|
1084
|
-
let
|
|
1085
|
-
for (let e of
|
|
1086
|
-
let t =
|
|
1087
|
-
if (
|
|
1088
|
-
let n =
|
|
1089
|
-
|
|
1090
|
-
} else
|
|
953
|
+
let a = await o(i.config), c = v(a.locales), u = a.format === "json" ? ".json" : ".po";
|
|
954
|
+
b(a.compileOutDir, { recursive: !0 });
|
|
955
|
+
let d = {}, f = {};
|
|
956
|
+
for (let e of c) {
|
|
957
|
+
let t = D(a.catalogDir, `${e}${u}`);
|
|
958
|
+
if (y(t)) {
|
|
959
|
+
let n = x(t, "utf-8");
|
|
960
|
+
f[e] = n, d[e] = a.format === "json" ? s(n) : l(n);
|
|
961
|
+
} else f[e] = "", d[e] = {};
|
|
1091
962
|
}
|
|
1092
|
-
let
|
|
1093
|
-
|
|
1094
|
-
let
|
|
1095
|
-
if (
|
|
1096
|
-
|
|
963
|
+
let p = r(d);
|
|
964
|
+
j.info(`Compiling ${p.length} messages across ${c.length} locales`);
|
|
965
|
+
let h = i["skip-fuzzy"] ?? !1, g = i["no-cache"] ?? !1 ? null : new pe(a.catalogDir, Y(process.cwd())), _ = i.parallel ?? !1, S = i.concurrency ? parseInt(i.concurrency, 10) : void 0;
|
|
966
|
+
if (S !== void 0 && (isNaN(S) || S < 1)) {
|
|
967
|
+
j.error("Invalid --concurrency. Must be a positive integer."), process.exitCode = 1;
|
|
1097
968
|
return;
|
|
1098
969
|
}
|
|
1099
|
-
let
|
|
1100
|
-
for (let e of
|
|
1101
|
-
if (
|
|
1102
|
-
|
|
970
|
+
let w = 0, T = !1, E = [];
|
|
971
|
+
for (let e of c) {
|
|
972
|
+
if (g && g.isUpToDate(e, f[e]) && y(D(a.compileOutDir, `${e}.js`))) {
|
|
973
|
+
w++;
|
|
1103
974
|
continue;
|
|
1104
975
|
}
|
|
1105
|
-
|
|
976
|
+
E.push(e);
|
|
1106
977
|
}
|
|
1107
|
-
if (
|
|
978
|
+
if (E.length > 0 && (T = !0), _ && E.length > 1) {
|
|
1108
979
|
let e = a.plugins ?? [], t = {};
|
|
1109
|
-
for (let n of
|
|
1110
|
-
for (let t of e) await t.onBeforeCompile?.($(n,
|
|
1111
|
-
t[n] = e.length > 0 ? await Q(
|
|
980
|
+
for (let n of E) {
|
|
981
|
+
for (let t of e) await t.onBeforeCompile?.($(n, d[n], a.compileOutDir, a));
|
|
982
|
+
t[n] = e.length > 0 ? await Q(d[n], n, e) : d[n];
|
|
1112
983
|
}
|
|
1113
|
-
let n = await
|
|
984
|
+
let n = await m(E.map((e) => ({
|
|
1114
985
|
locale: e,
|
|
1115
986
|
catalog: t[e],
|
|
1116
|
-
allIds:
|
|
987
|
+
allIds: p,
|
|
1117
988
|
sourceLocale: a.sourceLocale,
|
|
1118
|
-
options: { skipFuzzy:
|
|
1119
|
-
})),
|
|
989
|
+
options: { skipFuzzy: h }
|
|
990
|
+
})), S);
|
|
1120
991
|
for (let e of n) {
|
|
1121
|
-
let t =
|
|
1122
|
-
if (
|
|
1123
|
-
|
|
1124
|
-
for (let t of e.stats.missing)
|
|
1125
|
-
} else
|
|
992
|
+
let t = D(a.compileOutDir, `${e.locale}.js`);
|
|
993
|
+
if (C(t, e.code, "utf-8"), g && g.set(e.locale, f[e.locale]), e.stats.missing.length > 0) {
|
|
994
|
+
j.warn(`${e.locale}: ${e.stats.compiled} compiled, ${e.stats.missing.length} missing translations`);
|
|
995
|
+
for (let t of e.stats.missing) j.warn(` ⤷ ${t}`);
|
|
996
|
+
} else j.success(`Compiled ${e.locale}: ${e.stats.compiled} messages → ${t}`);
|
|
1126
997
|
}
|
|
1127
|
-
for (let n of
|
|
998
|
+
for (let n of E) for (let r of e) await r.onAfterCompile?.($(n, t[n], a.compileOutDir, a));
|
|
1128
999
|
} else {
|
|
1129
1000
|
let e = a.plugins ?? [];
|
|
1130
|
-
for (let n of
|
|
1131
|
-
let r =
|
|
1132
|
-
for (let t of e) await t.onBeforeCompile?.($(n,
|
|
1133
|
-
let i = e.length > 0 ? await Q(
|
|
1134
|
-
if (
|
|
1135
|
-
|
|
1136
|
-
for (let e of s.missing)
|
|
1137
|
-
} else
|
|
1001
|
+
for (let n of E) {
|
|
1002
|
+
let r = D(a.compileOutDir, `${n}.js`);
|
|
1003
|
+
for (let t of e) await t.onBeforeCompile?.($(n, d[n], a.compileOutDir, a));
|
|
1004
|
+
let i = e.length > 0 ? await Q(d[n], n, e) : d[n], { code: o, stats: s } = t(i, n, p, a.sourceLocale, { skipFuzzy: h });
|
|
1005
|
+
if (C(r, o, "utf-8"), g && g.set(n, f[n]), s.missing.length > 0) {
|
|
1006
|
+
j.warn(`${n}: ${s.compiled} compiled, ${s.missing.length} missing translations`);
|
|
1007
|
+
for (let e of s.missing) j.warn(` ⤷ ${e}`);
|
|
1008
|
+
} else j.success(`Compiled ${n}: ${s.compiled} messages → ${r}`);
|
|
1138
1009
|
for (let t of e) await t.onAfterCompile?.($(n, i, a.compileOutDir, a));
|
|
1139
1010
|
}
|
|
1140
1011
|
}
|
|
1141
|
-
|
|
1142
|
-
let O =
|
|
1143
|
-
(
|
|
1012
|
+
w > 0 && j.info(`${w} locale(s) unchanged — skipped`), g && g.save();
|
|
1013
|
+
let O = D(a.compileOutDir, "index.js"), k = D(a.compileOutDir, "messages.d.ts");
|
|
1014
|
+
(T || !y(O)) && (C(O, n(c, a.compileOutDir), "utf-8"), j.success(`Generated index → ${O}`)), (T || !y(k)) && (C(k, e(p, d, a.sourceLocale), "utf-8"), j.success(`Generated types → ${k}`));
|
|
1144
1015
|
}
|
|
1145
|
-
}),
|
|
1016
|
+
}), ze = M({
|
|
1146
1017
|
meta: {
|
|
1147
1018
|
name: "stats",
|
|
1148
1019
|
description: "Show translation progress"
|
|
@@ -1152,9 +1023,9 @@ var Ve = O({
|
|
|
1152
1023
|
description: "Path to config file"
|
|
1153
1024
|
} },
|
|
1154
1025
|
async run({ args: e }) {
|
|
1155
|
-
let t = await
|
|
1026
|
+
let t = await o(e.config), n = v(t.locales), r = t.format === "json" ? ".json" : ".po", i = [];
|
|
1156
1027
|
for (let e of n) {
|
|
1157
|
-
let n =
|
|
1028
|
+
let n = X(D(t.catalogDir, `${e}${r}`), t.format), a = Object.values(n).filter((e) => !e.obsolete), o = a.length, s = a.filter((e) => e.translation && e.translation.length > 0).length, c = o > 0 ? (s / o * 100).toFixed(1) + "%" : "—";
|
|
1158
1029
|
i.push({
|
|
1159
1030
|
locale: e,
|
|
1160
1031
|
total: o,
|
|
@@ -1162,11 +1033,11 @@ var Ve = O({
|
|
|
1162
1033
|
pct: c
|
|
1163
1034
|
});
|
|
1164
1035
|
}
|
|
1165
|
-
|
|
1166
|
-
for (let e of i)
|
|
1167
|
-
|
|
1036
|
+
j.log(""), j.log(" Locale │ Total │ Translated │ Progress"), j.log(" ────────┼───────┼────────────┼─────────────────────────────");
|
|
1037
|
+
for (let e of i) j.log(ae(e.locale, e.total, e.translated));
|
|
1038
|
+
j.log("");
|
|
1168
1039
|
}
|
|
1169
|
-
}),
|
|
1040
|
+
}), Be = M({
|
|
1170
1041
|
meta: {
|
|
1171
1042
|
name: "lint",
|
|
1172
1043
|
description: "Check translation quality (missing, inconsistent placeholders, fuzzy)"
|
|
@@ -1187,21 +1058,21 @@ var Ve = O({
|
|
|
1187
1058
|
}
|
|
1188
1059
|
},
|
|
1189
1060
|
async run({ args: e }) {
|
|
1190
|
-
let t = await
|
|
1191
|
-
for (let e of n) i[e] =
|
|
1061
|
+
let t = await o(e.config), n = v(t.locales), r = t.format === "json" ? ".json" : ".po", i = {};
|
|
1062
|
+
for (let e of n) i[e] = X(D(t.catalogDir, `${e}${r}`), t.format);
|
|
1192
1063
|
let a = e.locale ? [e.locale] : void 0;
|
|
1193
|
-
|
|
1194
|
-
let
|
|
1064
|
+
j.info(`Linting ${a ? a.join(", ") : "all locales"} (source: ${t.sourceLocale})`);
|
|
1065
|
+
let s = {
|
|
1195
1066
|
sourceLocale: t.sourceLocale,
|
|
1196
1067
|
strict: e.strict ?? !1
|
|
1197
1068
|
};
|
|
1198
|
-
a && (
|
|
1199
|
-
let
|
|
1200
|
-
|
|
1201
|
-
let
|
|
1202
|
-
(
|
|
1069
|
+
a && (s.locales = a);
|
|
1070
|
+
let c = B(i, s);
|
|
1071
|
+
j.log(""), j.log(se(c)), j.log("");
|
|
1072
|
+
let l = c.filter((e) => e.severity === "error"), u = c.filter((e) => e.severity === "warning");
|
|
1073
|
+
(l.length > 0 || e.strict && u.length > 0) && (process.exitCode = 1);
|
|
1203
1074
|
}
|
|
1204
|
-
}),
|
|
1075
|
+
}), Ve = M({
|
|
1205
1076
|
meta: {
|
|
1206
1077
|
name: "check",
|
|
1207
1078
|
description: "Check translation coverage for CI"
|
|
@@ -1231,35 +1102,35 @@ var Ve = O({
|
|
|
1231
1102
|
}
|
|
1232
1103
|
},
|
|
1233
1104
|
async run({ args: e }) {
|
|
1234
|
-
let t = await
|
|
1235
|
-
for (let e of n) i[e] =
|
|
1105
|
+
let t = await o(e.config), n = v(t.locales), r = t.format === "json" ? ".json" : ".po", i = {};
|
|
1106
|
+
for (let e of n) i[e] = X(D(t.catalogDir, `${e}${r}`), t.format);
|
|
1236
1107
|
let a = parseFloat(e["min-coverage"] ?? "100");
|
|
1237
1108
|
if (isNaN(a) || a < 0 || a > 100) {
|
|
1238
|
-
|
|
1109
|
+
j.error("Invalid --min-coverage. Must be a number between 0 and 100."), process.exitCode = 1;
|
|
1239
1110
|
return;
|
|
1240
1111
|
}
|
|
1241
|
-
let
|
|
1112
|
+
let s = e.format ?? (e.ci ? "github" : "text"), c = {
|
|
1242
1113
|
sourceLocale: t.sourceLocale,
|
|
1243
1114
|
minCoverage: a,
|
|
1244
|
-
format:
|
|
1115
|
+
format: s
|
|
1245
1116
|
};
|
|
1246
|
-
e.locale && (
|
|
1247
|
-
let
|
|
1248
|
-
switch (
|
|
1117
|
+
e.locale && (c.locale = e.locale);
|
|
1118
|
+
let l = le(i, c);
|
|
1119
|
+
switch (s) {
|
|
1249
1120
|
case "json":
|
|
1250
|
-
|
|
1121
|
+
j.log(fe(l));
|
|
1251
1122
|
break;
|
|
1252
1123
|
case "github":
|
|
1253
|
-
|
|
1124
|
+
j.log(de(l, t.catalogDir, t.format));
|
|
1254
1125
|
break;
|
|
1255
1126
|
default:
|
|
1256
|
-
|
|
1127
|
+
j.log(""), j.log(ue(l)), j.log("");
|
|
1257
1128
|
break;
|
|
1258
1129
|
}
|
|
1259
|
-
|
|
1130
|
+
l.passed || (process.exitCode = 1);
|
|
1260
1131
|
}
|
|
1261
1132
|
});
|
|
1262
|
-
async function
|
|
1133
|
+
async function He(e, t, n) {
|
|
1263
1134
|
let r = Array(e.length), i = 0, a = Array.from({ length: Math.min(n, e.length) }, async () => {
|
|
1264
1135
|
for (; i < e.length;) {
|
|
1265
1136
|
let n = i++;
|
|
@@ -1268,7 +1139,7 @@ async function Ge(e, t, n) {
|
|
|
1268
1139
|
});
|
|
1269
1140
|
return await Promise.all(a), r;
|
|
1270
1141
|
}
|
|
1271
|
-
var
|
|
1142
|
+
var Ue = M({
|
|
1272
1143
|
meta: {
|
|
1273
1144
|
name: "translate",
|
|
1274
1145
|
description: "Translate messages using AI (Claude Code or Codex CLI)"
|
|
@@ -1315,58 +1186,58 @@ var Ke = O({
|
|
|
1315
1186
|
}
|
|
1316
1187
|
},
|
|
1317
1188
|
async run({ args: e }) {
|
|
1318
|
-
let t = await
|
|
1189
|
+
let t = await o(e.config), n = v(t.locales), r = e.provider;
|
|
1319
1190
|
if (r !== "claude" && r !== "codex") {
|
|
1320
|
-
|
|
1191
|
+
j.error(`Invalid provider "${r}". Use "claude" or "codex".`);
|
|
1321
1192
|
return;
|
|
1322
1193
|
}
|
|
1323
1194
|
let i = parseInt(e["batch-size"] ?? "50", 10);
|
|
1324
1195
|
if (isNaN(i) || i < 1) {
|
|
1325
|
-
|
|
1196
|
+
j.error("Invalid batch-size. Must be a positive integer.");
|
|
1326
1197
|
return;
|
|
1327
1198
|
}
|
|
1328
|
-
let a = e.glossary ?
|
|
1329
|
-
if (isNaN(
|
|
1330
|
-
|
|
1199
|
+
let a = e.glossary ? ye(D(e.glossary)) : void 0, s = e.concurrency ? parseInt(e.concurrency, 10) : 3;
|
|
1200
|
+
if (isNaN(s) || s < 1) {
|
|
1201
|
+
j.error("Invalid concurrency. Must be a positive integer.");
|
|
1331
1202
|
return;
|
|
1332
1203
|
}
|
|
1333
|
-
let
|
|
1334
|
-
if (isNaN(
|
|
1335
|
-
|
|
1204
|
+
let c = e.timeout ? parseInt(e.timeout, 10) : 120;
|
|
1205
|
+
if (isNaN(c) || c < 1) {
|
|
1206
|
+
j.error("Invalid timeout. Must be a positive integer (seconds).");
|
|
1336
1207
|
return;
|
|
1337
1208
|
}
|
|
1338
|
-
let
|
|
1339
|
-
if (
|
|
1340
|
-
|
|
1209
|
+
let l = c * 1e3, u = e.locale ? [e.locale] : n.filter((e) => e !== t.sourceLocale);
|
|
1210
|
+
if (u.length === 0) {
|
|
1211
|
+
j.warn("No target locales to translate.");
|
|
1341
1212
|
return;
|
|
1342
1213
|
}
|
|
1343
|
-
|
|
1214
|
+
j.info(`Translating with ${r} (batch size: ${i})`);
|
|
1344
1215
|
let d = t.format === "json" ? ".json" : ".po";
|
|
1345
|
-
await
|
|
1346
|
-
|
|
1347
|
-
let o =
|
|
1216
|
+
await He(u, async (n) => {
|
|
1217
|
+
j.info(`\n[${n}]`);
|
|
1218
|
+
let o = D(t.catalogDir, `${n}${d}`), s = X(o, t.format);
|
|
1348
1219
|
if (e["dry-run"]) {
|
|
1349
1220
|
let e = Object.entries(s).filter(([, e]) => !e.obsolete && (!e.translation || e.translation.length === 0));
|
|
1350
1221
|
if (e.length > 0) {
|
|
1351
|
-
for (let [t, n] of e)
|
|
1352
|
-
|
|
1353
|
-
} else
|
|
1222
|
+
for (let [t, n] of e) j.log(` ${t}: ${n.message ?? t}`);
|
|
1223
|
+
j.success(` ${n}: ${e.length} messages would be translated (dry-run)`);
|
|
1224
|
+
} else j.success(` ${n}: already fully translated`);
|
|
1354
1225
|
return;
|
|
1355
1226
|
}
|
|
1356
|
-
let
|
|
1227
|
+
let c = a ? be(a, n) : void 0, { catalog: u, translated: f, warnings: p } = await De({
|
|
1357
1228
|
provider: r,
|
|
1358
1229
|
sourceLocale: t.sourceLocale,
|
|
1359
1230
|
targetLocale: n,
|
|
1360
1231
|
catalog: s,
|
|
1361
1232
|
batchSize: i,
|
|
1362
|
-
glossary:
|
|
1363
|
-
timeoutMs:
|
|
1233
|
+
glossary: c,
|
|
1234
|
+
timeoutMs: l,
|
|
1364
1235
|
...e.context ? { context: e.context } : {}
|
|
1365
1236
|
});
|
|
1366
|
-
f > 0 ? (
|
|
1367
|
-
},
|
|
1237
|
+
f > 0 ? (Z(o, u, t.format), j.success(` ${n}: ${f} messages translated`)) : j.success(` ${n}: already fully translated`), p.length > 0 && j.warn(` ${n}: ${p.length} QA warnings`);
|
|
1238
|
+
}, s);
|
|
1368
1239
|
}
|
|
1369
|
-
}),
|
|
1240
|
+
}), We = M({
|
|
1370
1241
|
meta: {
|
|
1371
1242
|
name: "migrate",
|
|
1372
1243
|
description: "Migrate from another i18n library using AI"
|
|
@@ -1391,40 +1262,96 @@ var Ke = O({
|
|
|
1391
1262
|
async run({ args: e }) {
|
|
1392
1263
|
let t = e.provider;
|
|
1393
1264
|
if (t !== "claude" && t !== "codex") {
|
|
1394
|
-
|
|
1265
|
+
j.error(`Invalid provider "${t}". Use "claude" or "codex".`);
|
|
1395
1266
|
return;
|
|
1396
1267
|
}
|
|
1397
|
-
await
|
|
1268
|
+
await Pe({
|
|
1398
1269
|
from: e.from,
|
|
1399
1270
|
provider: t,
|
|
1400
1271
|
write: e.write ?? !1
|
|
1401
1272
|
});
|
|
1402
1273
|
}
|
|
1403
1274
|
});
|
|
1404
|
-
|
|
1275
|
+
ee(M({
|
|
1405
1276
|
meta: {
|
|
1406
1277
|
name: "fluenti",
|
|
1407
1278
|
version: "0.0.1",
|
|
1408
1279
|
description: "Compile-time i18n for modern frameworks"
|
|
1409
1280
|
},
|
|
1410
1281
|
subCommands: {
|
|
1411
|
-
init:
|
|
1282
|
+
init: M({
|
|
1412
1283
|
meta: {
|
|
1413
1284
|
name: "init",
|
|
1414
1285
|
description: "Initialize Fluenti in your project"
|
|
1415
1286
|
},
|
|
1416
1287
|
args: {},
|
|
1417
1288
|
async run() {
|
|
1418
|
-
await
|
|
1289
|
+
await p({ cwd: process.cwd() });
|
|
1290
|
+
}
|
|
1291
|
+
}),
|
|
1292
|
+
doctor: M({
|
|
1293
|
+
meta: {
|
|
1294
|
+
name: "doctor",
|
|
1295
|
+
description: "Diagnose Fluenti setup and vNext import issues"
|
|
1296
|
+
},
|
|
1297
|
+
args: {
|
|
1298
|
+
config: {
|
|
1299
|
+
type: "string",
|
|
1300
|
+
description: "Optional path to fluenti config file"
|
|
1301
|
+
},
|
|
1302
|
+
strict: {
|
|
1303
|
+
type: "boolean",
|
|
1304
|
+
description: "Treat warnings as errors",
|
|
1305
|
+
default: !1
|
|
1306
|
+
}
|
|
1307
|
+
},
|
|
1308
|
+
async run({ args: e }) {
|
|
1309
|
+
let t = await u({
|
|
1310
|
+
cwd: process.cwd(),
|
|
1311
|
+
...e.config ? { config: e.config } : {}
|
|
1312
|
+
}), n = h(t);
|
|
1313
|
+
j.log(n);
|
|
1314
|
+
let r = t.findings.some((e) => e.severity === "error"), i = t.findings.some((e) => e.severity === "warning");
|
|
1315
|
+
(r || (e.strict ?? !1) && i) && (process.exitCode = 1);
|
|
1316
|
+
}
|
|
1317
|
+
}),
|
|
1318
|
+
codemod: M({
|
|
1319
|
+
meta: {
|
|
1320
|
+
name: "codemod",
|
|
1321
|
+
description: "Rewrite imports to the vNext Fluenti entry layout"
|
|
1322
|
+
},
|
|
1323
|
+
args: {
|
|
1324
|
+
write: {
|
|
1325
|
+
type: "boolean",
|
|
1326
|
+
description: "Write modified files to disk",
|
|
1327
|
+
default: !1
|
|
1328
|
+
},
|
|
1329
|
+
include: {
|
|
1330
|
+
type: "string",
|
|
1331
|
+
description: "Comma-separated glob list to scan"
|
|
1332
|
+
}
|
|
1333
|
+
},
|
|
1334
|
+
async run({ args: e }) {
|
|
1335
|
+
let t = e.include ? e.include.split(",").map((e) => e.trim()).filter(Boolean) : void 0, n = await a({
|
|
1336
|
+
cwd: process.cwd(),
|
|
1337
|
+
...t ? { include: t } : {},
|
|
1338
|
+
write: e.write ?? !1
|
|
1339
|
+
});
|
|
1340
|
+
if (n.changedCount === 0) {
|
|
1341
|
+
j.success("No files need import rewrites.");
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1344
|
+
for (let t of n.changedFiles) j.log(`${e.write ? "updated" : "would update"} ${t.file}`);
|
|
1345
|
+
j.success(`${e.write ? "Updated" : "Detected"} ${n.changedCount} file(s) with Fluenti import rewrites.`);
|
|
1419
1346
|
}
|
|
1420
1347
|
}),
|
|
1421
|
-
extract:
|
|
1422
|
-
compile:
|
|
1423
|
-
stats:
|
|
1424
|
-
lint:
|
|
1425
|
-
check:
|
|
1426
|
-
translate:
|
|
1427
|
-
migrate:
|
|
1348
|
+
extract: Ie,
|
|
1349
|
+
compile: Re,
|
|
1350
|
+
stats: ze,
|
|
1351
|
+
lint: Be,
|
|
1352
|
+
check: Ve,
|
|
1353
|
+
translate: Ue,
|
|
1354
|
+
migrate: We
|
|
1428
1355
|
}
|
|
1429
1356
|
}));
|
|
1430
1357
|
//#endregion
|