translomatic-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +216 -0
  2. package/dist/commands/analyze.d.ts +11 -0
  3. package/dist/commands/analyze.d.ts.map +1 -0
  4. package/dist/commands/analyze.js +108 -0
  5. package/dist/commands/analyze.js.map +1 -0
  6. package/dist/commands/init.d.ts +12 -0
  7. package/dist/commands/init.d.ts.map +1 -0
  8. package/dist/commands/init.js +100 -0
  9. package/dist/commands/init.js.map +1 -0
  10. package/dist/commands/status.d.ts +10 -0
  11. package/dist/commands/status.d.ts.map +1 -0
  12. package/dist/commands/status.js +116 -0
  13. package/dist/commands/status.js.map +1 -0
  14. package/dist/commands/translate.d.ts +15 -0
  15. package/dist/commands/translate.d.ts.map +1 -0
  16. package/dist/commands/translate.js +58 -0
  17. package/dist/commands/translate.js.map +1 -0
  18. package/dist/config.d.ts +19 -0
  19. package/dist/config.d.ts.map +1 -0
  20. package/dist/config.js +59 -0
  21. package/dist/config.js.map +1 -0
  22. package/dist/index.d.ts +3 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +50 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/logger.d.ts +25 -0
  27. package/dist/logger.d.ts.map +1 -0
  28. package/dist/logger.js +122 -0
  29. package/dist/logger.js.map +1 -0
  30. package/dist/scanner/detector.d.ts +6 -0
  31. package/dist/scanner/detector.d.ts.map +1 -0
  32. package/dist/scanner/detector.js +236 -0
  33. package/dist/scanner/detector.js.map +1 -0
  34. package/dist/scanner/extractor.d.ts +43 -0
  35. package/dist/scanner/extractor.d.ts.map +1 -0
  36. package/dist/scanner/extractor.js +107 -0
  37. package/dist/scanner/extractor.js.map +1 -0
  38. package/dist/translator.d.ts +34 -0
  39. package/dist/translator.d.ts.map +1 -0
  40. package/dist/translator.js +138 -0
  41. package/dist/translator.js.map +1 -0
  42. package/dist/types.d.ts +92 -0
  43. package/dist/types.d.ts.map +1 -0
  44. package/dist/types.js +2 -0
  45. package/dist/types.js.map +1 -0
  46. package/package.json +46 -0
