@inline-i18n-multi/cli 0.7.0 → 0.9.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/README.md +76 -8
- package/dist/bin.js +402 -26
- package/dist/bin.js.map +1 -1
- package/dist/index.d.ts +43 -1
- package/dist/index.js +354 -23
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -31,8 +31,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
coverage: () => coverage,
|
|
34
|
+
extract: () => extract,
|
|
35
|
+
extractProjectDictionaryKeys: () => extractProjectDictionaryKeys,
|
|
36
|
+
extractProjectTCalls: () => extractProjectTCalls,
|
|
34
37
|
find: () => find,
|
|
38
|
+
parseDictionaryKeys: () => parseDictionaryKeys,
|
|
35
39
|
parseProject: () => parseProject,
|
|
40
|
+
parseTCalls: () => parseTCalls,
|
|
41
|
+
typegen: () => typegen,
|
|
42
|
+
unused: () => unused,
|
|
36
43
|
validate: () => validate
|
|
37
44
|
});
|
|
38
45
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -156,6 +163,125 @@ function parseFile(filePath) {
|
|
|
156
163
|
});
|
|
157
164
|
return entries;
|
|
158
165
|
}
|
|
166
|
+
function flattenObjectKeys(node, prefix = "") {
|
|
167
|
+
const keys = [];
|
|
168
|
+
if (node.type !== "ObjectExpression" || !node.properties) return keys;
|
|
169
|
+
for (const prop of node.properties) {
|
|
170
|
+
if (prop.type !== "ObjectProperty") continue;
|
|
171
|
+
let propName;
|
|
172
|
+
if (prop.key.type === "Identifier") propName = prop.key.name;
|
|
173
|
+
else if (prop.key.type === "StringLiteral") propName = prop.key.value;
|
|
174
|
+
if (!propName) continue;
|
|
175
|
+
const fullKey = prefix ? `${prefix}.${propName}` : propName;
|
|
176
|
+
if (prop.value.type === "StringLiteral") {
|
|
177
|
+
keys.push(fullKey);
|
|
178
|
+
} else if (prop.value.type === "ObjectExpression") {
|
|
179
|
+
keys.push(...flattenObjectKeys(prop.value, fullKey));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return keys;
|
|
183
|
+
}
|
|
184
|
+
function parseDictionaryKeys(filePath) {
|
|
185
|
+
const entries = [];
|
|
186
|
+
const code = fs.readFileSync(filePath, "utf-8");
|
|
187
|
+
let ast;
|
|
188
|
+
try {
|
|
189
|
+
ast = (0, import_parser.parse)(code, {
|
|
190
|
+
sourceType: "module",
|
|
191
|
+
plugins: ["typescript", "jsx"]
|
|
192
|
+
});
|
|
193
|
+
} catch {
|
|
194
|
+
return [];
|
|
195
|
+
}
|
|
196
|
+
(0, import_traverse.default)(ast, {
|
|
197
|
+
CallExpression(nodePath) {
|
|
198
|
+
const { node } = nodePath;
|
|
199
|
+
const { callee } = node;
|
|
200
|
+
if (callee.type !== "Identifier" || callee.name !== "loadDictionaries") return;
|
|
201
|
+
const args = node.arguments;
|
|
202
|
+
const loc = node.loc;
|
|
203
|
+
if (!loc || !args[0] || args[0].type !== "ObjectExpression") return;
|
|
204
|
+
let namespace = "default";
|
|
205
|
+
if (args[1]?.type === "StringLiteral") {
|
|
206
|
+
namespace = args[1].value;
|
|
207
|
+
}
|
|
208
|
+
const dictObj = args[0];
|
|
209
|
+
const allKeys = /* @__PURE__ */ new Set();
|
|
210
|
+
for (const localeProp of dictObj.properties) {
|
|
211
|
+
if (localeProp.type !== "ObjectProperty") continue;
|
|
212
|
+
if (localeProp.value.type !== "ObjectExpression") continue;
|
|
213
|
+
const localeKeys = flattenObjectKeys(localeProp.value);
|
|
214
|
+
for (const key of localeKeys) {
|
|
215
|
+
allKeys.add(key);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (allKeys.size > 0) {
|
|
219
|
+
entries.push({
|
|
220
|
+
file: filePath,
|
|
221
|
+
line: loc.start.line,
|
|
222
|
+
namespace,
|
|
223
|
+
keys: [...allKeys]
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
return entries;
|
|
229
|
+
}
|
|
230
|
+
function parseTCalls(filePath) {
|
|
231
|
+
const entries = [];
|
|
232
|
+
const code = fs.readFileSync(filePath, "utf-8");
|
|
233
|
+
let ast;
|
|
234
|
+
try {
|
|
235
|
+
ast = (0, import_parser.parse)(code, {
|
|
236
|
+
sourceType: "module",
|
|
237
|
+
plugins: ["typescript", "jsx"]
|
|
238
|
+
});
|
|
239
|
+
} catch {
|
|
240
|
+
return [];
|
|
241
|
+
}
|
|
242
|
+
(0, import_traverse.default)(ast, {
|
|
243
|
+
CallExpression(nodePath) {
|
|
244
|
+
const { node } = nodePath;
|
|
245
|
+
const { callee } = node;
|
|
246
|
+
if (callee.type !== "Identifier" || callee.name !== "t") return;
|
|
247
|
+
const args = node.arguments;
|
|
248
|
+
const loc = node.loc;
|
|
249
|
+
if (!loc || !args[0] || args[0].type !== "StringLiteral") return;
|
|
250
|
+
entries.push({
|
|
251
|
+
file: filePath,
|
|
252
|
+
line: loc.start.line,
|
|
253
|
+
key: args[0].value
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
return entries;
|
|
258
|
+
}
|
|
259
|
+
async function extractProjectDictionaryKeys(options = {}) {
|
|
260
|
+
const {
|
|
261
|
+
cwd = process.cwd(),
|
|
262
|
+
include = ["**/*.{ts,tsx,js,jsx}"],
|
|
263
|
+
exclude = ["**/node_modules/**", "**/dist/**", "**/.next/**"]
|
|
264
|
+
} = options;
|
|
265
|
+
const files = await (0, import_fast_glob.default)(include, { cwd, ignore: exclude, absolute: true });
|
|
266
|
+
const allEntries = [];
|
|
267
|
+
for (const file of files) {
|
|
268
|
+
allEntries.push(...parseDictionaryKeys(file));
|
|
269
|
+
}
|
|
270
|
+
return allEntries;
|
|
271
|
+
}
|
|
272
|
+
async function extractProjectTCalls(options = {}) {
|
|
273
|
+
const {
|
|
274
|
+
cwd = process.cwd(),
|
|
275
|
+
include = ["**/*.{ts,tsx,js,jsx}"],
|
|
276
|
+
exclude = ["**/node_modules/**", "**/dist/**", "**/.next/**"]
|
|
277
|
+
} = options;
|
|
278
|
+
const files = await (0, import_fast_glob.default)(include, { cwd, ignore: exclude, absolute: true });
|
|
279
|
+
const allEntries = [];
|
|
280
|
+
for (const file of files) {
|
|
281
|
+
allEntries.push(...parseTCalls(file));
|
|
282
|
+
}
|
|
283
|
+
return allEntries;
|
|
284
|
+
}
|
|
159
285
|
async function parseProject(options = {}) {
|
|
160
286
|
const {
|
|
161
287
|
cwd = process.cwd(),
|
|
@@ -213,7 +339,66 @@ Searching for: "${query}"
|
|
|
213
339
|
}
|
|
214
340
|
|
|
215
341
|
// src/commands/validate.ts
|
|
342
|
+
var import_chalk3 = __toESM(require("chalk"));
|
|
343
|
+
|
|
344
|
+
// src/commands/unused.ts
|
|
216
345
|
var import_chalk2 = __toESM(require("chalk"));
|
|
346
|
+
var PLURAL_SUFFIXES = ["_zero", "_one", "_two", "_few", "_many", "_other"];
|
|
347
|
+
async function unused(options = {}) {
|
|
348
|
+
const { cwd } = options;
|
|
349
|
+
console.log(import_chalk2.default.blue("\nDetecting unused translations...\n"));
|
|
350
|
+
const dictEntries = await extractProjectDictionaryKeys({ cwd });
|
|
351
|
+
const tCalls = await extractProjectTCalls({ cwd });
|
|
352
|
+
const usedKeys = /* @__PURE__ */ new Set();
|
|
353
|
+
for (const call of tCalls) {
|
|
354
|
+
usedKeys.add(call.key);
|
|
355
|
+
if (!call.key.includes(":")) {
|
|
356
|
+
usedKeys.add(call.key);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
const unusedKeys = [];
|
|
360
|
+
for (const entry of dictEntries) {
|
|
361
|
+
for (const key of entry.keys) {
|
|
362
|
+
const fullKey = entry.namespace === "default" ? key : `${entry.namespace}:${key}`;
|
|
363
|
+
if (usedKeys.has(fullKey)) continue;
|
|
364
|
+
let isPluralVariant = false;
|
|
365
|
+
for (const suffix of PLURAL_SUFFIXES) {
|
|
366
|
+
if (key.endsWith(suffix)) {
|
|
367
|
+
const baseKey = key.slice(0, -suffix.length);
|
|
368
|
+
const fullBaseKey = entry.namespace === "default" ? baseKey : `${entry.namespace}:${baseKey}`;
|
|
369
|
+
if (usedKeys.has(fullBaseKey)) {
|
|
370
|
+
isPluralVariant = true;
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (isPluralVariant) continue;
|
|
376
|
+
unusedKeys.push({
|
|
377
|
+
namespace: entry.namespace,
|
|
378
|
+
key: fullKey,
|
|
379
|
+
definedIn: entry.file,
|
|
380
|
+
line: entry.line
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (unusedKeys.length === 0) {
|
|
385
|
+
const totalKeys = dictEntries.reduce((sum, e) => sum + e.keys.length, 0);
|
|
386
|
+
console.log(import_chalk2.default.green("No unused translations found!\n"));
|
|
387
|
+
console.log(import_chalk2.default.gray(`Checked ${totalKeys} dictionary key(s) against ${tCalls.length} t() call(s)`));
|
|
388
|
+
return { unusedKeys };
|
|
389
|
+
}
|
|
390
|
+
console.log(import_chalk2.default.yellow(`Found ${unusedKeys.length} unused translation key(s):
|
|
391
|
+
`));
|
|
392
|
+
for (const item of unusedKeys) {
|
|
393
|
+
const relativePath = item.definedIn.replace(process.cwd() + "/", "");
|
|
394
|
+
console.log(` ${import_chalk2.default.red("-")} ${import_chalk2.default.cyan(item.key)}`);
|
|
395
|
+
console.log(import_chalk2.default.gray(` defined in ${relativePath}:${item.line}`));
|
|
396
|
+
}
|
|
397
|
+
console.log();
|
|
398
|
+
return { unusedKeys };
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// src/commands/validate.ts
|
|
217
402
|
function checkVariableConsistency(entry) {
|
|
218
403
|
const varsByLocale = [];
|
|
219
404
|
for (const [locale, text] of Object.entries(entry.translations)) {
|
|
@@ -277,8 +462,8 @@ function checkICUTypeConsistency(entry) {
|
|
|
277
462
|
};
|
|
278
463
|
}
|
|
279
464
|
async function validate(options = {}) {
|
|
280
|
-
const { cwd, locales, strict } = options;
|
|
281
|
-
console.log(
|
|
465
|
+
const { cwd, locales, strict, unused: checkUnused } = options;
|
|
466
|
+
console.log(import_chalk3.default.blue("\nValidating translations...\n"));
|
|
282
467
|
const entries = await parseProject({ cwd });
|
|
283
468
|
const issues = [];
|
|
284
469
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -323,44 +508,53 @@ async function validate(options = {}) {
|
|
|
323
508
|
if (issue) issues.push(issue);
|
|
324
509
|
}
|
|
325
510
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
511
|
+
let unusedCount = 0;
|
|
512
|
+
if (checkUnused) {
|
|
513
|
+
const result = await unused({ cwd });
|
|
514
|
+
unusedCount = result.unusedKeys.length;
|
|
515
|
+
}
|
|
516
|
+
if (issues.length === 0 && unusedCount === 0) {
|
|
517
|
+
console.log(import_chalk3.default.green("All translations are valid!\n"));
|
|
518
|
+
console.log(import_chalk3.default.gray(`Checked ${entries.length} translation(s)`));
|
|
329
519
|
if (strict) {
|
|
330
|
-
console.log(
|
|
520
|
+
console.log(import_chalk3.default.gray("(strict mode enabled)"));
|
|
521
|
+
}
|
|
522
|
+
if (checkUnused) {
|
|
523
|
+
console.log(import_chalk3.default.gray("(unused key detection enabled)"));
|
|
331
524
|
}
|
|
332
525
|
return;
|
|
333
526
|
}
|
|
334
|
-
console.log(
|
|
527
|
+
console.log(import_chalk3.default.red(`Found ${issues.length} issue(s):
|
|
335
528
|
`));
|
|
336
529
|
for (const issue of issues) {
|
|
337
|
-
|
|
338
|
-
console.log(`${icon} ${import_chalk2.default.yellow(issue.message)}`);
|
|
530
|
+
console.log(` ${import_chalk3.default.yellow(issue.message)}`);
|
|
339
531
|
for (const entry of issue.entries) {
|
|
340
532
|
const relativePath = entry.file.replace(process.cwd() + "/", "");
|
|
341
|
-
console.log(
|
|
533
|
+
console.log(import_chalk3.default.gray(` ${relativePath}:${entry.line}`));
|
|
342
534
|
for (const [locale, text] of Object.entries(entry.translations)) {
|
|
343
|
-
console.log(` ${
|
|
535
|
+
console.log(` ${import_chalk3.default.cyan(locale)}: ${text}`);
|
|
344
536
|
}
|
|
345
537
|
}
|
|
346
538
|
if (issue.details && issue.details.length > 0) {
|
|
347
539
|
for (const detail of issue.details) {
|
|
348
|
-
console.log(
|
|
540
|
+
console.log(import_chalk3.default.gray(` \u2192 ${detail}`));
|
|
349
541
|
}
|
|
350
542
|
}
|
|
351
543
|
console.log();
|
|
352
544
|
}
|
|
353
|
-
|
|
545
|
+
if ((issues.length > 0 || unusedCount > 0) && !options.noExit) {
|
|
546
|
+
process.exit(1);
|
|
547
|
+
}
|
|
354
548
|
}
|
|
355
549
|
|
|
356
550
|
// src/commands/coverage.ts
|
|
357
|
-
var
|
|
551
|
+
var import_chalk4 = __toESM(require("chalk"));
|
|
358
552
|
async function coverage(options) {
|
|
359
553
|
const { cwd, locales } = options;
|
|
360
|
-
console.log(
|
|
554
|
+
console.log(import_chalk4.default.blue("\nAnalyzing translation coverage...\n"));
|
|
361
555
|
const entries = await parseProject({ cwd });
|
|
362
556
|
if (entries.length === 0) {
|
|
363
|
-
console.log(
|
|
557
|
+
console.log(import_chalk4.default.yellow("No translations found."));
|
|
364
558
|
return;
|
|
365
559
|
}
|
|
366
560
|
const coverageData = [];
|
|
@@ -379,20 +573,20 @@ async function coverage(options) {
|
|
|
379
573
|
percentage
|
|
380
574
|
});
|
|
381
575
|
}
|
|
382
|
-
console.log(
|
|
576
|
+
console.log(import_chalk4.default.bold("Translation Coverage:\n"));
|
|
383
577
|
const maxLocaleLen = Math.max(...locales.map((l) => l.length), 6);
|
|
384
578
|
console.log(
|
|
385
|
-
|
|
579
|
+
import_chalk4.default.gray(
|
|
386
580
|
`${"Locale".padEnd(maxLocaleLen)} ${"Coverage".padStart(10)} ${"Translated".padStart(12)}`
|
|
387
581
|
)
|
|
388
582
|
);
|
|
389
|
-
console.log(
|
|
583
|
+
console.log(import_chalk4.default.gray("\u2500".repeat(maxLocaleLen + 26)));
|
|
390
584
|
for (const data of coverageData) {
|
|
391
|
-
const color = data.percentage === 100 ?
|
|
585
|
+
const color = data.percentage === 100 ? import_chalk4.default.green : data.percentage >= 80 ? import_chalk4.default.yellow : import_chalk4.default.red;
|
|
392
586
|
const bar = createProgressBar(data.percentage, 10);
|
|
393
587
|
const percentStr = `${data.percentage}%`.padStart(4);
|
|
394
588
|
console.log(
|
|
395
|
-
`${data.locale.padEnd(maxLocaleLen)} ${color(bar)} ${color(percentStr)} ${
|
|
589
|
+
`${data.locale.padEnd(maxLocaleLen)} ${color(bar)} ${color(percentStr)} ${import_chalk4.default.gray(`${data.translated}/${data.total}`)}`
|
|
396
590
|
);
|
|
397
591
|
}
|
|
398
592
|
console.log();
|
|
@@ -400,9 +594,9 @@ async function coverage(options) {
|
|
|
400
594
|
const partiallyCovered = coverageData.filter((d) => d.percentage > 0 && d.percentage < 100).length;
|
|
401
595
|
const notCovered = coverageData.filter((d) => d.percentage === 0).length;
|
|
402
596
|
if (fullyCovered === locales.length) {
|
|
403
|
-
console.log(
|
|
597
|
+
console.log(import_chalk4.default.green("All locales are fully translated!"));
|
|
404
598
|
} else {
|
|
405
|
-
console.log(
|
|
599
|
+
console.log(import_chalk4.default.gray(`Full: ${fullyCovered}, Partial: ${partiallyCovered}, Empty: ${notCovered}`));
|
|
406
600
|
}
|
|
407
601
|
}
|
|
408
602
|
function createProgressBar(percentage, width) {
|
|
@@ -410,11 +604,148 @@ function createProgressBar(percentage, width) {
|
|
|
410
604
|
const empty = width - filled;
|
|
411
605
|
return "\u2588".repeat(filled) + "\u2591".repeat(empty);
|
|
412
606
|
}
|
|
607
|
+
|
|
608
|
+
// src/commands/typegen.ts
|
|
609
|
+
var fs2 = __toESM(require("fs"));
|
|
610
|
+
var path = __toESM(require("path"));
|
|
611
|
+
var import_chalk5 = __toESM(require("chalk"));
|
|
612
|
+
var PLURAL_SUFFIXES2 = ["_zero", "_one", "_two", "_few", "_many", "_other"];
|
|
613
|
+
async function typegen(options = {}) {
|
|
614
|
+
const { cwd, output = "src/i18n.d.ts" } = options;
|
|
615
|
+
console.log(import_chalk5.default.blue("\nGenerating TypeScript types...\n"));
|
|
616
|
+
const dictEntries = await extractProjectDictionaryKeys({ cwd });
|
|
617
|
+
const allKeys = /* @__PURE__ */ new Set();
|
|
618
|
+
for (const entry of dictEntries) {
|
|
619
|
+
for (const key of entry.keys) {
|
|
620
|
+
const fullKey = entry.namespace === "default" ? key : `${entry.namespace}:${key}`;
|
|
621
|
+
allKeys.add(fullKey);
|
|
622
|
+
for (const suffix of PLURAL_SUFFIXES2) {
|
|
623
|
+
if (key.endsWith(suffix)) {
|
|
624
|
+
const baseKey = key.slice(0, -suffix.length);
|
|
625
|
+
const fullBaseKey = entry.namespace === "default" ? baseKey : `${entry.namespace}:${baseKey}`;
|
|
626
|
+
allKeys.add(fullBaseKey);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
if (allKeys.size === 0) {
|
|
632
|
+
console.log(import_chalk5.default.yellow("No dictionary keys found. Make sure loadDictionaries() calls are present."));
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
const sortedKeys = [...allKeys].sort();
|
|
636
|
+
const keyUnion = sortedKeys.map((k) => ` | '${k}'`).join("\n");
|
|
637
|
+
const content = `// Auto-generated by inline-i18n typegen
|
|
638
|
+
// Do not edit manually. Re-run: npx inline-i18n typegen
|
|
639
|
+
|
|
640
|
+
import 'inline-i18n-multi'
|
|
641
|
+
|
|
642
|
+
declare module 'inline-i18n-multi' {
|
|
643
|
+
export type TranslationKey =
|
|
644
|
+
${keyUnion}
|
|
645
|
+
|
|
646
|
+
export function t(
|
|
647
|
+
key: TranslationKey,
|
|
648
|
+
vars?: TranslationVars,
|
|
649
|
+
locale?: string
|
|
650
|
+
): string
|
|
651
|
+
|
|
652
|
+
export function hasTranslation(
|
|
653
|
+
key: TranslationKey,
|
|
654
|
+
locale?: string
|
|
655
|
+
): boolean
|
|
656
|
+
}
|
|
657
|
+
`;
|
|
658
|
+
const outputPath = path.resolve(cwd || process.cwd(), output);
|
|
659
|
+
const outputDir = path.dirname(outputPath);
|
|
660
|
+
if (!fs2.existsSync(outputDir)) {
|
|
661
|
+
fs2.mkdirSync(outputDir, { recursive: true });
|
|
662
|
+
}
|
|
663
|
+
fs2.writeFileSync(outputPath, content, "utf-8");
|
|
664
|
+
console.log(import_chalk5.default.green(`Generated ${sortedKeys.length} translation key types`));
|
|
665
|
+
console.log(import_chalk5.default.gray(`Output: ${outputPath}
|
|
666
|
+
`));
|
|
667
|
+
const sample = sortedKeys.slice(0, 5);
|
|
668
|
+
for (const key of sample) {
|
|
669
|
+
console.log(` ${import_chalk5.default.cyan(key)}`);
|
|
670
|
+
}
|
|
671
|
+
if (sortedKeys.length > 5) {
|
|
672
|
+
console.log(import_chalk5.default.gray(` ... and ${sortedKeys.length - 5} more`));
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// src/commands/extract.ts
|
|
677
|
+
var import_chalk6 = __toESM(require("chalk"));
|
|
678
|
+
var fs3 = __toESM(require("fs"));
|
|
679
|
+
var path2 = __toESM(require("path"));
|
|
680
|
+
async function extract(options = {}) {
|
|
681
|
+
const { cwd, output = "translations", format = "flat" } = options;
|
|
682
|
+
console.log(import_chalk6.default.blue("\nExtracting inline translations...\n"));
|
|
683
|
+
const entries = await parseProject({ cwd });
|
|
684
|
+
if (entries.length === 0) {
|
|
685
|
+
console.log(import_chalk6.default.yellow("No translations found."));
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
const byLocale = {};
|
|
689
|
+
const basePath = cwd || process.cwd();
|
|
690
|
+
for (const entry of entries) {
|
|
691
|
+
for (const [locale, text] of Object.entries(entry.translations)) {
|
|
692
|
+
if (!byLocale[locale]) byLocale[locale] = {};
|
|
693
|
+
const relativePath = entry.file.replace(basePath + "/", "");
|
|
694
|
+
const key = `${relativePath}:${entry.line}`;
|
|
695
|
+
byLocale[locale][key] = text;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
const outputDir = path2.resolve(basePath, output);
|
|
699
|
+
if (!fs3.existsSync(outputDir)) {
|
|
700
|
+
fs3.mkdirSync(outputDir, { recursive: true });
|
|
701
|
+
}
|
|
702
|
+
const locales = Object.keys(byLocale).sort();
|
|
703
|
+
for (const locale of locales) {
|
|
704
|
+
const translations = byLocale[locale];
|
|
705
|
+
let content;
|
|
706
|
+
if (format === "nested") {
|
|
707
|
+
const nested = buildNestedObject(translations);
|
|
708
|
+
content = JSON.stringify(nested, null, 2) + "\n";
|
|
709
|
+
} else {
|
|
710
|
+
content = JSON.stringify(translations, null, 2) + "\n";
|
|
711
|
+
}
|
|
712
|
+
const filePath = path2.join(outputDir, `${locale}.json`);
|
|
713
|
+
fs3.writeFileSync(filePath, content);
|
|
714
|
+
console.log(
|
|
715
|
+
import_chalk6.default.green(` ${locale}.json`) + import_chalk6.default.gray(` (${Object.keys(translations).length} keys)`)
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
console.log(import_chalk6.default.gray(`
|
|
719
|
+
Extracted ${entries.length} translation(s) to ${output}/`));
|
|
720
|
+
}
|
|
721
|
+
function buildNestedObject(flat) {
|
|
722
|
+
const result = {};
|
|
723
|
+
for (const [key, value] of Object.entries(flat)) {
|
|
724
|
+
const parts = key.split(".");
|
|
725
|
+
let current = result;
|
|
726
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
727
|
+
const part = parts[i];
|
|
728
|
+
if (!current[part] || typeof current[part] !== "object") {
|
|
729
|
+
current[part] = {};
|
|
730
|
+
}
|
|
731
|
+
current = current[part];
|
|
732
|
+
}
|
|
733
|
+
current[parts[parts.length - 1]] = value;
|
|
734
|
+
}
|
|
735
|
+
return result;
|
|
736
|
+
}
|
|
413
737
|
// Annotate the CommonJS export names for ESM import in node:
|
|
414
738
|
0 && (module.exports = {
|
|
415
739
|
coverage,
|
|
740
|
+
extract,
|
|
741
|
+
extractProjectDictionaryKeys,
|
|
742
|
+
extractProjectTCalls,
|
|
416
743
|
find,
|
|
744
|
+
parseDictionaryKeys,
|
|
417
745
|
parseProject,
|
|
746
|
+
parseTCalls,
|
|
747
|
+
typegen,
|
|
748
|
+
unused,
|
|
418
749
|
validate
|
|
419
750
|
});
|
|
420
751
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/parser.ts","../src/commands/find.ts","../src/commands/validate.ts","../src/commands/coverage.ts"],"sourcesContent":["export { parseProject, type TranslationEntry } from './parser'\nexport { find } from './commands/find'\nexport { validate } from './commands/validate'\nexport { coverage } from './commands/coverage'\n","import { parse } from '@babel/parser'\nimport traverse from '@babel/traverse'\nimport * as fs from 'fs'\nimport fg from 'fast-glob'\n\nexport interface ICUTypeInfo {\n variable: string\n type: string\n}\n\nexport interface TranslationEntry {\n file: string\n line: number\n column: number\n translations: Record<string, string>\n variables: string[]\n /** ICU type information per locale (v0.7.0) */\n icuTypes?: Record<string, ICUTypeInfo[]>\n}\n\ninterface ParseOptions {\n cwd?: string\n include?: string[]\n exclude?: string[]\n}\n\nconst IT_FUNCTION_NAMES = [\n 'it',\n 'it_ja', 'it_zh', 'it_es', 'it_fr', 'it_de',\n 'en_ja', 'en_zh', 'en_es', 'en_fr', 'en_de',\n 'ja_zh', 'ja_es', 'zh_es',\n]\n\nconst PAIR_MAPPING: Record<string, [string, string]> = {\n it: ['ko', 'en'],\n it_ja: ['ko', 'ja'],\n it_zh: ['ko', 'zh'],\n it_es: ['ko', 'es'],\n it_fr: ['ko', 'fr'],\n it_de: ['ko', 'de'],\n en_ja: ['en', 'ja'],\n en_zh: ['en', 'zh'],\n en_es: ['en', 'es'],\n en_fr: ['en', 'fr'],\n en_de: ['en', 'de'],\n ja_zh: ['ja', 'zh'],\n ja_es: ['ja', 'es'],\n zh_es: ['zh', 'es'],\n}\n\nfunction extractVariables(text: string): string[] {\n const matches = text.match(/\\{(\\w+)\\}/g)\n if (!matches) return []\n return matches.map((m) => m.slice(1, -1))\n}\n\nconst ICU_TYPE_PATTERN = /\\{(\\w+),\\s*(\\w+)/g\n\nfunction extractICUTypes(text: string): ICUTypeInfo[] {\n const types: ICUTypeInfo[] = []\n let match: RegExpExecArray | null\n ICU_TYPE_PATTERN.lastIndex = 0\n while ((match = ICU_TYPE_PATTERN.exec(text)) !== null) {\n types.push({ variable: match[1]!, type: match[2]! })\n }\n return types\n}\n\nfunction parseFile(filePath: string): TranslationEntry[] {\n const entries: TranslationEntry[] = []\n const code = fs.readFileSync(filePath, 'utf-8')\n\n let ast\n try {\n ast = parse(code, {\n sourceType: 'module',\n plugins: ['typescript', 'jsx'],\n })\n } catch {\n return []\n }\n\n traverse(ast, {\n CallExpression(nodePath) {\n const { node } = nodePath\n const { callee } = node\n\n if (callee.type !== 'Identifier') return\n if (!IT_FUNCTION_NAMES.includes(callee.name)) return\n\n const funcName = callee.name\n const args = node.arguments\n const loc = node.loc\n\n if (!loc) return\n\n const entry: TranslationEntry = {\n file: filePath,\n line: loc.start.line,\n column: loc.start.column,\n translations: {},\n variables: [],\n }\n\n if (args[0]?.type === 'ObjectExpression') {\n const obj = args[0]\n for (const prop of obj.properties) {\n if (\n prop.type === 'ObjectProperty' &&\n prop.key.type === 'Identifier' &&\n prop.value.type === 'StringLiteral'\n ) {\n entry.translations[prop.key.name] = prop.value.value\n entry.variables.push(...extractVariables(prop.value.value))\n // Extract ICU type info (v0.7.0)\n const types = extractICUTypes(prop.value.value)\n if (types.length > 0) {\n if (!entry.icuTypes) entry.icuTypes = {}\n entry.icuTypes[prop.key.name] = types\n }\n }\n }\n } else if (\n args[0]?.type === 'StringLiteral' &&\n args[1]?.type === 'StringLiteral'\n ) {\n const [lang1, lang2] = PAIR_MAPPING[funcName] || ['ko', 'en']\n entry.translations[lang1] = args[0].value\n entry.translations[lang2] = args[1].value\n entry.variables.push(...extractVariables(args[0].value))\n entry.variables.push(...extractVariables(args[1].value))\n // Extract ICU type info (v0.7.0)\n const types1 = extractICUTypes(args[0].value)\n if (types1.length > 0) {\n if (!entry.icuTypes) entry.icuTypes = {}\n entry.icuTypes[lang1] = types1\n }\n const types2 = extractICUTypes(args[1].value)\n if (types2.length > 0) {\n if (!entry.icuTypes) entry.icuTypes = {}\n entry.icuTypes[lang2] = types2\n }\n }\n\n entry.variables = [...new Set(entry.variables)]\n\n if (Object.keys(entry.translations).length > 0) {\n entries.push(entry)\n }\n },\n })\n\n return entries\n}\n\nexport async function parseProject(options: ParseOptions = {}): Promise<TranslationEntry[]> {\n const {\n cwd = process.cwd(),\n include = ['**/*.{ts,tsx,js,jsx}'],\n exclude = ['**/node_modules/**', '**/dist/**', '**/.next/**'],\n } = options\n\n const files = await fg(include, {\n cwd,\n ignore: exclude,\n absolute: true,\n })\n\n const allEntries: TranslationEntry[] = []\n\n for (const file of files) {\n const entries = parseFile(file)\n allEntries.push(...entries)\n }\n\n return allEntries\n}\n","import chalk from 'chalk'\nimport { parseProject, type TranslationEntry } from '../parser'\n\ninterface FindOptions {\n query: string\n cwd?: string\n}\n\nexport async function find(options: FindOptions): Promise<void> {\n const { query, cwd } = options\n\n console.log(chalk.blue(`\\nSearching for: \"${query}\"\\n`))\n\n const entries = await parseProject({ cwd })\n const results: TranslationEntry[] = []\n\n const lowerQuery = query.toLowerCase()\n\n for (const entry of entries) {\n const values = Object.values(entry.translations)\n const matches = values.some((v) => v.toLowerCase().includes(lowerQuery))\n\n if (matches) {\n results.push(entry)\n }\n }\n\n if (results.length === 0) {\n console.log(chalk.yellow('No results found.'))\n return\n }\n\n console.log(chalk.green(`Found ${results.length} occurrence(s):\\n`))\n\n for (const result of results) {\n const relativePath = result.file.replace(process.cwd() + '/', '')\n console.log(chalk.gray(`${relativePath}:${result.line}:${result.column}`))\n\n for (const [locale, text] of Object.entries(result.translations)) {\n const highlighted = text.replace(\n new RegExp(`(${query})`, 'gi'),\n chalk.yellow('$1')\n )\n console.log(` ${chalk.cyan(locale)}: ${highlighted}`)\n }\n console.log()\n }\n}\n","import chalk from 'chalk'\nimport { parseProject, type TranslationEntry } from '../parser'\n\ninterface ValidateOptions {\n cwd?: string\n locales?: string[]\n strict?: boolean\n}\n\ninterface Issue {\n type: 'inconsistent' | 'missing' | 'variable_mismatch' | 'icu_type_mismatch'\n message: string\n entries: TranslationEntry[]\n details?: string[]\n}\n\nfunction checkVariableConsistency(entry: TranslationEntry): Issue | null {\n const varsByLocale: { locale: string; vars: string[] }[] = []\n\n for (const [locale, text] of Object.entries(entry.translations)) {\n const matches = text.match(/\\{(\\w+)\\}/g) || []\n const varNames = [...new Set(matches.map((v) => v.slice(1, -1)))].sort()\n varsByLocale.push({ locale, vars: varNames })\n }\n\n if (varsByLocale.length < 2) return null\n\n const reference = varsByLocale[0]!\n const details: string[] = []\n\n for (let i = 1; i < varsByLocale.length; i++) {\n const current = varsByLocale[i]!\n const refSet = new Set(reference.vars)\n const curSet = new Set(current.vars)\n\n const onlyInRef = reference.vars.filter((v) => !curSet.has(v))\n const onlyInCur = current.vars.filter((v) => !refSet.has(v))\n\n if (onlyInRef.length > 0) {\n details.push(\n `${reference.locale} has {${onlyInRef.join('}, {')}} missing in ${current.locale}`,\n )\n }\n if (onlyInCur.length > 0) {\n details.push(\n `${current.locale} has {${onlyInCur.join('}, {')}} missing in ${reference.locale}`,\n )\n }\n }\n\n if (details.length === 0) return null\n\n return {\n type: 'variable_mismatch',\n message: 'Variable mismatch between translations',\n entries: [entry],\n details,\n }\n}\n\nfunction checkICUTypeConsistency(entry: TranslationEntry): Issue | null {\n if (!entry.icuTypes) return null\n\n const locales = Object.keys(entry.icuTypes)\n if (locales.length < 2) return null\n\n const details: string[] = []\n const reference = locales[0]!\n const refTypes = entry.icuTypes[reference]!\n\n for (let i = 1; i < locales.length; i++) {\n const locale = locales[i]!\n const curTypes = entry.icuTypes[locale]!\n\n for (const refType of refTypes) {\n const match = curTypes.find((t: { variable: string; type: string }) => t.variable === refType.variable)\n if (match && match.type !== refType.type) {\n details.push(\n `{${refType.variable}} is \"${refType.type}\" in ${reference} but \"${match.type}\" in ${locale}`,\n )\n }\n }\n }\n\n if (details.length === 0) return null\n\n return {\n type: 'icu_type_mismatch',\n message: 'ICU type mismatch between translations',\n entries: [entry],\n details,\n }\n}\n\nexport async function validate(options: ValidateOptions = {}): Promise<void> {\n const { cwd, locales, strict } = options\n\n console.log(chalk.blue('\\nValidating translations...\\n'))\n\n const entries = await parseProject({ cwd })\n const issues: Issue[] = []\n\n // group by first language text (usually ko)\n const groups = new Map<string, TranslationEntry[]>()\n\n for (const entry of entries) {\n const key = Object.values(entry.translations)[0] || ''\n if (!groups.has(key)) {\n groups.set(key, [])\n }\n groups.get(key)!.push(entry)\n }\n\n // check for inconsistent translations\n for (const [key, group] of groups) {\n if (group.length < 2) continue\n\n const translationSets = group.map((e) => JSON.stringify(e.translations))\n const uniqueSets = [...new Set(translationSets)]\n\n if (uniqueSets.length > 1) {\n issues.push({\n type: 'inconsistent',\n message: `Inconsistent translations for \"${key}\"`,\n entries: group,\n })\n }\n }\n\n // check for missing locales\n if (locales && locales.length > 0) {\n for (const entry of entries) {\n const missing = locales.filter((l) => !entry.translations[l])\n\n if (missing.length > 0) {\n issues.push({\n type: 'missing',\n message: `Missing locales: ${missing.join(', ')}`,\n entries: [entry],\n })\n }\n }\n }\n\n // check for variable name consistency (enhanced in v0.7.0)\n for (const entry of entries) {\n const issue = checkVariableConsistency(entry)\n if (issue) issues.push(issue)\n }\n\n // check for ICU type consistency (strict mode, v0.7.0)\n if (strict) {\n for (const entry of entries) {\n const issue = checkICUTypeConsistency(entry)\n if (issue) issues.push(issue)\n }\n }\n\n // print results\n if (issues.length === 0) {\n console.log(chalk.green('✅ All translations are valid!\\n'))\n console.log(chalk.gray(`Checked ${entries.length} translation(s)`))\n if (strict) {\n console.log(chalk.gray('(strict mode enabled)'))\n }\n return\n }\n\n console.log(chalk.red(`❌ Found ${issues.length} issue(s):\\n`))\n\n for (const issue of issues) {\n const icon =\n issue.type === 'inconsistent'\n ? '⚠️'\n : issue.type === 'missing'\n ? '📭'\n : issue.type === 'icu_type_mismatch'\n ? '🔧'\n : '🔀'\n\n console.log(`${icon} ${chalk.yellow(issue.message)}`)\n\n for (const entry of issue.entries) {\n const relativePath = entry.file.replace(process.cwd() + '/', '')\n console.log(chalk.gray(` ${relativePath}:${entry.line}`))\n\n for (const [locale, text] of Object.entries(entry.translations)) {\n console.log(` ${chalk.cyan(locale)}: ${text}`)\n }\n }\n\n if (issue.details && issue.details.length > 0) {\n for (const detail of issue.details) {\n console.log(chalk.gray(` → ${detail}`))\n }\n }\n\n console.log()\n }\n\n process.exit(1)\n}\n","import chalk from 'chalk'\nimport { parseProject } from '../parser'\n\ninterface CoverageOptions {\n cwd?: string\n locales: string[]\n}\n\ninterface LocaleCoverage {\n locale: string\n total: number\n translated: number\n percentage: number\n}\n\nexport async function coverage(options: CoverageOptions): Promise<void> {\n const { cwd, locales } = options\n\n console.log(chalk.blue('\\nAnalyzing translation coverage...\\n'))\n\n const entries = await parseProject({ cwd })\n\n if (entries.length === 0) {\n console.log(chalk.yellow('No translations found.'))\n return\n }\n\n const coverageData: LocaleCoverage[] = []\n\n for (const locale of locales) {\n let translated = 0\n\n for (const entry of entries) {\n if (entry.translations[locale]) {\n translated++\n }\n }\n\n const percentage = Math.round((translated / entries.length) * 100)\n\n coverageData.push({\n locale,\n total: entries.length,\n translated,\n percentage,\n })\n }\n\n // print coverage table\n console.log(chalk.bold('Translation Coverage:\\n'))\n\n const maxLocaleLen = Math.max(...locales.map((l) => l.length), 6)\n\n console.log(\n chalk.gray(\n `${'Locale'.padEnd(maxLocaleLen)} ${'Coverage'.padStart(10)} ${'Translated'.padStart(12)}`\n )\n )\n console.log(chalk.gray('─'.repeat(maxLocaleLen + 26)))\n\n for (const data of coverageData) {\n const color =\n data.percentage === 100 ? chalk.green :\n data.percentage >= 80 ? chalk.yellow : chalk.red\n\n const bar = createProgressBar(data.percentage, 10)\n const percentStr = `${data.percentage}%`.padStart(4)\n\n console.log(\n `${data.locale.padEnd(maxLocaleLen)} ${color(bar)} ${color(percentStr)} ${chalk.gray(`${data.translated}/${data.total}`)}`\n )\n }\n\n console.log()\n\n // summary\n const fullyCovered = coverageData.filter((d) => d.percentage === 100).length\n const partiallyCovered = coverageData.filter((d) => d.percentage > 0 && d.percentage < 100).length\n const notCovered = coverageData.filter((d) => d.percentage === 0).length\n\n if (fullyCovered === locales.length) {\n console.log(chalk.green('✅ All locales are fully translated!'))\n } else {\n console.log(chalk.gray(`Full: ${fullyCovered}, Partial: ${partiallyCovered}, Empty: ${notCovered}`))\n }\n}\n\nfunction createProgressBar(percentage: number, width: number): string {\n const filled = Math.round((percentage / 100) * width)\n const empty = width - filled\n\n return '█'.repeat(filled) + '░'.repeat(empty)\n}\n"],"mappings":"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","names":["traverse","fg","chalk","import_chalk","chalk","import_chalk","chalk"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/parser.ts","../src/commands/find.ts","../src/commands/validate.ts","../src/commands/unused.ts","../src/commands/coverage.ts","../src/commands/typegen.ts","../src/commands/extract.ts"],"sourcesContent":["export {\n parseProject,\n parseDictionaryKeys,\n parseTCalls,\n extractProjectDictionaryKeys,\n extractProjectTCalls,\n type TranslationEntry,\n type DictionaryKeyEntry,\n type TCallEntry,\n} from './parser'\nexport { find } from './commands/find'\nexport { validate } from './commands/validate'\nexport { coverage } from './commands/coverage'\nexport { unused } from './commands/unused'\nexport { typegen } from './commands/typegen'\nexport { extract } from './commands/extract'\n","import { parse } from '@babel/parser'\nimport traverse from '@babel/traverse'\nimport * as fs from 'fs'\nimport fg from 'fast-glob'\n\n// v0.8.0: Dictionary key and t() call extraction\n\nexport interface DictionaryKeyEntry {\n file: string\n line: number\n namespace: string\n keys: string[]\n}\n\nexport interface TCallEntry {\n file: string\n line: number\n key: string\n}\n\nexport interface ICUTypeInfo {\n variable: string\n type: string\n}\n\nexport interface TranslationEntry {\n file: string\n line: number\n column: number\n translations: Record<string, string>\n variables: string[]\n /** ICU type information per locale (v0.7.0) */\n icuTypes?: Record<string, ICUTypeInfo[]>\n}\n\ninterface ParseOptions {\n cwd?: string\n include?: string[]\n exclude?: string[]\n}\n\nconst IT_FUNCTION_NAMES = [\n 'it',\n 'it_ja', 'it_zh', 'it_es', 'it_fr', 'it_de',\n 'en_ja', 'en_zh', 'en_es', 'en_fr', 'en_de',\n 'ja_zh', 'ja_es', 'zh_es',\n]\n\nconst PAIR_MAPPING: Record<string, [string, string]> = {\n it: ['ko', 'en'],\n it_ja: ['ko', 'ja'],\n it_zh: ['ko', 'zh'],\n it_es: ['ko', 'es'],\n it_fr: ['ko', 'fr'],\n it_de: ['ko', 'de'],\n en_ja: ['en', 'ja'],\n en_zh: ['en', 'zh'],\n en_es: ['en', 'es'],\n en_fr: ['en', 'fr'],\n en_de: ['en', 'de'],\n ja_zh: ['ja', 'zh'],\n ja_es: ['ja', 'es'],\n zh_es: ['zh', 'es'],\n}\n\nfunction extractVariables(text: string): string[] {\n const matches = text.match(/\\{(\\w+)\\}/g)\n if (!matches) return []\n return matches.map((m) => m.slice(1, -1))\n}\n\nconst ICU_TYPE_PATTERN = /\\{(\\w+),\\s*(\\w+)/g\n\nfunction extractICUTypes(text: string): ICUTypeInfo[] {\n const types: ICUTypeInfo[] = []\n let match: RegExpExecArray | null\n ICU_TYPE_PATTERN.lastIndex = 0\n while ((match = ICU_TYPE_PATTERN.exec(text)) !== null) {\n types.push({ variable: match[1]!, type: match[2]! })\n }\n return types\n}\n\nfunction parseFile(filePath: string): TranslationEntry[] {\n const entries: TranslationEntry[] = []\n const code = fs.readFileSync(filePath, 'utf-8')\n\n let ast\n try {\n ast = parse(code, {\n sourceType: 'module',\n plugins: ['typescript', 'jsx'],\n })\n } catch {\n return []\n }\n\n traverse(ast, {\n CallExpression(nodePath) {\n const { node } = nodePath\n const { callee } = node\n\n if (callee.type !== 'Identifier') return\n if (!IT_FUNCTION_NAMES.includes(callee.name)) return\n\n const funcName = callee.name\n const args = node.arguments\n const loc = node.loc\n\n if (!loc) return\n\n const entry: TranslationEntry = {\n file: filePath,\n line: loc.start.line,\n column: loc.start.column,\n translations: {},\n variables: [],\n }\n\n if (args[0]?.type === 'ObjectExpression') {\n const obj = args[0]\n for (const prop of obj.properties) {\n if (\n prop.type === 'ObjectProperty' &&\n prop.key.type === 'Identifier' &&\n prop.value.type === 'StringLiteral'\n ) {\n entry.translations[prop.key.name] = prop.value.value\n entry.variables.push(...extractVariables(prop.value.value))\n // Extract ICU type info (v0.7.0)\n const types = extractICUTypes(prop.value.value)\n if (types.length > 0) {\n if (!entry.icuTypes) entry.icuTypes = {}\n entry.icuTypes[prop.key.name] = types\n }\n }\n }\n } else if (\n args[0]?.type === 'StringLiteral' &&\n args[1]?.type === 'StringLiteral'\n ) {\n const [lang1, lang2] = PAIR_MAPPING[funcName] || ['ko', 'en']\n entry.translations[lang1] = args[0].value\n entry.translations[lang2] = args[1].value\n entry.variables.push(...extractVariables(args[0].value))\n entry.variables.push(...extractVariables(args[1].value))\n // Extract ICU type info (v0.7.0)\n const types1 = extractICUTypes(args[0].value)\n if (types1.length > 0) {\n if (!entry.icuTypes) entry.icuTypes = {}\n entry.icuTypes[lang1] = types1\n }\n const types2 = extractICUTypes(args[1].value)\n if (types2.length > 0) {\n if (!entry.icuTypes) entry.icuTypes = {}\n entry.icuTypes[lang2] = types2\n }\n }\n\n entry.variables = [...new Set(entry.variables)]\n\n if (Object.keys(entry.translations).length > 0) {\n entries.push(entry)\n }\n },\n })\n\n return entries\n}\n\n// v0.8.0: Flatten nested ObjectExpression to dot-notation keys\nfunction flattenObjectKeys(\n node: { type: string; properties?: unknown[] },\n prefix: string = '',\n): string[] {\n const keys: string[] = []\n if (node.type !== 'ObjectExpression' || !node.properties) return keys\n\n for (const prop of node.properties as Array<{\n type: string\n key: { type: string; name?: string; value?: string }\n value: { type: string; properties?: unknown[] }\n }>) {\n if (prop.type !== 'ObjectProperty') continue\n\n let propName: string | undefined\n if (prop.key.type === 'Identifier') propName = prop.key.name\n else if (prop.key.type === 'StringLiteral') propName = prop.key.value\n\n if (!propName) continue\n\n const fullKey = prefix ? `${prefix}.${propName}` : propName\n\n if (prop.value.type === 'StringLiteral') {\n keys.push(fullKey)\n } else if (prop.value.type === 'ObjectExpression') {\n keys.push(...flattenObjectKeys(prop.value, fullKey))\n }\n }\n return keys\n}\n\n// v0.8.0: Extract dictionary keys from loadDictionaries() calls\nexport function parseDictionaryKeys(filePath: string): DictionaryKeyEntry[] {\n const entries: DictionaryKeyEntry[] = []\n const code = fs.readFileSync(filePath, 'utf-8')\n\n let ast\n try {\n ast = parse(code, {\n sourceType: 'module',\n plugins: ['typescript', 'jsx'],\n })\n } catch {\n return []\n }\n\n traverse(ast, {\n CallExpression(nodePath) {\n const { node } = nodePath\n const { callee } = node\n\n if (callee.type !== 'Identifier' || callee.name !== 'loadDictionaries') return\n\n const args = node.arguments\n const loc = node.loc\n if (!loc || !args[0] || args[0].type !== 'ObjectExpression') return\n\n // Second argument is optional namespace\n let namespace = 'default'\n if (args[1]?.type === 'StringLiteral') {\n namespace = args[1].value\n }\n\n // First argument: { locale: { key: value } }\n // Collect keys from the first locale's dict (all locales should have same keys)\n const dictObj = args[0]\n const allKeys = new Set<string>()\n\n for (const localeProp of dictObj.properties as Array<{\n type: string\n value: { type: string; properties?: unknown[] }\n }>) {\n if (localeProp.type !== 'ObjectProperty') continue\n if (localeProp.value.type !== 'ObjectExpression') continue\n\n const localeKeys = flattenObjectKeys(localeProp.value)\n for (const key of localeKeys) {\n allKeys.add(key)\n }\n }\n\n if (allKeys.size > 0) {\n entries.push({\n file: filePath,\n line: loc.start.line,\n namespace,\n keys: [...allKeys],\n })\n }\n },\n })\n\n return entries\n}\n\n// v0.8.0: Extract t() call sites\nexport function parseTCalls(filePath: string): TCallEntry[] {\n const entries: TCallEntry[] = []\n const code = fs.readFileSync(filePath, 'utf-8')\n\n let ast\n try {\n ast = parse(code, {\n sourceType: 'module',\n plugins: ['typescript', 'jsx'],\n })\n } catch {\n return []\n }\n\n traverse(ast, {\n CallExpression(nodePath) {\n const { node } = nodePath\n const { callee } = node\n\n if (callee.type !== 'Identifier' || callee.name !== 't') return\n\n const args = node.arguments\n const loc = node.loc\n if (!loc || !args[0] || args[0].type !== 'StringLiteral') return\n\n entries.push({\n file: filePath,\n line: loc.start.line,\n key: args[0].value,\n })\n },\n })\n\n return entries\n}\n\nexport async function extractProjectDictionaryKeys(options: ParseOptions = {}): Promise<DictionaryKeyEntry[]> {\n const {\n cwd = process.cwd(),\n include = ['**/*.{ts,tsx,js,jsx}'],\n exclude = ['**/node_modules/**', '**/dist/**', '**/.next/**'],\n } = options\n\n const files = await fg(include, { cwd, ignore: exclude, absolute: true })\n const allEntries: DictionaryKeyEntry[] = []\n\n for (const file of files) {\n allEntries.push(...parseDictionaryKeys(file))\n }\n\n return allEntries\n}\n\nexport async function extractProjectTCalls(options: ParseOptions = {}): Promise<TCallEntry[]> {\n const {\n cwd = process.cwd(),\n include = ['**/*.{ts,tsx,js,jsx}'],\n exclude = ['**/node_modules/**', '**/dist/**', '**/.next/**'],\n } = options\n\n const files = await fg(include, { cwd, ignore: exclude, absolute: true })\n const allEntries: TCallEntry[] = []\n\n for (const file of files) {\n allEntries.push(...parseTCalls(file))\n }\n\n return allEntries\n}\n\nexport async function parseProject(options: ParseOptions = {}): Promise<TranslationEntry[]> {\n const {\n cwd = process.cwd(),\n include = ['**/*.{ts,tsx,js,jsx}'],\n exclude = ['**/node_modules/**', '**/dist/**', '**/.next/**'],\n } = options\n\n const files = await fg(include, {\n cwd,\n ignore: exclude,\n absolute: true,\n })\n\n const allEntries: TranslationEntry[] = []\n\n for (const file of files) {\n const entries = parseFile(file)\n allEntries.push(...entries)\n }\n\n return allEntries\n}\n","import chalk from 'chalk'\nimport { parseProject, type TranslationEntry } from '../parser'\n\ninterface FindOptions {\n query: string\n cwd?: string\n}\n\nexport async function find(options: FindOptions): Promise<void> {\n const { query, cwd } = options\n\n console.log(chalk.blue(`\\nSearching for: \"${query}\"\\n`))\n\n const entries = await parseProject({ cwd })\n const results: TranslationEntry[] = []\n\n const lowerQuery = query.toLowerCase()\n\n for (const entry of entries) {\n const values = Object.values(entry.translations)\n const matches = values.some((v) => v.toLowerCase().includes(lowerQuery))\n\n if (matches) {\n results.push(entry)\n }\n }\n\n if (results.length === 0) {\n console.log(chalk.yellow('No results found.'))\n return\n }\n\n console.log(chalk.green(`Found ${results.length} occurrence(s):\\n`))\n\n for (const result of results) {\n const relativePath = result.file.replace(process.cwd() + '/', '')\n console.log(chalk.gray(`${relativePath}:${result.line}:${result.column}`))\n\n for (const [locale, text] of Object.entries(result.translations)) {\n const highlighted = text.replace(\n new RegExp(`(${query})`, 'gi'),\n chalk.yellow('$1')\n )\n console.log(` ${chalk.cyan(locale)}: ${highlighted}`)\n }\n console.log()\n }\n}\n","import chalk from 'chalk'\nimport { parseProject, type TranslationEntry } from '../parser'\nimport { unused } from './unused'\n\ninterface ValidateOptions {\n cwd?: string\n locales?: string[]\n strict?: boolean\n unused?: boolean\n noExit?: boolean\n}\n\ninterface Issue {\n type: 'inconsistent' | 'missing' | 'variable_mismatch' | 'icu_type_mismatch'\n message: string\n entries: TranslationEntry[]\n details?: string[]\n}\n\nfunction checkVariableConsistency(entry: TranslationEntry): Issue | null {\n const varsByLocale: { locale: string; vars: string[] }[] = []\n\n for (const [locale, text] of Object.entries(entry.translations)) {\n const matches = text.match(/\\{(\\w+)\\}/g) || []\n const varNames = [...new Set(matches.map((v) => v.slice(1, -1)))].sort()\n varsByLocale.push({ locale, vars: varNames })\n }\n\n if (varsByLocale.length < 2) return null\n\n const reference = varsByLocale[0]!\n const details: string[] = []\n\n for (let i = 1; i < varsByLocale.length; i++) {\n const current = varsByLocale[i]!\n const refSet = new Set(reference.vars)\n const curSet = new Set(current.vars)\n\n const onlyInRef = reference.vars.filter((v) => !curSet.has(v))\n const onlyInCur = current.vars.filter((v) => !refSet.has(v))\n\n if (onlyInRef.length > 0) {\n details.push(\n `${reference.locale} has {${onlyInRef.join('}, {')}} missing in ${current.locale}`,\n )\n }\n if (onlyInCur.length > 0) {\n details.push(\n `${current.locale} has {${onlyInCur.join('}, {')}} missing in ${reference.locale}`,\n )\n }\n }\n\n if (details.length === 0) return null\n\n return {\n type: 'variable_mismatch',\n message: 'Variable mismatch between translations',\n entries: [entry],\n details,\n }\n}\n\nfunction checkICUTypeConsistency(entry: TranslationEntry): Issue | null {\n if (!entry.icuTypes) return null\n\n const locales = Object.keys(entry.icuTypes)\n if (locales.length < 2) return null\n\n const details: string[] = []\n const reference = locales[0]!\n const refTypes = entry.icuTypes[reference]!\n\n for (let i = 1; i < locales.length; i++) {\n const locale = locales[i]!\n const curTypes = entry.icuTypes[locale]!\n\n for (const refType of refTypes) {\n const match = curTypes.find((t: { variable: string; type: string }) => t.variable === refType.variable)\n if (match && match.type !== refType.type) {\n details.push(\n `{${refType.variable}} is \"${refType.type}\" in ${reference} but \"${match.type}\" in ${locale}`,\n )\n }\n }\n }\n\n if (details.length === 0) return null\n\n return {\n type: 'icu_type_mismatch',\n message: 'ICU type mismatch between translations',\n entries: [entry],\n details,\n }\n}\n\nexport async function validate(options: ValidateOptions = {}): Promise<void> {\n const { cwd, locales, strict, unused: checkUnused } = options\n\n console.log(chalk.blue('\\nValidating translations...\\n'))\n\n const entries = await parseProject({ cwd })\n const issues: Issue[] = []\n\n // group by first language text (usually ko)\n const groups = new Map<string, TranslationEntry[]>()\n\n for (const entry of entries) {\n const key = Object.values(entry.translations)[0] || ''\n if (!groups.has(key)) {\n groups.set(key, [])\n }\n groups.get(key)!.push(entry)\n }\n\n // check for inconsistent translations\n for (const [key, group] of groups) {\n if (group.length < 2) continue\n\n const translationSets = group.map((e) => JSON.stringify(e.translations))\n const uniqueSets = [...new Set(translationSets)]\n\n if (uniqueSets.length > 1) {\n issues.push({\n type: 'inconsistent',\n message: `Inconsistent translations for \"${key}\"`,\n entries: group,\n })\n }\n }\n\n // check for missing locales\n if (locales && locales.length > 0) {\n for (const entry of entries) {\n const missing = locales.filter((l) => !entry.translations[l])\n\n if (missing.length > 0) {\n issues.push({\n type: 'missing',\n message: `Missing locales: ${missing.join(', ')}`,\n entries: [entry],\n })\n }\n }\n }\n\n // check for variable name consistency (enhanced in v0.7.0)\n for (const entry of entries) {\n const issue = checkVariableConsistency(entry)\n if (issue) issues.push(issue)\n }\n\n // check for ICU type consistency (strict mode, v0.7.0)\n if (strict) {\n for (const entry of entries) {\n const issue = checkICUTypeConsistency(entry)\n if (issue) issues.push(issue)\n }\n }\n\n // unused key detection (v0.8.0)\n let unusedCount = 0\n if (checkUnused) {\n const result = await unused({ cwd })\n unusedCount = result.unusedKeys.length\n }\n\n // print results\n if (issues.length === 0 && unusedCount === 0) {\n console.log(chalk.green('All translations are valid!\\n'))\n console.log(chalk.gray(`Checked ${entries.length} translation(s)`))\n if (strict) {\n console.log(chalk.gray('(strict mode enabled)'))\n }\n if (checkUnused) {\n console.log(chalk.gray('(unused key detection enabled)'))\n }\n return\n }\n\n console.log(chalk.red(`Found ${issues.length} issue(s):\\n`))\n\n for (const issue of issues) {\n console.log(` ${chalk.yellow(issue.message)}`)\n\n for (const entry of issue.entries) {\n const relativePath = entry.file.replace(process.cwd() + '/', '')\n console.log(chalk.gray(` ${relativePath}:${entry.line}`))\n\n for (const [locale, text] of Object.entries(entry.translations)) {\n console.log(` ${chalk.cyan(locale)}: ${text}`)\n }\n }\n\n if (issue.details && issue.details.length > 0) {\n for (const detail of issue.details) {\n console.log(chalk.gray(` → ${detail}`))\n }\n }\n\n console.log()\n }\n\n if ((issues.length > 0 || unusedCount > 0) && !options.noExit) {\n process.exit(1)\n }\n}\n","import chalk from 'chalk'\nimport { extractProjectDictionaryKeys, extractProjectTCalls } from '../parser'\n\ninterface UnusedKey {\n namespace: string\n key: string\n definedIn: string\n line: number\n}\n\nconst PLURAL_SUFFIXES = ['_zero', '_one', '_two', '_few', '_many', '_other']\n\nexport async function unused(options: { cwd?: string } = {}): Promise<{ unusedKeys: UnusedKey[] }> {\n const { cwd } = options\n\n console.log(chalk.blue('\\nDetecting unused translations...\\n'))\n\n const dictEntries = await extractProjectDictionaryKeys({ cwd })\n const tCalls = await extractProjectTCalls({ cwd })\n\n // Build set of used keys (normalized: namespace:key or key for default)\n const usedKeys = new Set<string>()\n for (const call of tCalls) {\n usedKeys.add(call.key)\n // Also add with default namespace if no namespace prefix\n if (!call.key.includes(':')) {\n usedKeys.add(call.key)\n }\n }\n\n // Check each dictionary key against used keys\n const unusedKeys: UnusedKey[] = []\n\n for (const entry of dictEntries) {\n for (const key of entry.keys) {\n const fullKey = entry.namespace === 'default' ? key : `${entry.namespace}:${key}`\n\n // Check if key is directly used\n if (usedKeys.has(fullKey)) continue\n\n // Check if this is a plural suffix variant (e.g. count_one, count_other)\n // and the base key is used via t('items.count', { count: N })\n let isPluralVariant = false\n for (const suffix of PLURAL_SUFFIXES) {\n if (key.endsWith(suffix)) {\n const baseKey = key.slice(0, -suffix.length)\n const fullBaseKey = entry.namespace === 'default' ? baseKey : `${entry.namespace}:${baseKey}`\n if (usedKeys.has(fullBaseKey)) {\n isPluralVariant = true\n break\n }\n }\n }\n if (isPluralVariant) continue\n\n unusedKeys.push({\n namespace: entry.namespace,\n key: fullKey,\n definedIn: entry.file,\n line: entry.line,\n })\n }\n }\n\n // Report results\n if (unusedKeys.length === 0) {\n const totalKeys = dictEntries.reduce((sum, e) => sum + e.keys.length, 0)\n console.log(chalk.green('No unused translations found!\\n'))\n console.log(chalk.gray(`Checked ${totalKeys} dictionary key(s) against ${tCalls.length} t() call(s)`))\n return { unusedKeys }\n }\n\n console.log(chalk.yellow(`Found ${unusedKeys.length} unused translation key(s):\\n`))\n for (const item of unusedKeys) {\n const relativePath = item.definedIn.replace(process.cwd() + '/', '')\n console.log(` ${chalk.red('-')} ${chalk.cyan(item.key)}`)\n console.log(chalk.gray(` defined in ${relativePath}:${item.line}`))\n }\n\n console.log()\n return { unusedKeys }\n}\n","import chalk from 'chalk'\nimport { parseProject } from '../parser'\n\ninterface CoverageOptions {\n cwd?: string\n locales: string[]\n}\n\ninterface LocaleCoverage {\n locale: string\n total: number\n translated: number\n percentage: number\n}\n\nexport async function coverage(options: CoverageOptions): Promise<void> {\n const { cwd, locales } = options\n\n console.log(chalk.blue('\\nAnalyzing translation coverage...\\n'))\n\n const entries = await parseProject({ cwd })\n\n if (entries.length === 0) {\n console.log(chalk.yellow('No translations found.'))\n return\n }\n\n const coverageData: LocaleCoverage[] = []\n\n for (const locale of locales) {\n let translated = 0\n\n for (const entry of entries) {\n if (entry.translations[locale]) {\n translated++\n }\n }\n\n const percentage = Math.round((translated / entries.length) * 100)\n\n coverageData.push({\n locale,\n total: entries.length,\n translated,\n percentage,\n })\n }\n\n // print coverage table\n console.log(chalk.bold('Translation Coverage:\\n'))\n\n const maxLocaleLen = Math.max(...locales.map((l) => l.length), 6)\n\n console.log(\n chalk.gray(\n `${'Locale'.padEnd(maxLocaleLen)} ${'Coverage'.padStart(10)} ${'Translated'.padStart(12)}`\n )\n )\n console.log(chalk.gray('─'.repeat(maxLocaleLen + 26)))\n\n for (const data of coverageData) {\n const color =\n data.percentage === 100 ? chalk.green :\n data.percentage >= 80 ? chalk.yellow : chalk.red\n\n const bar = createProgressBar(data.percentage, 10)\n const percentStr = `${data.percentage}%`.padStart(4)\n\n console.log(\n `${data.locale.padEnd(maxLocaleLen)} ${color(bar)} ${color(percentStr)} ${chalk.gray(`${data.translated}/${data.total}`)}`\n )\n }\n\n console.log()\n\n // summary\n const fullyCovered = coverageData.filter((d) => d.percentage === 100).length\n const partiallyCovered = coverageData.filter((d) => d.percentage > 0 && d.percentage < 100).length\n const notCovered = coverageData.filter((d) => d.percentage === 0).length\n\n if (fullyCovered === locales.length) {\n console.log(chalk.green('All locales are fully translated!'))\n } else {\n console.log(chalk.gray(`Full: ${fullyCovered}, Partial: ${partiallyCovered}, Empty: ${notCovered}`))\n }\n}\n\nfunction createProgressBar(percentage: number, width: number): string {\n const filled = Math.round((percentage / 100) * width)\n const empty = width - filled\n\n return '█'.repeat(filled) + '░'.repeat(empty)\n}\n","import * as fs from 'fs'\nimport * as path from 'path'\nimport chalk from 'chalk'\nimport { extractProjectDictionaryKeys } from '../parser'\n\ninterface TypegenOptions {\n cwd?: string\n output?: string\n}\n\nconst PLURAL_SUFFIXES = ['_zero', '_one', '_two', '_few', '_many', '_other']\n\nexport async function typegen(options: TypegenOptions = {}): Promise<void> {\n const { cwd, output = 'src/i18n.d.ts' } = options\n\n console.log(chalk.blue('\\nGenerating TypeScript types...\\n'))\n\n const dictEntries = await extractProjectDictionaryKeys({ cwd })\n\n // Collect all unique full keys (namespace:key or key)\n const allKeys = new Set<string>()\n\n for (const entry of dictEntries) {\n for (const key of entry.keys) {\n const fullKey = entry.namespace === 'default' ? key : `${entry.namespace}:${key}`\n allKeys.add(fullKey)\n\n // Also add plural base keys (strip _one, _other, etc.)\n for (const suffix of PLURAL_SUFFIXES) {\n if (key.endsWith(suffix)) {\n const baseKey = key.slice(0, -suffix.length)\n const fullBaseKey = entry.namespace === 'default' ? baseKey : `${entry.namespace}:${baseKey}`\n allKeys.add(fullBaseKey)\n }\n }\n }\n }\n\n if (allKeys.size === 0) {\n console.log(chalk.yellow('No dictionary keys found. Make sure loadDictionaries() calls are present.'))\n return\n }\n\n const sortedKeys = [...allKeys].sort()\n\n // Generate the .d.ts content\n const keyUnion = sortedKeys.map(k => ` | '${k}'`).join('\\n')\n\n const content = `// Auto-generated by inline-i18n typegen\n// Do not edit manually. Re-run: npx inline-i18n typegen\n\nimport 'inline-i18n-multi'\n\ndeclare module 'inline-i18n-multi' {\n export type TranslationKey =\n${keyUnion}\n\n export function t(\n key: TranslationKey,\n vars?: TranslationVars,\n locale?: string\n ): string\n\n export function hasTranslation(\n key: TranslationKey,\n locale?: string\n ): boolean\n}\n`\n\n const outputPath = path.resolve(cwd || process.cwd(), output)\n const outputDir = path.dirname(outputPath)\n\n if (!fs.existsSync(outputDir)) {\n fs.mkdirSync(outputDir, { recursive: true })\n }\n\n fs.writeFileSync(outputPath, content, 'utf-8')\n\n console.log(chalk.green(`Generated ${sortedKeys.length} translation key types`))\n console.log(chalk.gray(`Output: ${outputPath}\\n`))\n\n // Show sample\n const sample = sortedKeys.slice(0, 5)\n for (const key of sample) {\n console.log(` ${chalk.cyan(key)}`)\n }\n if (sortedKeys.length > 5) {\n console.log(chalk.gray(` ... and ${sortedKeys.length - 5} more`))\n }\n}\n","import chalk from 'chalk'\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { parseProject } from '../parser'\n\ninterface ExtractOptions {\n cwd?: string\n output?: string\n format?: 'flat' | 'nested'\n}\n\nexport async function extract(options: ExtractOptions = {}): Promise<void> {\n const { cwd, output = 'translations', format = 'flat' } = options\n\n console.log(chalk.blue('\\nExtracting inline translations...\\n'))\n\n const entries = await parseProject({ cwd })\n\n if (entries.length === 0) {\n console.log(chalk.yellow('No translations found.'))\n return\n }\n\n // Group translations by locale\n const byLocale: Record<string, Record<string, string>> = {}\n const basePath = cwd || process.cwd()\n\n for (const entry of entries) {\n for (const [locale, text] of Object.entries(entry.translations)) {\n if (!byLocale[locale]) byLocale[locale] = {}\n\n const relativePath = entry.file.replace(basePath + '/', '')\n const key = `${relativePath}:${entry.line}`\n byLocale[locale][key] = text\n }\n }\n\n // Write output files\n const outputDir = path.resolve(basePath, output)\n if (!fs.existsSync(outputDir)) {\n fs.mkdirSync(outputDir, { recursive: true })\n }\n\n const locales = Object.keys(byLocale).sort()\n for (const locale of locales) {\n const translations = byLocale[locale]!\n let content: string\n\n if (format === 'nested') {\n const nested = buildNestedObject(translations)\n content = JSON.stringify(nested, null, 2) + '\\n'\n } else {\n content = JSON.stringify(translations, null, 2) + '\\n'\n }\n\n const filePath = path.join(outputDir, `${locale}.json`)\n fs.writeFileSync(filePath, content)\n console.log(\n chalk.green(` ${locale}.json`) +\n chalk.gray(` (${Object.keys(translations).length} keys)`)\n )\n }\n\n console.log(chalk.gray(`\\nExtracted ${entries.length} translation(s) to ${output}/`))\n}\n\n/**\n * Build a nested object from flat dot-notation keys\n */\nfunction buildNestedObject(flat: Record<string, string>): Record<string, unknown> {\n const result: Record<string, unknown> = {}\n\n for (const [key, value] of Object.entries(flat)) {\n const parts = key.split('.')\n let current: Record<string, unknown> = result\n\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]!\n if (!current[part] || typeof current[part] !== 'object') {\n current[part] = {}\n }\n current = current[part] as Record<string, unknown>\n }\n\n current[parts[parts.length - 1]!] = value\n }\n\n return result\n}\n"],"mappings":"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","names":["traverse","fg","chalk","import_chalk","import_chalk","chalk","chalk","import_chalk","chalk","fs","import_chalk","PLURAL_SUFFIXES","chalk","import_chalk","fs","path","chalk"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inline-i18n-multi/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "CLI tools for inline-i18n-multi",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"@babel/parser": "^7.26.3",
|
|
28
28
|
"@babel/traverse": "^7.26.4",
|
|
29
29
|
"chalk": "^5.4.1",
|
|
30
|
+
"chokidar": "^5.0.0",
|
|
30
31
|
"commander": "^13.0.0",
|
|
31
32
|
"fast-glob": "^3.3.3"
|
|
32
33
|
},
|