@fluenti/cli 0.3.4 → 0.4.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-provider.d.ts +4 -3
- package/dist/ai-provider.d.ts.map +1 -1
- package/dist/cli.cjs +13 -12
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +419 -261
- package/dist/cli.js.map +1 -1
- package/dist/{compile-CXReVuTG.js → compile-CdA4EZ-p.js} +2 -2
- package/dist/compile-CdA4EZ-p.js.map +1 -0
- package/dist/{compile-CX1b_JVQ.cjs → compile-kXClO6q4.cjs} +2 -2
- package/dist/compile-kXClO6q4.cjs.map +1 -0
- package/dist/compile-worker.cjs +1 -1
- package/dist/compile-worker.js +1 -1
- package/dist/{extract-cache-CGSKwh76.cjs → extract-cache-BioSaoFo.cjs} +2 -2
- package/dist/{extract-cache-CGSKwh76.cjs.map → extract-cache-BioSaoFo.cjs.map} +1 -1
- package/dist/{extract-cache-BTxWgic2.js → extract-cache-C-MI1_ll.js} +3 -3
- package/dist/{extract-cache-BTxWgic2.js.map → extract-cache-C-MI1_ll.js.map} +1 -1
- package/dist/glossary.d.ts.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +2 -2
- package/dist/migrate.d.ts.map +1 -1
- package/dist/translate-parse.d.ts +10 -0
- package/dist/translate-parse.d.ts.map +1 -0
- package/dist/translate-prompt.d.ts +9 -0
- package/dist/translate-prompt.d.ts.map +1 -0
- package/dist/translate.d.ts +9 -8
- package/dist/translate.d.ts.map +1 -1
- package/dist/{tsx-extractor-BOD7JJQK.cjs → tsx-extractor-B0vFXziu.cjs} +2 -2
- package/dist/{tsx-extractor-BOD7JJQK.cjs.map → tsx-extractor-B0vFXziu.cjs.map} +1 -1
- package/dist/vue-extractor.cjs +1 -1
- package/package.json +2 -2
- package/dist/compile-CX1b_JVQ.cjs.map +0 -1
- package/dist/compile-CXReVuTG.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,38 +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-CdA4EZ-p.js";
|
|
3
3
|
import { t as i } from "./tsx-extractor-C-HZNobu.js";
|
|
4
|
-
import { a, c as o, i as s, n as c, o as l, r as u, s as d, t as f } from "./extract-cache-
|
|
4
|
+
import { a, c as o, i as s, n as c, o as l, r as u, s as d, t as f } from "./extract-cache-C-MI1_ll.js";
|
|
5
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,
|
|
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
|
|
15
|
-
import { setTimeout as
|
|
6
|
+
import { appendFileSync as h, existsSync as g, mkdirSync as _, readFileSync as v, statSync as y, writeFileSync as b } from "node:fs";
|
|
7
|
+
import { dirname as x, extname as S, join as C, resolve as w } from "node:path";
|
|
8
|
+
import { fileURLToPath as T } from "node:url";
|
|
9
|
+
import E from "fast-glob";
|
|
10
|
+
import { createHash as D } from "node:crypto";
|
|
11
|
+
import { defineCommand as O, runMain as k } from "citty";
|
|
12
|
+
import A from "consola";
|
|
13
|
+
import { execFile as j } from "node:child_process";
|
|
14
|
+
import { promisify as M } from "node:util";
|
|
15
|
+
import { setTimeout as N } from "node:timers/promises";
|
|
16
16
|
//#region src/stats-format.ts
|
|
17
|
-
var
|
|
18
|
-
function
|
|
17
|
+
var ee = "█", te = "░";
|
|
18
|
+
function ne(e, t = 20) {
|
|
19
19
|
let n = Math.round(Math.max(0, Math.min(100, e)) / 100 * t);
|
|
20
|
-
return
|
|
20
|
+
return ee.repeat(n) + te.repeat(t - n);
|
|
21
21
|
}
|
|
22
|
-
function
|
|
22
|
+
function re(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
26
|
function ie(e, t, n) {
|
|
27
|
-
let r = t > 0 ? n / t * 100 : 0, i = t > 0 ?
|
|
27
|
+
let r = t > 0 ? n / t * 100 : 0, i = t > 0 ? re(r) : "—", a = t > 0 ? ne(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
|
-
|
|
32
|
+
var P = /\{(\w+),\s*(plural|select|selectordinal)\s*,/;
|
|
33
|
+
function F(e) {
|
|
33
34
|
try {
|
|
34
35
|
let t = p(e), n = /* @__PURE__ */ new Set();
|
|
35
|
-
return
|
|
36
|
+
return I(t, n), [...n].sort();
|
|
36
37
|
} catch {
|
|
37
38
|
let t = /* @__PURE__ */ new Set(), n = 0, r = 0;
|
|
38
39
|
for (; r < e.length;) {
|
|
@@ -50,16 +51,37 @@ function N(e) {
|
|
|
50
51
|
return [...t].sort();
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
|
-
function
|
|
54
|
+
function I(e, t) {
|
|
54
55
|
for (let n of e) if (n.type === "variable" && n.name !== "#") t.add(n.name);
|
|
55
56
|
else if (n.type === "plural" || n.type === "select") {
|
|
56
57
|
t.add(n.variable);
|
|
57
|
-
for (let e of Object.values(n.options))
|
|
58
|
+
for (let e of Object.values(n.options)) I(e, t);
|
|
58
59
|
} else n.type === "function" && t.add(n.variable);
|
|
59
60
|
}
|
|
61
|
+
function L(e) {
|
|
62
|
+
let t = /* @__PURE__ */ new Set(), n = /<\/?([a-zA-Z][\w-]*)[^>]*>/g, r;
|
|
63
|
+
for (; (r = n.exec(e)) !== null;) t.add(r[1].toLowerCase());
|
|
64
|
+
return [...t].sort();
|
|
65
|
+
}
|
|
66
|
+
function ae(e, t) {
|
|
67
|
+
let n = F(e), r = F(t), i = L(e), a = L(t), o = n.filter((e) => !r.includes(e)), s = r.filter((e) => !n.includes(e)), c = i.filter((e) => !a.includes(e)), l = a.filter((e) => !i.includes(e)), u = [];
|
|
68
|
+
if (P.test(t)) try {
|
|
69
|
+
p(t);
|
|
70
|
+
} catch (e) {
|
|
71
|
+
u.push(e.message);
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
valid: o.length === 0 && s.length === 0 && c.length === 0 && l.length === 0 && u.length === 0,
|
|
75
|
+
missingPlaceholders: o,
|
|
76
|
+
extraPlaceholders: s,
|
|
77
|
+
missingHtmlTags: c,
|
|
78
|
+
extraHtmlTags: l,
|
|
79
|
+
syntaxErrors: u
|
|
80
|
+
};
|
|
81
|
+
}
|
|
60
82
|
//#endregion
|
|
61
83
|
//#region src/lint.ts
|
|
62
|
-
function
|
|
84
|
+
function R(e, t) {
|
|
63
85
|
let n = [], { sourceLocale: r } = t, i = t.locales ?? Object.keys(e), a = e[r];
|
|
64
86
|
if (!a) return n.push({
|
|
65
87
|
rule: "missing-source",
|
|
@@ -91,7 +113,7 @@ function F(e, t) {
|
|
|
91
113
|
});
|
|
92
114
|
continue;
|
|
93
115
|
}
|
|
94
|
-
let s =
|
|
116
|
+
let s = F(r.message ?? e), c = F(o.translation), l = s.filter((e) => !c.includes(e)), u = c.filter((e) => !s.includes(e));
|
|
95
117
|
l.length > 0 && n.push({
|
|
96
118
|
rule: "inconsistent-placeholders",
|
|
97
119
|
severity: "error",
|
|
@@ -134,9 +156,9 @@ function F(e, t) {
|
|
|
134
156
|
});
|
|
135
157
|
return n;
|
|
136
158
|
}
|
|
137
|
-
function
|
|
159
|
+
function oe(e) {
|
|
138
160
|
if (e.length === 0) return " ✓ All checks passed";
|
|
139
|
-
let t = [], n =
|
|
161
|
+
let t = [], n = se(e, (e) => e.rule);
|
|
140
162
|
for (let [e, r] of Object.entries(n)) {
|
|
141
163
|
t.push(` ${e} (${r.length}):`);
|
|
142
164
|
for (let e of r) {
|
|
@@ -147,7 +169,7 @@ function ae(e) {
|
|
|
147
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;
|
|
148
170
|
return t.push(""), t.push(` Summary: ${r} errors, ${i} warnings, ${a} info`), t.join("\n");
|
|
149
171
|
}
|
|
150
|
-
function
|
|
172
|
+
function se(e, t) {
|
|
151
173
|
let n = {};
|
|
152
174
|
for (let r of e) {
|
|
153
175
|
let e = t(r);
|
|
@@ -157,7 +179,7 @@ function oe(e, t) {
|
|
|
157
179
|
}
|
|
158
180
|
//#endregion
|
|
159
181
|
//#region src/check.ts
|
|
160
|
-
function
|
|
182
|
+
function ce(e, t) {
|
|
161
183
|
let { sourceLocale: n, minCoverage: r, locale: i } = t, a = e[n];
|
|
162
184
|
if (!a) return {
|
|
163
185
|
results: [],
|
|
@@ -205,10 +227,10 @@ function se(e, t) {
|
|
|
205
227
|
passed: p,
|
|
206
228
|
minCoverage: r,
|
|
207
229
|
actualCoverage: f,
|
|
208
|
-
diagnostics:
|
|
230
|
+
diagnostics: R(e, m)
|
|
209
231
|
};
|
|
210
232
|
}
|
|
211
|
-
function
|
|
233
|
+
function le(e) {
|
|
212
234
|
let t = [];
|
|
213
235
|
for (let n of e.results) {
|
|
214
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` : "";
|
|
@@ -218,7 +240,7 @@ function ce(e) {
|
|
|
218
240
|
let n = e.actualCoverage.toFixed(1), r = e.passed ? "PASSED" : "FAILED";
|
|
219
241
|
return t.push(`Coverage: ${n}% (min: ${e.minCoverage}%) — ${r}`), t.join("\n");
|
|
220
242
|
}
|
|
221
|
-
function
|
|
243
|
+
function ue(e, t, n) {
|
|
222
244
|
let r = [], i = n === "json" ? ".json" : ".po";
|
|
223
245
|
for (let n of e.results) if (n.coverage < e.minCoverage) {
|
|
224
246
|
let a = `${t}/${n.locale}${i}`;
|
|
@@ -231,7 +253,7 @@ function le(e, t, n) {
|
|
|
231
253
|
}
|
|
232
254
|
return r.join("\n");
|
|
233
255
|
}
|
|
234
|
-
function
|
|
256
|
+
function de(e) {
|
|
235
257
|
return JSON.stringify({
|
|
236
258
|
results: e.results,
|
|
237
259
|
passed: e.passed,
|
|
@@ -241,109 +263,196 @@ function ue(e) {
|
|
|
241
263
|
}
|
|
242
264
|
//#endregion
|
|
243
265
|
//#region src/compile-cache.ts
|
|
244
|
-
var
|
|
266
|
+
var z = "1", fe = class {
|
|
245
267
|
data;
|
|
246
268
|
cachePath;
|
|
247
269
|
dirty = !1;
|
|
248
270
|
constructor(e, t) {
|
|
249
|
-
this.cachePath =
|
|
271
|
+
this.cachePath = w(t ? w(e, ".cache", t) : w(e, ".cache"), "compile-cache.json"), this.data = this.load();
|
|
250
272
|
}
|
|
251
273
|
isUpToDate(e, t) {
|
|
252
274
|
let n = this.data.entries[e];
|
|
253
275
|
if (!n) return !1;
|
|
254
|
-
let r =
|
|
276
|
+
let r = B(t);
|
|
255
277
|
return n.inputHash === r;
|
|
256
278
|
}
|
|
257
279
|
set(e, t) {
|
|
258
|
-
this.data.entries[e] = { inputHash:
|
|
280
|
+
this.data.entries[e] = { inputHash: B(t) }, this.dirty = !0;
|
|
259
281
|
}
|
|
260
282
|
save() {
|
|
261
283
|
if (this.dirty) try {
|
|
262
|
-
_(
|
|
284
|
+
_(x(this.cachePath), { recursive: !0 }), b(this.cachePath, JSON.stringify(this.data), "utf-8"), this.dirty = !1;
|
|
263
285
|
} catch {}
|
|
264
286
|
}
|
|
265
287
|
load() {
|
|
266
288
|
try {
|
|
267
289
|
if (g(this.cachePath)) {
|
|
268
290
|
let e = v(this.cachePath, "utf-8"), t = JSON.parse(e);
|
|
269
|
-
if (t.version ===
|
|
291
|
+
if (t.version === z) return t;
|
|
270
292
|
}
|
|
271
293
|
} catch {}
|
|
272
294
|
return {
|
|
273
|
-
version:
|
|
295
|
+
version: z,
|
|
274
296
|
entries: {}
|
|
275
297
|
};
|
|
276
298
|
}
|
|
277
299
|
};
|
|
278
|
-
function
|
|
279
|
-
return
|
|
300
|
+
function B(e) {
|
|
301
|
+
return D("md5").update(e).digest("hex");
|
|
280
302
|
}
|
|
281
303
|
//#endregion
|
|
282
304
|
//#region src/ai-provider.ts
|
|
283
|
-
var
|
|
305
|
+
var V = M(j), pe = {
|
|
284
306
|
claude: "npm install -g @anthropic-ai/claude-code",
|
|
285
307
|
codex: "npm install -g @openai/codex"
|
|
286
308
|
};
|
|
287
|
-
function
|
|
309
|
+
function me(e, t) {
|
|
288
310
|
return e === "claude" ? ["-p", t] : [
|
|
289
311
|
"-p",
|
|
290
312
|
t,
|
|
291
313
|
"--full-auto"
|
|
292
314
|
];
|
|
293
315
|
}
|
|
294
|
-
function
|
|
316
|
+
function he(e) {
|
|
295
317
|
return e.code === "ENOENT";
|
|
296
318
|
}
|
|
297
|
-
async function
|
|
298
|
-
let { provider: t, prompt: n, maxRetries: r = 3, initialDelayMs: i = 1e3, maxBuffer: a = 10 * 1024 * 1024 } = e,
|
|
319
|
+
async function ge(e) {
|
|
320
|
+
let { provider: t, prompt: n, maxRetries: r = 3, initialDelayMs: i = 1e3, maxBuffer: a = 10 * 1024 * 1024, timeoutMs: o = 12e4 } = e, s = me(t, n), c = t === "claude" ? "claude" : "codex", l;
|
|
299
321
|
for (let e = 0; e <= r; e++) try {
|
|
300
|
-
let
|
|
322
|
+
let t = V(c, [...s], { maxBuffer: a }), { stdout: n } = await Promise.race([t, N(o).then(() => {
|
|
323
|
+
throw Error(`AI provider timed out after ${o}ms`);
|
|
324
|
+
})]);
|
|
301
325
|
return {
|
|
302
|
-
stdout:
|
|
326
|
+
stdout: n,
|
|
303
327
|
attempts: e + 1
|
|
304
328
|
};
|
|
305
329
|
} catch (n) {
|
|
306
|
-
if (
|
|
307
|
-
|
|
330
|
+
if (he(n)) throw Error(`"${t}" CLI not found. Please install it first:\n ${pe[t]}`);
|
|
331
|
+
if (n instanceof Error && n.message.includes("timed out")) throw n;
|
|
332
|
+
l = n, e < r && await N(i * 2 ** e);
|
|
308
333
|
}
|
|
309
|
-
throw
|
|
334
|
+
throw l;
|
|
310
335
|
}
|
|
311
336
|
//#endregion
|
|
312
|
-
//#region src/
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
return
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
337
|
+
//#region src/glossary.ts
|
|
338
|
+
var H = 1048576;
|
|
339
|
+
function _e(e) {
|
|
340
|
+
if (!g(e)) return {};
|
|
341
|
+
let t = y(e).size;
|
|
342
|
+
if (t > H) throw Error(`Glossary file exceeds maximum size of ${H} bytes (got ${t} bytes)`);
|
|
343
|
+
let n;
|
|
344
|
+
try {
|
|
345
|
+
let t = v(e, "utf-8");
|
|
346
|
+
n = JSON.parse(t);
|
|
347
|
+
} catch (e) {
|
|
348
|
+
let t = e instanceof Error ? e.message : String(e);
|
|
349
|
+
throw Error(`Invalid glossary format: failed to parse JSON — ${t}`);
|
|
350
|
+
}
|
|
351
|
+
if (typeof n != "object" || !n || Array.isArray(n)) throw Error("Invalid glossary format: root must be a plain object");
|
|
352
|
+
for (let [e, t] of Object.entries(n)) {
|
|
353
|
+
if (typeof t != "object" || !t || Array.isArray(t)) throw Error(`Invalid glossary format: value for "${e}" must be a plain object`);
|
|
354
|
+
for (let [n, r] of Object.entries(t)) if (typeof r != "string") throw Error(`Invalid glossary format: "${e}.${n}" must be a string, got ${typeof r}`);
|
|
355
|
+
}
|
|
356
|
+
return n;
|
|
357
|
+
}
|
|
358
|
+
function ve(e, t) {
|
|
359
|
+
let n = {};
|
|
360
|
+
for (let [r, i] of Object.entries(e)) t in i && (n[r] = i[t]);
|
|
361
|
+
return n;
|
|
362
|
+
}
|
|
363
|
+
function U(e) {
|
|
364
|
+
return e.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/[\n\r]/g, " ").replace(/\t/g, " ");
|
|
365
|
+
}
|
|
366
|
+
function ye(e) {
|
|
367
|
+
let t = Object.entries(e);
|
|
368
|
+
return t.length === 0 ? "" : `=== GLOSSARY (use these exact translations) ===\n${[...t].sort(([e], [t]) => e.localeCompare(t)).map(([e, t]) => `"${U(e)}" → "${U(t)}"`).join("\n")}`;
|
|
369
|
+
}
|
|
370
|
+
//#endregion
|
|
371
|
+
//#region src/translate-prompt.ts
|
|
372
|
+
function be(e) {
|
|
373
|
+
let { sourceLocale: t, targetLocale: n, messages: r, glossary: i, context: a } = e, o = JSON.stringify(r, null, 2), s = [
|
|
374
|
+
`You are a professional translator. Translate the following messages from "${t}" to "${n}".`,
|
|
321
375
|
"",
|
|
322
376
|
"Rules:",
|
|
323
|
-
"
|
|
324
|
-
"
|
|
325
|
-
"
|
|
326
|
-
"
|
|
327
|
-
|
|
377
|
+
"1. Output ONLY a valid JSON object with the same keys and translated values.",
|
|
378
|
+
"2. Keep ICU MessageFormat placeholders unchanged: {name}, {count, plural, ...}, {val, number}, etc.",
|
|
379
|
+
"3. Keep HTML tags unchanged: <b>, <a href=\"...\">, </span>, <br/>, etc.",
|
|
380
|
+
"4. Keep numbered rich-text tags unchanged: <0>, </0>, <1/>, etc.",
|
|
381
|
+
"5. Do not add any explanation, markdown formatting, or code fences — output raw JSON only."
|
|
382
|
+
];
|
|
383
|
+
if (i && Object.keys(i).length > 0) {
|
|
384
|
+
let e = ye(i);
|
|
385
|
+
e && s.push("", e);
|
|
386
|
+
}
|
|
387
|
+
return a && s.push("", "=== PROJECT CONTEXT ===", a), s.push("", "Input (JSON):", o), s.join("\n");
|
|
328
388
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
let
|
|
389
|
+
//#endregion
|
|
390
|
+
//#region src/translate-parse.ts
|
|
391
|
+
function xe(e) {
|
|
392
|
+
let t = e.indexOf("{");
|
|
393
|
+
if (t === -1) throw Error("No JSON object found in AI response");
|
|
394
|
+
let n = 0, r = !1, i = !1;
|
|
395
|
+
for (let a = t; a < e.length; a++) {
|
|
396
|
+
let o = e[a];
|
|
397
|
+
if (i) {
|
|
398
|
+
i = !1;
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
if (o === "\\" && r) {
|
|
402
|
+
i = !0;
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
if (o === "\"" && !i) {
|
|
406
|
+
r = !r;
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
if (!r && (o === "{" && n++, o === "}" && (n--, n === 0))) return e.slice(t, a + 1);
|
|
410
|
+
}
|
|
411
|
+
throw Error("Unterminated JSON object in AI response");
|
|
412
|
+
}
|
|
413
|
+
function Se(e) {
|
|
414
|
+
let t = e.match(/```(?:json)?\s*\n?([\s\S]*?)```/);
|
|
415
|
+
return t ? t[1] : e;
|
|
416
|
+
}
|
|
417
|
+
function Ce(e, t) {
|
|
418
|
+
let n = [], r = xe(Se(e)), i;
|
|
333
419
|
try {
|
|
334
|
-
|
|
420
|
+
i = JSON.parse(r);
|
|
335
421
|
} catch {
|
|
336
|
-
throw Error(`Failed to parse JSON from AI response: ${
|
|
422
|
+
throw Error(`Failed to parse JSON from AI response: ${r.slice(0, 200)}`);
|
|
337
423
|
}
|
|
338
|
-
if (typeof
|
|
339
|
-
|
|
424
|
+
if (typeof i != "object" || !i || Array.isArray(i)) throw Error("AI response is not a valid JSON object");
|
|
425
|
+
let a = i, o = {}, s = new Set(Object.keys(t));
|
|
426
|
+
for (let [e, r] of Object.entries(a)) {
|
|
427
|
+
if (!s.has(e)) {
|
|
428
|
+
n.push(`Extra key in AI response (ignored): "${e}"`);
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
if (typeof r != "string") {
|
|
432
|
+
n.push(`Non-string value for key "${e}" (ignored)`);
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
o[e] = r;
|
|
436
|
+
let i = t[e], a = ae(i, r);
|
|
437
|
+
if (!a.valid) {
|
|
438
|
+
let t = [];
|
|
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("; ")}`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
for (let e of s) e in o || n.push(`Missing translation for key: "${e}"`);
|
|
443
|
+
return {
|
|
444
|
+
translations: o,
|
|
445
|
+
warnings: n
|
|
446
|
+
};
|
|
340
447
|
}
|
|
341
|
-
|
|
448
|
+
//#endregion
|
|
449
|
+
//#region src/translate.ts
|
|
450
|
+
function we(e) {
|
|
342
451
|
let t = {};
|
|
343
452
|
for (let [n, r] of Object.entries(e)) r.obsolete || (!r.translation || r.translation.length === 0) && (t[n] = r.message ?? n);
|
|
344
453
|
return t;
|
|
345
454
|
}
|
|
346
|
-
function
|
|
455
|
+
function Te(e, t) {
|
|
347
456
|
let n = Object.keys(e), r = [];
|
|
348
457
|
for (let i = 0; i < n.length; i += t) {
|
|
349
458
|
let a = {};
|
|
@@ -352,34 +461,49 @@ function me(e, t) {
|
|
|
352
461
|
}
|
|
353
462
|
return r;
|
|
354
463
|
}
|
|
355
|
-
async function
|
|
356
|
-
let { provider: t, sourceLocale: n, targetLocale: r, catalog: i, batchSize: a, context: o } = e,
|
|
357
|
-
if (
|
|
464
|
+
async function Ee(e) {
|
|
465
|
+
let { provider: t, sourceLocale: n, targetLocale: r, catalog: i, batchSize: a, context: o, glossary: s, timeoutMs: c } = e, l = we(i), u = Object.keys(l).length;
|
|
466
|
+
if (u === 0) return {
|
|
358
467
|
catalog: { ...i },
|
|
359
|
-
translated: 0
|
|
468
|
+
translated: 0,
|
|
469
|
+
warnings: []
|
|
360
470
|
};
|
|
361
|
-
|
|
362
|
-
let
|
|
363
|
-
for (let e = 0; e <
|
|
364
|
-
let i =
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
471
|
+
A.info(` ${u} untranslated messages, translating with ${t}...`);
|
|
472
|
+
let d = { ...i }, f = Te(l, a), p = 0, m = [];
|
|
473
|
+
for (let e = 0; e < f.length; e++) {
|
|
474
|
+
let i = f[e], a = Object.keys(i);
|
|
475
|
+
f.length > 1 && A.info(` Batch ${e + 1}/${f.length} (${a.length} messages)`);
|
|
476
|
+
try {
|
|
477
|
+
let { stdout: e } = await ge({
|
|
478
|
+
provider: t,
|
|
479
|
+
prompt: be({
|
|
480
|
+
sourceLocale: n,
|
|
481
|
+
targetLocale: r,
|
|
482
|
+
messages: i,
|
|
483
|
+
glossary: s,
|
|
484
|
+
context: o
|
|
485
|
+
}),
|
|
486
|
+
timeoutMs: c
|
|
487
|
+
}), { translations: l, warnings: u } = Ce(e, i);
|
|
488
|
+
for (let e of u) m.push(`[${r}] ${e}`), A.warn(` ${e}`);
|
|
489
|
+
for (let e of a) e in l && (d[e] = {
|
|
490
|
+
...d[e],
|
|
491
|
+
translation: l[e]
|
|
492
|
+
}, p++);
|
|
493
|
+
} catch (t) {
|
|
494
|
+
let n = t instanceof Error ? t.message : String(t);
|
|
495
|
+
m.push(`[${r}] Batch ${e + 1} failed: ${n}`), A.error(` Batch ${e + 1} failed: ${n}`);
|
|
496
|
+
}
|
|
374
497
|
}
|
|
375
498
|
return {
|
|
376
|
-
catalog:
|
|
377
|
-
translated:
|
|
499
|
+
catalog: d,
|
|
500
|
+
translated: p,
|
|
501
|
+
warnings: m
|
|
378
502
|
};
|
|
379
503
|
}
|
|
380
504
|
//#endregion
|
|
381
505
|
//#region src/migrate.ts
|
|
382
|
-
var W = j
|
|
506
|
+
var W = M(j), G = {
|
|
383
507
|
"vue-i18n": {
|
|
384
508
|
name: "vue-i18n",
|
|
385
509
|
framework: "Vue",
|
|
@@ -520,36 +644,36 @@ var W = j(A), G = {
|
|
|
520
644
|
migrationGuide: "react/llms-migration.txt"
|
|
521
645
|
}
|
|
522
646
|
}, K = Object.keys(G);
|
|
523
|
-
function
|
|
647
|
+
function De(e) {
|
|
524
648
|
let t = e.toLowerCase().replace(/^@nuxtjs\//, "nuxt-").replace(/^@/, "");
|
|
525
649
|
return K.find((e) => e === t);
|
|
526
650
|
}
|
|
527
|
-
async function
|
|
651
|
+
async function Oe(e) {
|
|
528
652
|
let t = {
|
|
529
653
|
configFiles: [],
|
|
530
654
|
localeFiles: [],
|
|
531
655
|
sampleSources: [],
|
|
532
656
|
packageJson: void 0
|
|
533
|
-
}, n =
|
|
657
|
+
}, n = w("package.json");
|
|
534
658
|
g(n) && (t.packageJson = v(n, "utf-8"));
|
|
535
659
|
for (let n of e.configPatterns) {
|
|
536
|
-
let e =
|
|
660
|
+
let e = w(n);
|
|
537
661
|
g(e) && t.configFiles.push({
|
|
538
662
|
path: n,
|
|
539
663
|
content: v(e, "utf-8")
|
|
540
664
|
});
|
|
541
665
|
}
|
|
542
|
-
let r = await
|
|
666
|
+
let r = await E(e.localePatterns, { absolute: !1 });
|
|
543
667
|
for (let e of r.slice(0, 10)) {
|
|
544
|
-
let n = v(
|
|
668
|
+
let n = v(w(e), "utf-8");
|
|
545
669
|
t.localeFiles.push({
|
|
546
670
|
path: e,
|
|
547
671
|
content: n.length > 5e3 ? n.slice(0, 5e3) + "\n... (truncated)" : n
|
|
548
672
|
});
|
|
549
673
|
}
|
|
550
|
-
let i = await
|
|
674
|
+
let i = await E(e.sourcePatterns, { absolute: !1 });
|
|
551
675
|
for (let e of i.slice(0, 5)) {
|
|
552
|
-
let n = v(
|
|
676
|
+
let n = v(w(e), "utf-8");
|
|
553
677
|
t.sampleSources.push({
|
|
554
678
|
path: e,
|
|
555
679
|
content: n.length > 3e3 ? n.slice(0, 3e3) + "\n... (truncated)" : n
|
|
@@ -557,16 +681,16 @@ async function _e(e) {
|
|
|
557
681
|
}
|
|
558
682
|
return t;
|
|
559
683
|
}
|
|
560
|
-
function
|
|
561
|
-
let t = typeof __dirname < "u" ? __dirname :
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
684
|
+
function ke(e) {
|
|
685
|
+
let t = typeof __dirname < "u" ? __dirname : x(T(import.meta.url)), n = [
|
|
686
|
+
w("node_modules", "@fluenti", "cli", "..", "..", e),
|
|
687
|
+
C(t, "..", "..", "..", e),
|
|
688
|
+
C(t, "..", "..", e)
|
|
565
689
|
];
|
|
566
690
|
for (let e of n) if (g(e)) return v(e, "utf-8");
|
|
567
691
|
return "";
|
|
568
692
|
}
|
|
569
|
-
function
|
|
693
|
+
function Ae(e, t, n) {
|
|
570
694
|
let r = [];
|
|
571
695
|
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) {
|
|
572
696
|
r.push("=== EXISTING CONFIG FILES ===");
|
|
@@ -582,7 +706,7 @@ function ye(e, t, n) {
|
|
|
582
706
|
}
|
|
583
707
|
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");
|
|
584
708
|
}
|
|
585
|
-
async function
|
|
709
|
+
async function je(e, t) {
|
|
586
710
|
let n = 10 * 1024 * 1024;
|
|
587
711
|
try {
|
|
588
712
|
if (e === "claude") {
|
|
@@ -601,79 +725,79 @@ async function be(e, t) {
|
|
|
601
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;
|
|
602
726
|
}
|
|
603
727
|
}
|
|
604
|
-
function
|
|
605
|
-
let t = {
|
|
728
|
+
function Me(e) {
|
|
729
|
+
let t = 5e5, n = e.length > t ? e.slice(0, t) : e, r = {
|
|
606
730
|
config: void 0,
|
|
607
731
|
localeFiles: [],
|
|
608
732
|
steps: void 0,
|
|
609
733
|
installCommands: void 0
|
|
610
|
-
},
|
|
611
|
-
|
|
612
|
-
let
|
|
613
|
-
if (
|
|
614
|
-
let e = /#### LOCALE:\s*(\S+)\s*\n```(?:po)?\n([\s\S]*?)```/g,
|
|
615
|
-
for (; (
|
|
616
|
-
locale:
|
|
617
|
-
content:
|
|
734
|
+
}, i = n.match(/### FLUENTI_CONFIG[\s\S]*?```(?:ts|typescript)?\n([\s\S]*?)```/);
|
|
735
|
+
i && (r.config = i[1].trim());
|
|
736
|
+
let a = n.match(/### LOCALE_FILES([\s\S]*?)(?=### MIGRATION_STEPS|### INSTALL_COMMANDS|$)/);
|
|
737
|
+
if (a) {
|
|
738
|
+
let e = /#### LOCALE:\s*(\S+)\s*\n```(?:po)?\n([\s\S]*?)```/g, t;
|
|
739
|
+
for (; (t = e.exec(a[1])) !== null;) r.localeFiles.push({
|
|
740
|
+
locale: t[1],
|
|
741
|
+
content: t[2].trim()
|
|
618
742
|
});
|
|
619
743
|
}
|
|
620
|
-
let
|
|
621
|
-
|
|
622
|
-
let
|
|
623
|
-
return
|
|
744
|
+
let o = n.match(/### MIGRATION_STEPS\s*\n([\s\S]*?)(?=### INSTALL_COMMANDS|$)/);
|
|
745
|
+
o && (r.steps = o[1].trim());
|
|
746
|
+
let s = n.match(/### INSTALL_COMMANDS[\s\S]*?```(?:bash|sh)?\n([\s\S]*?)```/);
|
|
747
|
+
return s && (r.installCommands = s[1].trim()), r;
|
|
624
748
|
}
|
|
625
|
-
async function
|
|
626
|
-
let { from: t, provider: n, write: r } = e, i =
|
|
749
|
+
async function Ne(e) {
|
|
750
|
+
let { from: t, provider: n, write: r } = e, i = De(t);
|
|
627
751
|
if (!i) {
|
|
628
|
-
|
|
629
|
-
for (let e of K)
|
|
752
|
+
A.error(`Unsupported library "${t}". Supported libraries:`);
|
|
753
|
+
for (let e of K) A.log(` - ${e}`);
|
|
630
754
|
return;
|
|
631
755
|
}
|
|
632
756
|
let a = G[i];
|
|
633
|
-
|
|
634
|
-
let o = await
|
|
757
|
+
A.info(`Migrating from ${a.name} (${a.framework}) to Fluenti`), A.info("Scanning project for existing i18n files...");
|
|
758
|
+
let o = await Oe(a);
|
|
635
759
|
if (o.configFiles.length === 0 && o.localeFiles.length === 0) {
|
|
636
|
-
|
|
760
|
+
A.warn(`No ${a.name} configuration or locale files found.`), A.info("Make sure you are running this command from the project root directory.");
|
|
637
761
|
return;
|
|
638
762
|
}
|
|
639
|
-
|
|
640
|
-
let s =
|
|
641
|
-
|
|
642
|
-
let c =
|
|
643
|
-
if (c.installCommands && (
|
|
763
|
+
A.info(`Found: ${o.configFiles.length} config file(s), ${o.localeFiles.length} locale file(s), ${o.sampleSources.length} source file(s)`);
|
|
764
|
+
let s = ke(a.migrationGuide);
|
|
765
|
+
A.info(`Generating migration plan with ${n}...`);
|
|
766
|
+
let c = Me(await je(n, Ae(a, o, s)));
|
|
767
|
+
if (c.installCommands && (A.log(""), A.box({
|
|
644
768
|
title: "Install Commands",
|
|
645
769
|
message: c.installCommands
|
|
646
770
|
})), c.config) if (r) {
|
|
647
|
-
let { writeFileSync: e } = await import("node:fs"), t =
|
|
648
|
-
e(t, c.config, "utf-8"),
|
|
649
|
-
} else
|
|
771
|
+
let { writeFileSync: e } = await import("node:fs"), t = w("fluenti.config.ts");
|
|
772
|
+
e(t, c.config, "utf-8"), A.success(`Written: ${t}`);
|
|
773
|
+
} else A.log(""), A.box({
|
|
650
774
|
title: "fluenti.config.ts",
|
|
651
775
|
message: c.config
|
|
652
776
|
});
|
|
653
777
|
if (c.localeFiles.length > 0) if (r) {
|
|
654
778
|
let { writeFileSync: e, mkdirSync: t } = await import("node:fs"), n = "./locales";
|
|
655
|
-
t(
|
|
779
|
+
t(w(n), { recursive: !0 });
|
|
656
780
|
for (let t of c.localeFiles) {
|
|
657
|
-
let r =
|
|
658
|
-
e(r, t.content, "utf-8"),
|
|
781
|
+
let r = w(n, `${t.locale}.po`);
|
|
782
|
+
e(r, t.content, "utf-8"), A.success(`Written: ${r}`);
|
|
659
783
|
}
|
|
660
|
-
} else for (let e of c.localeFiles)
|
|
784
|
+
} else for (let e of c.localeFiles) A.log(""), A.box({
|
|
661
785
|
title: `locales/${e.locale}.po`,
|
|
662
786
|
message: e.content.length > 500 ? e.content.slice(0, 500) + "\n... (use --write to save full file)" : e.content
|
|
663
787
|
});
|
|
664
|
-
c.steps && (
|
|
788
|
+
c.steps && (A.log(""), A.box({
|
|
665
789
|
title: "Migration Steps",
|
|
666
790
|
message: c.steps
|
|
667
|
-
})), !r && (c.config || c.localeFiles.length > 0) && (
|
|
791
|
+
})), !r && (c.config || c.localeFiles.length > 0) && (A.log(""), A.info("Run with --write to save generated files to disk:"), A.log(` fluenti migrate --from ${t} --write`));
|
|
668
792
|
}
|
|
669
793
|
//#endregion
|
|
670
794
|
//#region src/init.ts
|
|
671
|
-
var
|
|
795
|
+
var Pe = /^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{1,8})*$/;
|
|
672
796
|
function q(e) {
|
|
673
|
-
if (!
|
|
797
|
+
if (!Pe.test(e)) throw Error(`Invalid locale format: "${e}"`);
|
|
674
798
|
return e;
|
|
675
799
|
}
|
|
676
|
-
var
|
|
800
|
+
var Fe = [
|
|
677
801
|
{
|
|
678
802
|
dep: "next",
|
|
679
803
|
name: "nextjs",
|
|
@@ -705,8 +829,8 @@ var we = [
|
|
|
705
829
|
pluginPackage: "@fluenti/react"
|
|
706
830
|
}
|
|
707
831
|
];
|
|
708
|
-
function
|
|
709
|
-
for (let t of
|
|
832
|
+
function Ie(e) {
|
|
833
|
+
for (let t of Fe) if (t.dep in e) return {
|
|
710
834
|
name: t.name,
|
|
711
835
|
pluginPackage: t.pluginPackage
|
|
712
836
|
};
|
|
@@ -715,49 +839,49 @@ function Te(e) {
|
|
|
715
839
|
pluginPackage: null
|
|
716
840
|
};
|
|
717
841
|
}
|
|
718
|
-
function
|
|
719
|
-
let t = e.locales.map((e) =>
|
|
842
|
+
function Le(e) {
|
|
843
|
+
let t = e.locales.map((e) => JSON.stringify(e)).join(", ");
|
|
720
844
|
return `import { defineConfig } from '@fluenti/cli'
|
|
721
845
|
|
|
722
846
|
export default defineConfig({
|
|
723
|
-
sourceLocale:
|
|
847
|
+
sourceLocale: ${JSON.stringify(e.sourceLocale)},
|
|
724
848
|
locales: [${t}],
|
|
725
849
|
catalogDir: './locales',
|
|
726
|
-
format:
|
|
850
|
+
format: ${JSON.stringify(e.format)},
|
|
727
851
|
include: ['./src/**/*.{vue,tsx,jsx,ts,js}'],
|
|
728
852
|
compileOutDir: './src/locales/compiled',
|
|
729
853
|
})
|
|
730
854
|
`;
|
|
731
855
|
}
|
|
732
|
-
async function
|
|
733
|
-
let t =
|
|
856
|
+
async function Re(e) {
|
|
857
|
+
let t = w(e.cwd, "package.json");
|
|
734
858
|
if (!g(t)) {
|
|
735
|
-
|
|
859
|
+
A.error("No package.json found in current directory.");
|
|
736
860
|
return;
|
|
737
861
|
}
|
|
738
|
-
let n = JSON.parse(v(t, "utf-8")), r =
|
|
862
|
+
let n = JSON.parse(v(t, "utf-8")), r = Ie({
|
|
739
863
|
...n.dependencies,
|
|
740
864
|
...n.devDependencies
|
|
741
865
|
});
|
|
742
|
-
|
|
743
|
-
let i =
|
|
866
|
+
A.info(`Detected framework: ${r.name}`), r.pluginPackage && A.info(`Recommended plugin: ${r.pluginPackage}`);
|
|
867
|
+
let i = w(e.cwd, "fluenti.config.ts");
|
|
744
868
|
if (g(i)) {
|
|
745
|
-
|
|
869
|
+
A.warn("fluenti.config.ts already exists. Skipping config generation.");
|
|
746
870
|
return;
|
|
747
871
|
}
|
|
748
|
-
let a = await
|
|
872
|
+
let a = await A.prompt("Source locale?", {
|
|
749
873
|
type: "text",
|
|
750
874
|
default: "en",
|
|
751
875
|
placeholder: "en"
|
|
752
876
|
});
|
|
753
877
|
if (typeof a == "symbol") return;
|
|
754
|
-
let o = await
|
|
878
|
+
let o = await A.prompt("Target locales (comma-separated)?", {
|
|
755
879
|
type: "text",
|
|
756
880
|
default: "ja,zh-CN",
|
|
757
881
|
placeholder: "ja,zh-CN"
|
|
758
882
|
});
|
|
759
883
|
if (typeof o == "symbol") return;
|
|
760
|
-
let s = await
|
|
884
|
+
let s = await A.prompt("Catalog format?", {
|
|
761
885
|
type: "select",
|
|
762
886
|
options: ["po", "json"],
|
|
763
887
|
initial: "po"
|
|
@@ -766,13 +890,13 @@ async function De(e) {
|
|
|
766
890
|
let c = o.split(",").map((e) => e.trim()).filter(Boolean);
|
|
767
891
|
q(a);
|
|
768
892
|
for (let e of c) q(e);
|
|
769
|
-
|
|
893
|
+
b(i, Le({
|
|
770
894
|
sourceLocale: a,
|
|
771
895
|
locales: [a, ...c.filter((e) => e !== a)],
|
|
772
896
|
format: s
|
|
773
|
-
}), "utf-8"),
|
|
774
|
-
let l =
|
|
775
|
-
g(l) ? v(l, "utf-8").includes(u) || (h(l, `\n# Fluenti compiled catalogs\n${u}\n`),
|
|
897
|
+
}), "utf-8"), A.success("Created fluenti.config.ts");
|
|
898
|
+
let l = w(e.cwd, ".gitignore"), u = "src/locales/compiled/";
|
|
899
|
+
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"));
|
|
776
900
|
let d = n.scripts ?? {}, f = {}, p = !1;
|
|
777
901
|
if (d["i18n:extract"] || (f["i18n:extract"] = "fluenti extract", p = !0), d["i18n:compile"] || (f["i18n:compile"] = "fluenti compile", p = !0), p) {
|
|
778
902
|
let e = {
|
|
@@ -782,9 +906,9 @@ async function De(e) {
|
|
|
782
906
|
...f
|
|
783
907
|
}
|
|
784
908
|
};
|
|
785
|
-
|
|
909
|
+
b(t, JSON.stringify(e, null, 2) + "\n", "utf-8"), A.success("Added i18n:extract and i18n:compile scripts to package.json");
|
|
786
910
|
}
|
|
787
|
-
|
|
911
|
+
A.log(""), A.box({
|
|
788
912
|
title: "Next steps",
|
|
789
913
|
message: [
|
|
790
914
|
r.pluginPackage ? `1. Install: pnpm add -D ${r.pluginPackage} @fluenti/cli` : "1. Install: pnpm add -D @fluenti/cli",
|
|
@@ -798,7 +922,7 @@ async function De(e) {
|
|
|
798
922
|
//#endregion
|
|
799
923
|
//#region src/cli.ts
|
|
800
924
|
function J(e) {
|
|
801
|
-
return
|
|
925
|
+
return D("md5").update(e).digest("hex").slice(0, 8);
|
|
802
926
|
}
|
|
803
927
|
function Y(e, t) {
|
|
804
928
|
if (!g(e)) return {};
|
|
@@ -806,18 +930,18 @@ function Y(e, t) {
|
|
|
806
930
|
return t === "json" ? l(n) : s(n);
|
|
807
931
|
}
|
|
808
932
|
function X(e, t, n) {
|
|
809
|
-
_(
|
|
933
|
+
_(x(e), { recursive: !0 }), b(e, n === "json" ? d(t) : a(t), "utf-8");
|
|
810
934
|
}
|
|
811
|
-
async function
|
|
812
|
-
if (
|
|
935
|
+
async function ze(e, t, n) {
|
|
936
|
+
if (S(e) === ".vue") try {
|
|
813
937
|
let { extractFromVue: r } = await import("./vue-extractor.js");
|
|
814
938
|
return r(t, e, n);
|
|
815
939
|
} catch {
|
|
816
|
-
return
|
|
940
|
+
return A.warn(`Skipping ${e}: install @vue/compiler-sfc to extract from .vue files`), [];
|
|
817
941
|
}
|
|
818
942
|
return i(t, e, n);
|
|
819
943
|
}
|
|
820
|
-
var
|
|
944
|
+
var Z = O({
|
|
821
945
|
meta: {
|
|
822
946
|
name: "extract",
|
|
823
947
|
description: "Extract messages from source files"
|
|
@@ -845,8 +969,8 @@ var ke = D({
|
|
|
845
969
|
},
|
|
846
970
|
async run({ args: e }) {
|
|
847
971
|
let t = await u(e.config), n = m(t.locales);
|
|
848
|
-
|
|
849
|
-
let r = await
|
|
972
|
+
A.info(`Extracting messages from ${t.include.join(", ")}`);
|
|
973
|
+
let r = await E(t.include, { ignore: t.exclude ?? [] }), i = [], a = e["no-cache"] ?? !1 ? null : new f(t.catalogDir, J(process.cwd())), s = 0;
|
|
850
974
|
for (let e of r) {
|
|
851
975
|
if (a) {
|
|
852
976
|
let t = a.get(e);
|
|
@@ -855,16 +979,16 @@ var ke = D({
|
|
|
855
979
|
continue;
|
|
856
980
|
}
|
|
857
981
|
}
|
|
858
|
-
let n = await
|
|
982
|
+
let n = await ze(e, v(e, "utf-8"), t.idGenerator);
|
|
859
983
|
i.push(...n), a && a.set(e, n);
|
|
860
984
|
}
|
|
861
|
-
a && (a.prune(new Set(r)), a.save()), s > 0 ?
|
|
985
|
+
a && (a.prune(new Set(r)), a.save()), s > 0 ? A.info(`Found ${i.length} messages in ${r.length} files (${s} cached)`) : A.info(`Found ${i.length} messages in ${r.length} files`);
|
|
862
986
|
let c = t.format === "json" ? ".json" : ".po", l = e.clean ?? !1, d = e["no-fuzzy"] ?? !1;
|
|
863
987
|
for (let e of n) {
|
|
864
|
-
let n =
|
|
988
|
+
let n = w(t.catalogDir, `${e}${c}`), { catalog: r, result: a } = o(Y(n, t.format), i, { stripFuzzy: d });
|
|
865
989
|
X(n, l ? Object.fromEntries(Object.entries(r).filter(([, e]) => !e.obsolete)) : r, t.format);
|
|
866
990
|
let s = l ? `${a.obsolete} removed` : `${a.obsolete} obsolete`;
|
|
867
|
-
|
|
991
|
+
A.success(`${e}: ${a.added} added, ${a.unchanged} unchanged, ${s}`);
|
|
868
992
|
}
|
|
869
993
|
for (let e of t.plugins ?? []) await e.onAfterExtract?.({
|
|
870
994
|
messages: new Map(i.map((e) => [e.id, e])),
|
|
@@ -874,13 +998,13 @@ var ke = D({
|
|
|
874
998
|
});
|
|
875
999
|
}
|
|
876
1000
|
});
|
|
877
|
-
function
|
|
1001
|
+
function Be(e) {
|
|
878
1002
|
let t = {};
|
|
879
1003
|
for (let [n, r] of Object.entries(e)) r.translation && r.translation.length > 0 ? t[n] = r.translation : r.message && (t[n] = r.message);
|
|
880
1004
|
return t;
|
|
881
1005
|
}
|
|
882
|
-
async function
|
|
883
|
-
let r =
|
|
1006
|
+
async function Q(e, t, n) {
|
|
1007
|
+
let r = Be(e);
|
|
884
1008
|
for (let e of n) e.transformMessages && (r = await e.transformMessages(r, t));
|
|
885
1009
|
let i = {};
|
|
886
1010
|
for (let [t, n] of Object.entries(e)) {
|
|
@@ -892,7 +1016,7 @@ async function Z(e, t, n) {
|
|
|
892
1016
|
}
|
|
893
1017
|
return i;
|
|
894
1018
|
}
|
|
895
|
-
function
|
|
1019
|
+
function $(e, t, n, r) {
|
|
896
1020
|
let i = {};
|
|
897
1021
|
for (let [e, n] of Object.entries(t)) n.translation && n.translation.length > 0 ? i[e] = n.translation : n.message && (i[e] = n.message);
|
|
898
1022
|
return {
|
|
@@ -902,7 +1026,7 @@ function Q(e, t, n, r) {
|
|
|
902
1026
|
config: r
|
|
903
1027
|
};
|
|
904
1028
|
}
|
|
905
|
-
var
|
|
1029
|
+
var Ve = O({
|
|
906
1030
|
meta: {
|
|
907
1031
|
name: "compile",
|
|
908
1032
|
description: "Compile message catalogs to JS modules"
|
|
@@ -937,22 +1061,22 @@ var $ = D({
|
|
|
937
1061
|
_(a.compileOutDir, { recursive: !0 });
|
|
938
1062
|
let f = {}, p = {};
|
|
939
1063
|
for (let e of o) {
|
|
940
|
-
let t =
|
|
1064
|
+
let t = w(a.catalogDir, `${e}${d}`);
|
|
941
1065
|
if (g(t)) {
|
|
942
1066
|
let n = v(t, "utf-8");
|
|
943
1067
|
p[e] = n, f[e] = a.format === "json" ? l(n) : s(n);
|
|
944
1068
|
} else p[e] = "", f[e] = {};
|
|
945
1069
|
}
|
|
946
1070
|
let h = r(f);
|
|
947
|
-
|
|
948
|
-
let
|
|
949
|
-
if (
|
|
950
|
-
|
|
1071
|
+
A.info(`Compiling ${h.length} messages across ${o.length} locales`);
|
|
1072
|
+
let y = i["skip-fuzzy"] ?? !1, x = i["no-cache"] ?? !1 ? null : new fe(a.catalogDir, J(process.cwd())), S = i.parallel ?? !1, C = i.concurrency ? parseInt(i.concurrency, 10) : void 0;
|
|
1073
|
+
if (C !== void 0 && (isNaN(C) || C < 1)) {
|
|
1074
|
+
A.error("Invalid --concurrency. Must be a positive integer."), process.exitCode = 1;
|
|
951
1075
|
return;
|
|
952
1076
|
}
|
|
953
1077
|
let T = 0, E = !1, D = [];
|
|
954
1078
|
for (let e of o) {
|
|
955
|
-
if (x && x.isUpToDate(e, p[e]) && g(
|
|
1079
|
+
if (x && x.isUpToDate(e, p[e]) && g(w(a.compileOutDir, `${e}.js`))) {
|
|
956
1080
|
T++;
|
|
957
1081
|
continue;
|
|
958
1082
|
}
|
|
@@ -961,42 +1085,42 @@ var $ = D({
|
|
|
961
1085
|
if (D.length > 0 && (E = !0), S && D.length > 1) {
|
|
962
1086
|
let e = a.plugins ?? [], t = {};
|
|
963
1087
|
for (let n of D) {
|
|
964
|
-
for (let t of e) await t.onBeforeCompile?.(
|
|
965
|
-
t[n] = e.length > 0 ? await
|
|
1088
|
+
for (let t of e) await t.onBeforeCompile?.($(n, f[n], a.compileOutDir, a));
|
|
1089
|
+
t[n] = e.length > 0 ? await Q(f[n], n, e) : f[n];
|
|
966
1090
|
}
|
|
967
1091
|
let n = await c(D.map((e) => ({
|
|
968
1092
|
locale: e,
|
|
969
1093
|
catalog: t[e],
|
|
970
1094
|
allIds: h,
|
|
971
1095
|
sourceLocale: a.sourceLocale,
|
|
972
|
-
options: { skipFuzzy:
|
|
973
|
-
})),
|
|
1096
|
+
options: { skipFuzzy: y }
|
|
1097
|
+
})), C);
|
|
974
1098
|
for (let e of n) {
|
|
975
|
-
let t =
|
|
976
|
-
if (
|
|
977
|
-
|
|
978
|
-
for (let t of e.stats.missing)
|
|
979
|
-
} else
|
|
1099
|
+
let t = w(a.compileOutDir, `${e.locale}.js`);
|
|
1100
|
+
if (b(t, e.code, "utf-8"), x && x.set(e.locale, p[e.locale]), e.stats.missing.length > 0) {
|
|
1101
|
+
A.warn(`${e.locale}: ${e.stats.compiled} compiled, ${e.stats.missing.length} missing translations`);
|
|
1102
|
+
for (let t of e.stats.missing) A.warn(` ⤷ ${t}`);
|
|
1103
|
+
} else A.success(`Compiled ${e.locale}: ${e.stats.compiled} messages → ${t}`);
|
|
980
1104
|
}
|
|
981
|
-
for (let n of D) for (let r of e) await r.onAfterCompile?.(
|
|
1105
|
+
for (let n of D) for (let r of e) await r.onAfterCompile?.($(n, t[n], a.compileOutDir, a));
|
|
982
1106
|
} else {
|
|
983
1107
|
let e = a.plugins ?? [];
|
|
984
1108
|
for (let n of D) {
|
|
985
|
-
let r =
|
|
986
|
-
for (let t of e) await t.onBeforeCompile?.(
|
|
987
|
-
let i = e.length > 0 ? await
|
|
988
|
-
if (
|
|
989
|
-
|
|
990
|
-
for (let e of s.missing)
|
|
991
|
-
} else
|
|
992
|
-
for (let t of e) await t.onAfterCompile?.(
|
|
1109
|
+
let r = w(a.compileOutDir, `${n}.js`);
|
|
1110
|
+
for (let t of e) await t.onBeforeCompile?.($(n, f[n], a.compileOutDir, a));
|
|
1111
|
+
let i = e.length > 0 ? await Q(f[n], n, e) : f[n], { code: o, stats: s } = t(i, n, h, a.sourceLocale, { skipFuzzy: y });
|
|
1112
|
+
if (b(r, o, "utf-8"), x && x.set(n, p[n]), s.missing.length > 0) {
|
|
1113
|
+
A.warn(`${n}: ${s.compiled} compiled, ${s.missing.length} missing translations`);
|
|
1114
|
+
for (let e of s.missing) A.warn(` ⤷ ${e}`);
|
|
1115
|
+
} else A.success(`Compiled ${n}: ${s.compiled} messages → ${r}`);
|
|
1116
|
+
for (let t of e) await t.onAfterCompile?.($(n, i, a.compileOutDir, a));
|
|
993
1117
|
}
|
|
994
1118
|
}
|
|
995
|
-
T > 0 &&
|
|
996
|
-
let O =
|
|
997
|
-
(E || !g(O)) && (
|
|
1119
|
+
T > 0 && A.info(`${T} locale(s) unchanged — skipped`), x && x.save();
|
|
1120
|
+
let O = w(a.compileOutDir, "index.js"), k = w(a.compileOutDir, "messages.d.ts");
|
|
1121
|
+
(E || !g(O)) && (b(O, n(o, a.compileOutDir), "utf-8"), A.success(`Generated index → ${O}`)), (E || !g(k)) && (b(k, e(h, f, a.sourceLocale), "utf-8"), A.success(`Generated types → ${k}`));
|
|
998
1122
|
}
|
|
999
|
-
}),
|
|
1123
|
+
}), He = O({
|
|
1000
1124
|
meta: {
|
|
1001
1125
|
name: "stats",
|
|
1002
1126
|
description: "Show translation progress"
|
|
@@ -1008,7 +1132,7 @@ var $ = D({
|
|
|
1008
1132
|
async run({ args: e }) {
|
|
1009
1133
|
let t = await u(e.config), n = m(t.locales), r = t.format === "json" ? ".json" : ".po", i = [];
|
|
1010
1134
|
for (let e of n) {
|
|
1011
|
-
let n = Y(
|
|
1135
|
+
let n = Y(w(t.catalogDir, `${e}${r}`), t.format), a = Object.values(n).filter((e) => !e.obsolete), o = a.length, s = a.filter((e) => e.translation && e.translation.length > 0).length, c = o > 0 ? (s / o * 100).toFixed(1) + "%" : "—";
|
|
1012
1136
|
i.push({
|
|
1013
1137
|
locale: e,
|
|
1014
1138
|
total: o,
|
|
@@ -1016,11 +1140,11 @@ var $ = D({
|
|
|
1016
1140
|
pct: c
|
|
1017
1141
|
});
|
|
1018
1142
|
}
|
|
1019
|
-
|
|
1020
|
-
for (let e of i)
|
|
1021
|
-
|
|
1143
|
+
A.log(""), A.log(" Locale │ Total │ Translated │ Progress"), A.log(" ────────┼───────┼────────────┼─────────────────────────────");
|
|
1144
|
+
for (let e of i) A.log(ie(e.locale, e.total, e.translated));
|
|
1145
|
+
A.log("");
|
|
1022
1146
|
}
|
|
1023
|
-
}),
|
|
1147
|
+
}), Ue = O({
|
|
1024
1148
|
meta: {
|
|
1025
1149
|
name: "lint",
|
|
1026
1150
|
description: "Check translation quality (missing, inconsistent placeholders, fuzzy)"
|
|
@@ -1042,20 +1166,20 @@ var $ = D({
|
|
|
1042
1166
|
},
|
|
1043
1167
|
async run({ args: e }) {
|
|
1044
1168
|
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(
|
|
1169
|
+
for (let e of n) i[e] = Y(w(t.catalogDir, `${e}${r}`), t.format);
|
|
1046
1170
|
let a = e.locale ? [e.locale] : void 0;
|
|
1047
|
-
|
|
1171
|
+
A.info(`Linting ${a ? a.join(", ") : "all locales"} (source: ${t.sourceLocale})`);
|
|
1048
1172
|
let o = {
|
|
1049
1173
|
sourceLocale: t.sourceLocale,
|
|
1050
1174
|
strict: e.strict ?? !1
|
|
1051
1175
|
};
|
|
1052
1176
|
a && (o.locales = a);
|
|
1053
|
-
let s =
|
|
1054
|
-
|
|
1177
|
+
let s = R(i, o);
|
|
1178
|
+
A.log(""), A.log(oe(s)), A.log("");
|
|
1055
1179
|
let c = s.filter((e) => e.severity === "error"), l = s.filter((e) => e.severity === "warning");
|
|
1056
1180
|
(c.length > 0 || e.strict && l.length > 0) && (process.exitCode = 1);
|
|
1057
1181
|
}
|
|
1058
|
-
}),
|
|
1182
|
+
}), We = O({
|
|
1059
1183
|
meta: {
|
|
1060
1184
|
name: "check",
|
|
1061
1185
|
description: "Check translation coverage for CI"
|
|
@@ -1086,10 +1210,10 @@ var $ = D({
|
|
|
1086
1210
|
},
|
|
1087
1211
|
async run({ args: e }) {
|
|
1088
1212
|
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(
|
|
1213
|
+
for (let e of n) i[e] = Y(w(t.catalogDir, `${e}${r}`), t.format);
|
|
1090
1214
|
let a = parseFloat(e["min-coverage"] ?? "100");
|
|
1091
1215
|
if (isNaN(a) || a < 0 || a > 100) {
|
|
1092
|
-
|
|
1216
|
+
A.error("Invalid --min-coverage. Must be a number between 0 and 100."), process.exitCode = 1;
|
|
1093
1217
|
return;
|
|
1094
1218
|
}
|
|
1095
1219
|
let o = e.format ?? (e.ci ? "github" : "text"), s = {
|
|
@@ -1098,21 +1222,31 @@ var $ = D({
|
|
|
1098
1222
|
format: o
|
|
1099
1223
|
};
|
|
1100
1224
|
e.locale && (s.locale = e.locale);
|
|
1101
|
-
let c =
|
|
1225
|
+
let c = ce(i, s);
|
|
1102
1226
|
switch (o) {
|
|
1103
1227
|
case "json":
|
|
1104
|
-
|
|
1228
|
+
A.log(de(c));
|
|
1105
1229
|
break;
|
|
1106
1230
|
case "github":
|
|
1107
|
-
|
|
1231
|
+
A.log(ue(c, t.catalogDir, t.format));
|
|
1108
1232
|
break;
|
|
1109
1233
|
default:
|
|
1110
|
-
|
|
1234
|
+
A.log(""), A.log(le(c)), A.log("");
|
|
1111
1235
|
break;
|
|
1112
1236
|
}
|
|
1113
1237
|
c.passed || (process.exitCode = 1);
|
|
1114
1238
|
}
|
|
1115
|
-
})
|
|
1239
|
+
});
|
|
1240
|
+
async function Ge(e, t, n) {
|
|
1241
|
+
let r = Array(e.length), i = 0, a = Array.from({ length: Math.min(n, e.length) }, async () => {
|
|
1242
|
+
for (; i < e.length;) {
|
|
1243
|
+
let n = i++;
|
|
1244
|
+
r[n] = await t(e[n]);
|
|
1245
|
+
}
|
|
1246
|
+
});
|
|
1247
|
+
return await Promise.all(a), r;
|
|
1248
|
+
}
|
|
1249
|
+
var Ke = O({
|
|
1116
1250
|
meta: {
|
|
1117
1251
|
name: "translate",
|
|
1118
1252
|
description: "Translate messages using AI (Claude Code or Codex CLI)"
|
|
@@ -1144,49 +1278,73 @@ var $ = D({
|
|
|
1144
1278
|
context: {
|
|
1145
1279
|
type: "string",
|
|
1146
1280
|
description: "Project context description to improve translation quality"
|
|
1281
|
+
},
|
|
1282
|
+
glossary: {
|
|
1283
|
+
type: "string",
|
|
1284
|
+
description: "Path to glossary JSON file"
|
|
1285
|
+
},
|
|
1286
|
+
concurrency: {
|
|
1287
|
+
type: "string",
|
|
1288
|
+
description: "Max parallel locale translations (default: 3)"
|
|
1289
|
+
},
|
|
1290
|
+
timeout: {
|
|
1291
|
+
type: "string",
|
|
1292
|
+
description: "AI call timeout in seconds (default: 120)"
|
|
1147
1293
|
}
|
|
1148
1294
|
},
|
|
1149
1295
|
async run({ args: e }) {
|
|
1150
1296
|
let t = await u(e.config), n = m(t.locales), r = e.provider;
|
|
1151
1297
|
if (r !== "claude" && r !== "codex") {
|
|
1152
|
-
|
|
1298
|
+
A.error(`Invalid provider "${r}". Use "claude" or "codex".`);
|
|
1153
1299
|
return;
|
|
1154
1300
|
}
|
|
1155
1301
|
let i = parseInt(e["batch-size"] ?? "50", 10);
|
|
1156
1302
|
if (isNaN(i) || i < 1) {
|
|
1157
|
-
|
|
1303
|
+
A.error("Invalid batch-size. Must be a positive integer.");
|
|
1304
|
+
return;
|
|
1305
|
+
}
|
|
1306
|
+
let a = e.glossary ? _e(w(e.glossary)) : void 0, o = e.concurrency ? parseInt(e.concurrency, 10) : 3;
|
|
1307
|
+
if (isNaN(o) || o < 1) {
|
|
1308
|
+
A.error("Invalid concurrency. Must be a positive integer.");
|
|
1158
1309
|
return;
|
|
1159
1310
|
}
|
|
1160
|
-
let
|
|
1161
|
-
if (
|
|
1162
|
-
|
|
1311
|
+
let s = e.timeout ? parseInt(e.timeout, 10) : 120;
|
|
1312
|
+
if (isNaN(s) || s < 1) {
|
|
1313
|
+
A.error("Invalid timeout. Must be a positive integer (seconds).");
|
|
1163
1314
|
return;
|
|
1164
1315
|
}
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1316
|
+
let c = s * 1e3, l = e.locale ? [e.locale] : n.filter((e) => e !== t.sourceLocale);
|
|
1317
|
+
if (l.length === 0) {
|
|
1318
|
+
A.warn("No target locales to translate.");
|
|
1319
|
+
return;
|
|
1320
|
+
}
|
|
1321
|
+
A.info(`Translating with ${r} (batch size: ${i})`);
|
|
1322
|
+
let d = t.format === "json" ? ".json" : ".po";
|
|
1323
|
+
await Ge(l, async (n) => {
|
|
1324
|
+
A.info(`\n[${n}]`);
|
|
1325
|
+
let o = w(t.catalogDir, `${n}${d}`), s = Y(o, t.format);
|
|
1170
1326
|
if (e["dry-run"]) {
|
|
1171
1327
|
let e = Object.entries(s).filter(([, e]) => !e.obsolete && (!e.translation || e.translation.length === 0));
|
|
1172
1328
|
if (e.length > 0) {
|
|
1173
|
-
for (let [t, n] of e)
|
|
1174
|
-
|
|
1175
|
-
} else
|
|
1176
|
-
|
|
1329
|
+
for (let [t, n] of e) A.log(` ${t}: ${n.message ?? t}`);
|
|
1330
|
+
A.success(` ${n}: ${e.length} messages would be translated (dry-run)`);
|
|
1331
|
+
} else A.success(` ${n}: already fully translated`);
|
|
1332
|
+
return;
|
|
1177
1333
|
}
|
|
1178
|
-
let { catalog:
|
|
1334
|
+
let l = a ? ve(a, n) : void 0, { catalog: u, translated: f, warnings: p } = await Ee({
|
|
1179
1335
|
provider: r,
|
|
1180
1336
|
sourceLocale: t.sourceLocale,
|
|
1181
1337
|
targetLocale: n,
|
|
1182
1338
|
catalog: s,
|
|
1183
1339
|
batchSize: i,
|
|
1340
|
+
glossary: l,
|
|
1341
|
+
timeoutMs: c,
|
|
1184
1342
|
...e.context ? { context: e.context } : {}
|
|
1185
1343
|
});
|
|
1186
|
-
|
|
1187
|
-
}
|
|
1344
|
+
f > 0 ? (X(o, u, t.format), A.success(` ${n}: ${f} messages translated`)) : A.success(` ${n}: already fully translated`), p.length > 0 && A.warn(` ${n}: ${p.length} QA warnings`);
|
|
1345
|
+
}, o);
|
|
1188
1346
|
}
|
|
1189
|
-
}),
|
|
1347
|
+
}), qe = O({
|
|
1190
1348
|
meta: {
|
|
1191
1349
|
name: "migrate",
|
|
1192
1350
|
description: "Migrate from another i18n library using AI"
|
|
@@ -1211,40 +1369,40 @@ var $ = D({
|
|
|
1211
1369
|
async run({ args: e }) {
|
|
1212
1370
|
let t = e.provider;
|
|
1213
1371
|
if (t !== "claude" && t !== "codex") {
|
|
1214
|
-
|
|
1372
|
+
A.error(`Invalid provider "${t}". Use "claude" or "codex".`);
|
|
1215
1373
|
return;
|
|
1216
1374
|
}
|
|
1217
|
-
await
|
|
1375
|
+
await Ne({
|
|
1218
1376
|
from: e.from,
|
|
1219
1377
|
provider: t,
|
|
1220
1378
|
write: e.write ?? !1
|
|
1221
1379
|
});
|
|
1222
1380
|
}
|
|
1223
1381
|
});
|
|
1224
|
-
O(
|
|
1382
|
+
k(O({
|
|
1225
1383
|
meta: {
|
|
1226
1384
|
name: "fluenti",
|
|
1227
1385
|
version: "0.0.1",
|
|
1228
1386
|
description: "Compile-time i18n for modern frameworks"
|
|
1229
1387
|
},
|
|
1230
1388
|
subCommands: {
|
|
1231
|
-
init:
|
|
1389
|
+
init: O({
|
|
1232
1390
|
meta: {
|
|
1233
1391
|
name: "init",
|
|
1234
1392
|
description: "Initialize Fluenti in your project"
|
|
1235
1393
|
},
|
|
1236
1394
|
args: {},
|
|
1237
1395
|
async run() {
|
|
1238
|
-
await
|
|
1396
|
+
await Re({ cwd: process.cwd() });
|
|
1239
1397
|
}
|
|
1240
1398
|
}),
|
|
1241
|
-
extract:
|
|
1242
|
-
compile:
|
|
1243
|
-
stats:
|
|
1244
|
-
lint:
|
|
1245
|
-
check:
|
|
1246
|
-
translate:
|
|
1247
|
-
migrate:
|
|
1399
|
+
extract: Z,
|
|
1400
|
+
compile: Ve,
|
|
1401
|
+
stats: He,
|
|
1402
|
+
lint: Ue,
|
|
1403
|
+
check: We,
|
|
1404
|
+
translate: Ke,
|
|
1405
|
+
migrate: qe
|
|
1248
1406
|
}
|
|
1249
1407
|
}));
|
|
1250
1408
|
//#endregion
|