@@ -0,0 +1,58 @@
1
+ import { loadConfig } from "../config.js";
2
+ import { translateProject } from "../translator.js";
3
+ import { Logger } from "../logger.js";
4
+ /**
5
+ * `translomatic translate` command
6
+ *
7
+ * Translates all missing strings in the project.
8
+ * Reads source language file, finds missing keys in each target language,
9
+ * and batch-translates them using the Translomatic API.
10
+ */
11
+ export async function translateCommand(projectDir, options) {
12
+ const logger = new Logger(options.verbose);
13
+ logger.banner();
14
+ logger.section("Translating Project");
15
+ // Load config
16
+ const config = await loadConfig(projectDir);
17
+ logger.info(`Source language: ${config.sourceLang}`);
18
+ const targetLangs = options.lang
19
+ ? options.lang.split(",").map((l) => l.trim())
20
+ : config.targetLangs;
21
+ logger.info(`Target languages: ${targetLangs.join(", ")}`);
22
+ if (config.model) {
23
+ logger.info(`Model: ${config.model}`);
24
+ }
25
+ if (config.context) {
26
+ logger.info(`Context: ${config.context.slice(0, 60)}${config.context.length > 60 ? "..." : ""}`);
27
+ }
28
+ if (options.force) {
29
+ logger.warn("Force mode: re-translating all strings");
30
+ }
31
+ if (options.dryRun) {
32
+ logger.warn("Dry run: no files will be written");
33
+ }
34
+ const translateOpts = {
35
+ force: options.force,
36
+ langs: targetLangs,
37
+ namespaces: options.namespace
38
+ ? options.namespace.split(",").map((n) => n.trim())
39
+ : undefined,
40
+ dryRun: options.dryRun,
41
+ };
42
+ try {
43
+ const result = await translateProject(projectDir, config, translateOpts, logger);
44
+ if (result.totalTranslated > 0 || result.totalSkipped > 0) {
45
+ logger.summary(result.totalLangs, result.totalTranslated + result.totalSkipped, result.totalTranslated, result.totalTimeMs);
46
+ }
47
+ else {
48
+ logger.section("Result");
49
+ logger.success("All strings are already translated!");
50
+ console.log();
51
+ }
52
+ }
53
+ catch (err) {
54
+ logger.error(err.message);
55
+ process.exit(1);
56
+ }
57
+ }
58
+ //# sourceMappingURL=translate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translate.js","sourceRoot":"","sources":["../../src/commands/translate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { CLIConfig } from "./types.js";
2
+ /**
3
+ * Load config from .translomaticrc.json in the given directory.
4
+ * Falls back to defaults if not found.
5
+ */
6
+ export declare function loadConfig(dir: string): Promise<CLIConfig>;
7
+ /**
8
+ * Save config to .translomaticrc.json.
9
+ */
10
+ export declare function saveConfig(dir: string, config: CLIConfig): Promise<string>;
11
+ /**
12
+ * Check if config file exists.
13
+ */
14
+ export declare function configExists(dir: string): Promise<boolean>;
15
+ /**
16
+ * Resolve API key from config or environment variable.
17
+ */
18
+ export declare function resolveApiKey(config: CLIConfig): string;
19
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAY5C;;;GAGG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAchE;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAKhF;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOhE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAQvD"}
package/dist/config.js ADDED
@@ -0,0 +1,59 @@
1
+ import { readFile, writeFile, access } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ const CONFIG_FILENAME = ".translomaticrc.json";
4
+ const DEFAULT_CONFIG = {
5
+ sourceLang: "en",
6
+ targetLangs: [],
7
+ localesDir: "./locales",
8
+ fileFormat: "nested-json",
9
+ filePattern: "{lang}.json",
10
+ };
11
+ /**
12
+ * Load config from .translomaticrc.json in the given directory.
13
+ * Falls back to defaults if not found.
14
+ */
15
+ export async function loadConfig(dir) {
16
+ const configPath = join(dir, CONFIG_FILENAME);
17
+ try {
18
+ await access(configPath);
19
+ const raw = await readFile(configPath, "utf-8");
20
+ const parsed = JSON.parse(raw);
21
+ return { ...DEFAULT_CONFIG, ...parsed };
22
+ }
23
+ catch {
24
+ throw new Error(`Config file not found: ${configPath}\n` +
25
+ `Run "translomatic init" to create one.`);
26
+ }
27
+ }
28
+ /**
29
+ * Save config to .translomaticrc.json.
30
+ */
31
+ export async function saveConfig(dir, config) {
32
+ const configPath = join(dir, CONFIG_FILENAME);
33
+ const content = JSON.stringify(config, null, 2) + "\n";
34
+ await writeFile(configPath, content, "utf-8");
35
+ return configPath;
36
+ }
37
+ /**
38
+ * Check if config file exists.
39
+ */
40
+ export async function configExists(dir) {
41
+ try {
42
+ await access(join(dir, CONFIG_FILENAME));
43
+ return true;
44
+ }
45
+ catch {
46
+ return false;
47
+ }
48
+ }
49
+ /**
50
+ * Resolve API key from config or environment variable.
51
+ */
52
+ export function resolveApiKey(config) {
53
+ const key = config.apiKey || process.env.TRANSLOMATIC_API_KEY;
54
+ if (!key) {
55
+ throw new Error("API key not found. Set it in .translomaticrc.json or TRANSLOMATIC_API_KEY env var.");
56
+ }
57
+ return key;
58
+ }
59
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,MAAM,eAAe,GAAG,sBAAsB,CAAC;AAE/C,MAAM,cAAc,GAAc;IAChC,UAAU,EAAE,IAAI;IAChB,WAAW,EAAE,EAAE;IACf,UAAU,EAAE,WAAW;IACvB,UAAU,EAAE,aAAa;IACzB,WAAW,EAAE,aAAa;CAC3B,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAW;IAC1C,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAE9C,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;QACzB,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAuB,CAAC;QACrD,OAAO,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,EAAE,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,0BAA0B,UAAU,IAAI;YACtC,wCAAwC,CAC3C,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAW,EAAE,MAAiB;IAC7D,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;IACvD,MAAM,SAAS,CAAC,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC9C,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW;IAC5C,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC,CAAC;QACzC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,MAAiB;IAC7C,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;IAC9D,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CACb,oFAAoF,CACrF,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { initCommand } from "./commands/init.js";
4
+ import { translateCommand } from "./commands/translate.js";
5
+ import { analyzeCommand } from "./commands/analyze.js";
6
+ import { statusCommand } from "./commands/status.js";
7
+ const program = new Command();
8
+ program
9
+ .name("translomatic")
10
+ .description("Auto-scan and translate your entire project using Translomatic API")
11
+ .version("1.0.0");
12
+ // ─── init ──────────────────────────────────────────────
13
+ program
14
+ .command("init")
15
+ .description("Scan project and create .translomaticrc.json config")
16
+ .option("-f, --force", "Overwrite existing config file")
17
+ .action(async (options) => {
18
+ await initCommand(process.cwd(), options);
19
+ });
20
+ // ─── translate ─────────────────────────────────────────
21
+ program
22
+ .command("translate")
23
+ .description("Translate all missing strings in the project")
24
+ .option("--force", "Re-translate all strings (ignore existing translations)")
25
+ .option("-l, --lang <langs>", "Only translate specific languages (comma-separated)")
26
+ .option("-n, --namespace <ns>", "Only translate specific namespaces (comma-separated)")
27
+ .option("--dry-run", "Show what would be translated without making changes")
28
+ .option("-v, --verbose", "Show detailed output")
29
+ .action(async (options) => {
30
+ await translateCommand(process.cwd(), options);
31
+ });
32
+ // ─── analyze ───────────────────────────────────────────
33
+ program
34
+ .command("analyze")
35
+ .description("Auto-analyze project context for smarter translations")
36
+ .option("-v, --verbose", "Show detailed output")
37
+ .action(async (options) => {
38
+ await analyzeCommand(process.cwd(), options);
39
+ });
40
+ // ─── status ────────────────────────────────────────────
41
+ program
42
+ .command("status")
43
+ .description("Show translation coverage for all target languages")
44
+ .option("-v, --verbose", "Show namespace-level details")
45
+ .action(async (options) => {
46
+ await statusCommand(process.cwd(), options);
47
+ });
48
+ // ─── Run ───────────────────────────────────────────────
49
+ program.parse();
50
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,cAAc,CAAC;KACpB,WAAW,CAAC,oEAAoE,CAAC;KACjF,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,0DAA0D;AAE1D,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,qDAAqD,CAAC;KAClE,MAAM,CAAC,aAAa,EAAE,gCAAgC,CAAC;KACvD,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IACxB,MAAM,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC;AAC5C,CAAC,CAAC,CAAC;AAEL,0DAA0D;AAE1D,OAAO;KACJ,OAAO,CAAC,WAAW,CAAC;KACpB,WAAW,CAAC,8CAA8C,CAAC;KAC3D,MAAM,CAAC,SAAS,EAAE,yDAAyD,CAAC;KAC5E,MAAM,CAAC,oBAAoB,EAAE,qDAAqD,CAAC;KACnF,MAAM,CAAC,sBAAsB,EAAE,sDAAsD,CAAC;KACtF,MAAM,CAAC,WAAW,EAAE,sDAAsD,CAAC;KAC3E,MAAM,CAAC,eAAe,EAAE,sBAAsB,CAAC;KAC/C,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IACxB,MAAM,gBAAgB,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC;AACjD,CAAC,CAAC,CAAC;AAEL,0DAA0D;AAE1D,OAAO;KACJ,OAAO,CAAC,SAAS,CAAC;KAClB,WAAW,CAAC,uDAAuD,CAAC;KACpE,MAAM,CAAC,eAAe,EAAE,sBAAsB,CAAC;KAC/C,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IACxB,MAAM,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC;AAC/C,CAAC,CAAC,CAAC;AAEL,0DAA0D;AAE1D,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,oDAAoD,CAAC;KACjE,MAAM,CAAC,eAAe,EAAE,8BAA8B,CAAC;KACvD,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IACxB,MAAM,aAAa,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC;AAC9C,CAAC,CAAC,CAAC;AAEL,0DAA0D;AAE1D,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { TranslationProgress } from "./types.js";
2
+ export declare class Logger {
3
+ private verbose;
4
+ constructor(verbose?: boolean);
5
+ banner(): void;
6
+ info(msg: string): void;
7
+ success(msg: string): void;
8
+ warn(msg: string): void;
9
+ error(msg: string): void;
10
+ debug(msg: string): void;
11
+ section(title: string): void;
12
+ langStart(lang: string, total: number): void;
13
+ langComplete(lang: string, translated: number, skipped: number, failed: number, timeMs: number): void;
14
+ batchProgress(progress: TranslationProgress): void;
15
+ clearLine(): void;
16
+ private progressBar;
17
+ statusTable(rows: Array<{
18
+ lang: string;
19
+ coverage: number;
20
+ translated: number;
21
+ total: number;
22
+ }>): void;
23
+ summary(totalLangs: number, totalStrings: number, totalTranslated: number, totalTimeMs: number): void;
24
+ }
25
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAItD,qBAAa,MAAM;IACjB,OAAO,CAAC,OAAO,CAAU;gBAEb,OAAO,UAAQ;IAM3B,MAAM,IAAI,IAAI;IAwBd,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIvB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAI1B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIvB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIxB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAQxB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAQ5B,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAS5C,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAerG,aAAa,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI;IAYlD,SAAS,IAAI,IAAI;IAIjB,OAAO,CAAC,WAAW;IAUnB,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI;IAmCrG,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;CAiBtG"}
package/dist/logger.js ADDED
@@ -0,0 +1,122 @@
1
+ import chalk from "chalk";
2
+ const LOGO = "translomatic";
3
+ export class Logger {
4
+ verbose;
5
+ constructor(verbose = false) {
6
+ this.verbose = verbose;
7
+ }
8
+ // ─── Branding ────────────────────────────────────────────
9
+ banner() {
10
+ console.log();
11
+ console.log(chalk.bold.cyan(" ╔══════════════════════════════════════╗"));
12
+ console.log(chalk.bold.cyan(" ║") +
13
+ chalk.bold.white(" Translomatic CLI ") +
14
+ chalk.dim("v1.0.0") +
15
+ chalk.bold.cyan(" ║"));
16
+ console.log(chalk.bold.cyan(" ║") +
17
+ chalk.dim(" Auto-translate your entire project ") +
18
+ chalk.bold.cyan("║"));
19
+ console.log(chalk.bold.cyan(" ╚══════════════════════════════════════╝"));
20
+ console.log();
21
+ }
22
+ // ─── Status Messages ────────────────────────────────────
23
+ info(msg) {
24
+ console.log(chalk.blue(" ℹ ") + msg);
25
+ }
26
+ success(msg) {
27
+ console.log(chalk.green(" ✓ ") + msg);
28
+ }
29
+ warn(msg) {
30
+ console.log(chalk.yellow(" ⚠ ") + msg);
31
+ }
32
+ error(msg) {
33
+ console.log(chalk.red(" ✗ ") + msg);
34
+ }
35
+ debug(msg) {
36
+ if (this.verbose) {
37
+ console.log(chalk.gray(" · ") + chalk.gray(msg));
38
+ }
39
+ }
40
+ // ─── Section Headers ────────────────────────────────────
41
+ section(title) {
42
+ console.log();
43
+ console.log(chalk.bold.white(` ${title}`));
44
+ console.log(chalk.dim(" " + "─".repeat(40)));
45
+ }
46
+ // ─── Translation Progress ──────────────────────────────
47
+ langStart(lang, total) {
48
+ console.log();
49
+ console.log(chalk.bold.cyan(` ▸ `) +
50
+ chalk.bold(`Translating → ${lang}`) +
51
+ chalk.dim(` (${total} strings)`));
52
+ }
53
+ langComplete(lang, translated, skipped, failed, timeMs) {
54
+ const parts = [];
55
+ if (translated > 0)
56
+ parts.push(chalk.green(`${translated} translated`));
57
+ if (skipped > 0)
58
+ parts.push(chalk.dim(`${skipped} skipped`));
59
+ if (failed > 0)
60
+ parts.push(chalk.red(`${failed} failed`));
61
+ const time = timeMs < 1000 ? `${timeMs}ms` : `${(timeMs / 1000).toFixed(1)}s`;
62
+ console.log(chalk.green(" ✓ ") +
63
+ chalk.bold(lang) +
64
+ chalk.dim(" — ") +
65
+ parts.join(chalk.dim(", ")) +
66
+ chalk.dim(` (${time})`));
67
+ }
68
+ batchProgress(progress) {
69
+ const pct = Math.round((progress.translated / progress.total) * 100);
70
+ const bar = this.progressBar(pct, 20);
71
+ process.stdout.write(`\r ${bar} ${chalk.bold(`${pct}%`)} ` +
72
+ chalk.dim(`(${progress.translated}/${progress.total}) ` +
73
+ `batch ${progress.currentBatch}/${progress.totalBatches}`));
74
+ }
75
+ clearLine() {
76
+ process.stdout.write("\r" + " ".repeat(80) + "\r");
77
+ }
78
+ progressBar(pct, width) {
79
+ const filled = Math.round((pct / 100) * width);
80
+ const empty = width - filled;
81
+ return (chalk.cyan("█".repeat(filled)) + chalk.gray("░".repeat(empty)));
82
+ }
83
+ // ─── Status Table ──────────────────────────────────────
84
+ statusTable(rows) {
85
+ const maxLang = Math.max(4, ...rows.map((r) => r.lang.length));
86
+ console.log(chalk.dim(" ") +
87
+ chalk.bold("Lang".padEnd(maxLang + 2)) +
88
+ chalk.bold("Coverage".padEnd(12)) +
89
+ chalk.bold("Translated".padEnd(14)) +
90
+ chalk.bold("Missing"));
91
+ console.log(chalk.dim(" " + "─".repeat(maxLang + 2 + 12 + 14 + 10)));
92
+ for (const row of rows) {
93
+ const missing = row.total - row.translated;
94
+ const coverageStr = `${row.coverage}%`;
95
+ const coverageColor = row.coverage === 100
96
+ ? chalk.green
97
+ : row.coverage >= 80
98
+ ? chalk.yellow
99
+ : chalk.red;
100
+ const bar = this.progressBar(row.coverage, 10);
101
+ console.log(chalk.dim(" ") +
102
+ chalk.bold(row.lang.padEnd(maxLang + 2)) +
103
+ `${bar} ${coverageColor(coverageStr.padEnd(5))} ` +
104
+ chalk.dim(`${row.translated}/${row.total}`).padEnd(22) +
105
+ (missing > 0 ? chalk.red(`${missing}`) : chalk.green("0")));
106
+ }
107
+ }
108
+ // ─── Summary ───────────────────────────────────────────
109
+ summary(totalLangs, totalStrings, totalTranslated, totalTimeMs) {
110
+ console.log();
111
+ console.log(chalk.dim(" " + "═".repeat(40)));
112
+ const time = totalTimeMs < 1000
113
+ ? `${totalTimeMs}ms`
114
+ : `${(totalTimeMs / 1000).toFixed(1)}s`;
115
+ console.log(chalk.bold.green(" ✓ Translation complete!") +
116
+ chalk.dim(` (${time})`));
117
+ console.log(chalk.dim(" ") +
118
+ `${totalLangs} languages, ${totalTranslated} strings translated`);
119
+ console.log();
120
+ }
121
+ }
122
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { FrameworkInfo } from "../types.js";
2
+ /**
3
+ * Scan a project directory to detect i18n framework and locale files.
4
+ */
5
+ export declare function detectFramework(projectDir: string): Promise<FrameworkInfo | null>;
6
+ //# sourceMappingURL=detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../../src/scanner/detector.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAwIjD;;GAEG;AACH,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAsBvF"}
@@ -0,0 +1,236 @@
1
+ import { readFile, readdir, stat } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ const FRAMEWORKS = [
4
+ {
5
+ name: "react-i18next",
6
+ displayName: "react-i18next",
7
+ deps: ["react-i18next", "i18next"],
8
+ localeDirs: ["locales", "src/locales", "public/locales", "src/i18n/locales"],
9
+ filePattern: "{lang}/{namespace}.json",
10
+ usesNamespaces: true,
11
+ },
12
+ {
13
+ name: "next-intl",
14
+ displayName: "next-intl",
15
+ deps: ["next-intl"],
16
+ localeDirs: ["messages", "src/messages", "locales", "src/locales"],
17
+ filePattern: "{lang}.json",
18
+ usesNamespaces: false,
19
+ },
20
+ {
21
+ name: "vue-i18n",
22
+ displayName: "vue-i18n",
23
+ deps: ["vue-i18n", "@intlify/vue-i18n"],
24
+ localeDirs: ["locales", "src/locales", "src/i18n", "src/lang"],
25
+ filePattern: "{lang}.json",
26
+ usesNamespaces: false,
27
+ },
28
+ {
29
+ name: "angular-i18n",
30
+ displayName: "Angular i18n",
31
+ deps: ["@angular/localize", "@ngx-translate/core"],
32
+ localeDirs: ["src/assets/i18n", "src/locale", "src/i18n"],
33
+ filePattern: "{lang}.json",
34
+ usesNamespaces: false,
35
+ },
36
+ {
37
+ name: "svelte-i18n",
38
+ displayName: "svelte-i18n",
39
+ deps: ["svelte-i18n", "svelte-intl-precompile"],
40
+ localeDirs: ["src/lib/i18n", "src/locales", "locales", "messages"],
41
+ filePattern: "{lang}.json",
42
+ usesNamespaces: false,
43
+ },
44
+ {
45
+ name: "i18next",
46
+ displayName: "i18next (generic)",
47
+ deps: ["i18next"],
48
+ localeDirs: ["locales", "src/locales", "public/locales", "i18n", "lang"],
49
+ filePattern: "{lang}.json",
50
+ usesNamespaces: false,
51
+ },
52
+ ];
53
+ /**
54
+ * Common locale directory names to scan when no framework is detected.
55
+ */
56
+ const COMMON_LOCALE_DIRS = [
57
+ "locales",
58
+ "locale",
59
+ "lang",
60
+ "languages",
61
+ "i18n",
62
+ "translations",
63
+ "messages",
64
+ "src/locales",
65
+ "src/locale",
66
+ "src/lang",
67
+ "src/i18n",
68
+ "src/translations",
69
+ "src/messages",
70
+ "public/locales",
71
+ "public/lang",
72
+ "assets/i18n",
73
+ "src/assets/i18n",
74
+ ];
75
+ /**
76
+ * ISO 639-1 language codes (common ones for quick detection).
77
+ */
78
+ const LANG_CODES = new Set([
79
+ "aa", "ab", "af", "ak", "am", "an", "ar", "as", "av", "ay", "az",
80
+ "ba", "be", "bg", "bh", "bi", "bm", "bn", "bo", "br", "bs",
81
+ "ca", "ce", "ch", "co", "cr", "cs", "cu", "cv", "cy",
82
+ "da", "de", "dv", "dz",
83
+ "ee", "el", "en", "eo", "es", "et", "eu",
84
+ "fa", "ff", "fi", "fj", "fo", "fr", "fy",
85
+ "ga", "gd", "gl", "gn", "gu", "gv",
86
+ "ha", "he", "hi", "ho", "hr", "ht", "hu", "hy", "hz",
87
+ "ia", "id", "ie", "ig", "ii", "ik", "io", "is", "it", "iu",
88
+ "ja", "jv",
89
+ "ka", "kg", "ki", "kj", "kk", "kl", "km", "kn", "ko", "kr", "ks", "ku", "kv", "kw", "ky",
90
+ "la", "lb", "lg", "li", "ln", "lo", "lt", "lu", "lv",
91
+ "mg", "mh", "mi", "mk", "ml", "mn", "mr", "ms", "mt", "my",
92
+ "na", "nb", "nd", "ne", "ng", "nl", "nn", "no", "nr", "nv", "ny",
93
+ "oc", "oj", "om", "or", "os",
94
+ "pa", "pi", "pl", "ps", "pt",
95
+ "qu",
96
+ "rm", "rn", "ro", "ru", "rw",
97
+ "sa", "sc", "sd", "se", "sg", "si", "sk", "sl", "sm", "sn", "so", "sq", "sr", "ss", "st", "su", "sv", "sw",
98
+ "ta", "te", "tg", "th", "ti", "tk", "tl", "tn", "to", "tr", "ts", "tt", "tw", "ty",
99
+ "ug", "uk", "ur", "uz",
100
+ "ve", "vi", "vo",
101
+ "wa", "wo",
102
+ "xh",
103
+ "yi", "yo",
104
+ "za", "zh", "zu",
105
+ ]);
106
+ /**
107
+ * Also support locale codes like "en-US", "zh-CN", "pt-BR"
108
+ */
109
+ function isLangCode(name) {
110
+ const lower = name.toLowerCase();
111
+ if (LANG_CODES.has(lower))
112
+ return true;
113
+ // Match patterns like en-US, zh-CN, pt-BR
114
+ const match = lower.match(/^([a-z]{2})[-_]([a-z]{2,4})$/);
115
+ if (match && LANG_CODES.has(match[1]))
116
+ return true;
117
+ return false;
118
+ }
119
+ /**
120
+ * Scan a project directory to detect i18n framework and locale files.
121
+ */
122
+ export async function detectFramework(projectDir) {
123
+ // Step 1: Read package.json to find framework deps
124
+ const detectedFramework = await detectFromPackageJson(projectDir);
125
+ // Step 2: Scan for locale directories
126
+ const localeDir = await findLocaleDir(projectDir, detectedFramework);
127
+ if (!localeDir)
128
+ return null;
129
+ // Step 3: Analyze locale directory structure
130
+ const analysis = await analyzeLocaleDir(join(projectDir, localeDir));
131
+ if (!analysis)
132
+ return null;
133
+ return {
134
+ name: detectedFramework?.name || "generic",
135
+ displayName: detectedFramework?.displayName || "Generic JSON i18n",
136
+ localesDir: localeDir,
137
+ filePattern: analysis.filePattern,
138
+ sourceLang: analysis.sourceLang,
139
+ existingLangs: analysis.langs,
140
+ namespaces: analysis.namespaces,
141
+ confidence: detectedFramework ? 0.9 : 0.6,
142
+ };
143
+ }
144
+ async function detectFromPackageJson(projectDir) {
145
+ try {
146
+ const pkgPath = join(projectDir, "package.json");
147
+ const pkgRaw = await readFile(pkgPath, "utf-8");
148
+ const pkg = JSON.parse(pkgRaw);
149
+ const allDeps = {
150
+ ...(pkg.dependencies || {}),
151
+ ...(pkg.devDependencies || {}),
152
+ };
153
+ for (const fw of FRAMEWORKS) {
154
+ if (fw.deps.some((dep) => dep in allDeps)) {
155
+ return fw;
156
+ }
157
+ }
158
+ }
159
+ catch {
160
+ // No package.json or invalid
161
+ }
162
+ return null;
163
+ }
164
+ async function findLocaleDir(projectDir, framework) {
165
+ // Check framework-specific dirs first
166
+ const dirsToCheck = framework
167
+ ? [...framework.localeDirs, ...COMMON_LOCALE_DIRS]
168
+ : COMMON_LOCALE_DIRS;
169
+ // Deduplicate
170
+ const unique = [...new Set(dirsToCheck)];
171
+ for (const dir of unique) {
172
+ const fullPath = join(projectDir, dir);
173
+ try {
174
+ const s = await stat(fullPath);
175
+ if (s.isDirectory()) {
176
+ // Verify it contains lang files or lang subdirectories
177
+ const entries = await readdir(fullPath, { withFileTypes: true });
178
+ const hasLangFiles = entries.some((e) => (e.isFile() && e.name.endsWith(".json") && isLangCode(e.name.replace(".json", ""))) ||
179
+ (e.isDirectory() && isLangCode(e.name)));
180
+ if (hasLangFiles)
181
+ return dir;
182
+ }
183
+ }
184
+ catch {
185
+ // Dir doesn't exist
186
+ }
187
+ }
188
+ return null;
189
+ }
190
+ async function analyzeLocaleDir(localeFullPath) {
191
+ const entries = await readdir(localeFullPath, { withFileTypes: true });
192
+ // Pattern 1: {lang}.json files (e.g., en.json, vi.json)
193
+ const jsonFiles = entries.filter((e) => e.isFile() && e.name.endsWith(".json"));
194
+ const langFiles = jsonFiles.filter((e) => isLangCode(e.name.replace(".json", "")));
195
+ if (langFiles.length > 0) {
196
+ const langs = langFiles.map((e) => e.name.replace(".json", ""));
197
+ const sourceLang = langs.includes("en") ? "en" : langs[0];
198
+ return {
199
+ filePattern: "{lang}.json",
200
+ sourceLang,
201
+ langs,
202
+ };
203
+ }
204
+ // Pattern 2: {lang}/ subdirectories with namespace files
205
+ const langDirs = entries.filter((e) => e.isDirectory() && isLangCode(e.name));
206
+ if (langDirs.length > 0) {
207
+ const langs = langDirs.map((e) => e.name);
208
+ const sourceLang = langs.includes("en") ? "en" : langs[0];
209
+ // Check first lang dir for namespace files
210
+ const firstLangDir = join(localeFullPath, sourceLang || langs[0]);
211
+ try {
212
+ const nsEntries = await readdir(firstLangDir, { withFileTypes: true });
213
+ const nsFiles = nsEntries.filter((e) => e.isFile() && e.name.endsWith(".json"));
214
+ if (nsFiles.length > 0) {
215
+ const namespaces = nsFiles.map((e) => e.name.replace(".json", ""));
216
+ return {
217
+ filePattern: "{lang}/{namespace}.json",
218
+ sourceLang,
219
+ langs,
220
+ namespaces,
221
+ };
222
+ }
223
+ }
224
+ catch {
225
+ // Can't read lang subdirectory
226
+ }
227
+ return {
228
+ filePattern: "{lang}/{namespace}.json",
229
+ sourceLang,
230
+ langs,
231
+ namespaces: ["translation"],
232
+ };
233
+ }
234
+ return null;
235
+ }
236
+ //# sourceMappingURL=detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detector.js","sourceRoot":"","sources":["../../src/scanner/detector.ts"],"names":[],"mappings":"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"}