@ccgp/i18n-ai 0.0.2 → 0.1.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 CHANGED
@@ -55,8 +55,6 @@ Create a `.env` file:
55
55
 
56
56
  ```env
57
57
  OPENROUTER_API_KEY=your_api_key
58
- # or
59
- AI_API_KEY=your_api_key
60
58
  ```
61
59
 
62
60
  ## Programmatic Usage
@@ -72,7 +70,7 @@ const service = new TranslationService({
72
70
  defaultLocale: 'en',
73
71
  messagesDir: resolve(process.cwd(), 'messages'),
74
72
  lockFilePath: resolve(process.cwd(), 'translation-lock.json'),
75
- apiKey: process.env.AI_API_KEY
73
+ apiKey: process.env.OPENROUTER_API_KEY
76
74
  });
77
75
 
78
76
  await service.sync();
package/dist/cli.js CHANGED
@@ -311,36 +311,102 @@ var TranslationService = class {
311
311
 
312
312
  // src/cli.ts
313
313
  var import_dotenv = __toESM(require("dotenv"));
314
+ var import_prompts = __toESM(require("prompts"));
315
+ var import_chalk2 = __toESM(require("chalk"));
314
316
  import_dotenv.default.config();
315
317
  var program = new import_commander.Command();
316
318
  program.name("i18n-ai").description("AI-powered translation CLI").version("0.0.1");
317
- program.command("sync").description("Synchronize translations using AI").option("-d, --dir <path>", "Messages directory", "messages").option("-l, --locales <items>", "Comma separated list of locales").option("--default <locale>", "Default locale", "en").option("--lock <path>", "Lock file path", "translation-lock.json").action(async (options) => {
319
+ var CONFIG_FILE = "i18n-ai.config.json";
320
+ async function loadConfig() {
321
+ try {
322
+ const configPath = (0, import_path2.join)(process.cwd(), CONFIG_FILE);
323
+ const content = await (0, import_promises3.readFile)(configPath, "utf-8");
324
+ return JSON.parse(content);
325
+ } catch {
326
+ return null;
327
+ }
328
+ }
329
+ program.command("init").description("Initialize i18n-ai configuration").action(async () => {
330
+ console.log(import_chalk2.default.bold("\u{1F680} Initializing i18n-ai configuration\n"));
331
+ const response = await (0, import_prompts.default)([
332
+ {
333
+ type: "text",
334
+ name: "messagesDir",
335
+ message: "Where are your messages located?",
336
+ initial: "messages",
337
+ validate: (value) => value.length > 0 ? true : "Directory cannot be empty"
338
+ },
339
+ {
340
+ type: "text",
341
+ name: "defaultLocale",
342
+ message: "What is your default locale?",
343
+ initial: "en"
344
+ },
345
+ {
346
+ type: "list",
347
+ name: "locales",
348
+ message: "Which other locales do you support? (comma separated)",
349
+ initial: "es, fr",
350
+ separator: ","
351
+ }
352
+ ]);
353
+ if (!response.messagesDir || !response.defaultLocale) {
354
+ console.log(import_chalk2.default.red("\n\u274C Initialization canceled"));
355
+ return;
356
+ }
357
+ const allLocales = [response.defaultLocale, ...response.locales.filter((l) => l !== response.defaultLocale)];
358
+ const config = {
359
+ defaultLocale: response.defaultLocale,
360
+ locales: allLocales,
361
+ messagesDir: response.messagesDir
362
+ };
363
+ await (0, import_promises3.writeFile)((0, import_path2.join)(process.cwd(), CONFIG_FILE), JSON.stringify(config, null, 2));
364
+ console.log(import_chalk2.default.green(`
365
+ \u2705 Created ${CONFIG_FILE}`));
366
+ const absMessagesDir = (0, import_path2.resolve)(process.cwd(), response.messagesDir);
367
+ await (0, import_promises3.mkdir)(absMessagesDir, { recursive: true });
368
+ const baseFilePath = (0, import_path2.join)(absMessagesDir, `${response.defaultLocale}.json`);
369
+ try {
370
+ await (0, import_promises3.readFile)(baseFilePath);
371
+ } catch {
372
+ await (0, import_promises3.writeFile)(baseFilePath, JSON.stringify({ welcome: "Hello World" }, null, 2));
373
+ console.log(import_chalk2.default.green(`\u2705 Created base file ${response.messagesDir}/${response.defaultLocale}.json`));
374
+ }
375
+ console.log(import_chalk2.default.blue('\n\u{1F389} Setup complete! You can now run "i18n-ai sync"'));
376
+ });
377
+ program.command("sync").description("Synchronize translations using AI").option("-d, --dir <path>", "Messages directory").option("-l, --locales <items>", "Comma separated list of locales").option("--default <locale>", "Default locale").option("--lock <path>", "Lock file path", "translation-lock.json").action(async (options) => {
318
378
  try {
319
379
  const cwd = process.cwd();
320
- const messagesDir = (0, import_path2.resolve)(cwd, options.dir);
321
- let locales = options.locales ? options.locales.split(",") : [];
322
- let defaultLocale = options.default;
323
- if (locales.length === 0) {
324
- try {
325
- const routingPath = (0, import_path2.join)(cwd, "i18n", "routing.ts");
326
- const routingContent = await (0, import_promises3.readFile)(routingPath, "utf-8").catch(() => "");
327
- if (routingContent) {
328
- const localesMatch = routingContent.match(/locales:\s*\[([\s\S]*?)\]/);
329
- const defaultMatch = routingContent.match(/defaultLocale:\s*["'](\w+)["']/);
330
- if (localesMatch) {
331
- locales = localesMatch[1].split(",").map((l) => l.trim().replace(/['"]/g, "")).filter(Boolean);
332
- }
333
- if (defaultMatch && !defaultLocale) {
334
- defaultLocale = defaultMatch[1];
380
+ const config = await loadConfig();
381
+ let messagesDir = options.dir ? (0, import_path2.resolve)(cwd, options.dir) : config ? (0, import_path2.resolve)(cwd, config.messagesDir) : null;
382
+ let locales = options.locales ? options.locales.split(",") : config ? config.locales : [];
383
+ let defaultLocale = options.default || (config ? config.defaultLocale : null);
384
+ if (!messagesDir || locales.length === 0 || !defaultLocale) {
385
+ if (!config) {
386
+ try {
387
+ const routingPath = (0, import_path2.join)(cwd, "i18n", "routing.ts");
388
+ const routingContent = await (0, import_promises3.readFile)(routingPath, "utf-8").catch(() => "");
389
+ if (routingContent) {
390
+ const localesMatch = routingContent.match(/locales:\s*\[([\s\S]*?)\]/);
391
+ const defaultMatch = routingContent.match(/defaultLocale:\s*["'](\w+)["']/);
392
+ if (localesMatch && locales.length === 0) {
393
+ locales = localesMatch[1].split(",").map((l) => l.trim().replace(/['"]/g, "")).filter(Boolean);
394
+ }
395
+ if (defaultMatch && !defaultLocale) {
396
+ defaultLocale = defaultMatch[1];
397
+ }
335
398
  }
399
+ } catch {
336
400
  }
337
- } catch (e) {
338
401
  }
402
+ if (!messagesDir) messagesDir = (0, import_path2.resolve)(cwd, "messages");
403
+ if (!defaultLocale) defaultLocale = "en";
339
404
  }
340
405
  if (locales.length === 0) {
341
- console.error("\u274C No locales found. Please specify --locales or ensure i18n/routing.ts exists.");
406
+ console.error(import_chalk2.default.red('\u274C No locales found. Run "i18n-ai init" or specify --locales.'));
342
407
  process.exit(1);
343
408
  }
409
+ console.log(import_chalk2.default.dim(`Using Config: Default=${defaultLocale}, Locales=${locales.join(",")}, Dir=${messagesDir}`));
344
410
  const service = new TranslationService({
345
411
  locales,
346
412
  defaultLocale,
@@ -349,7 +415,7 @@ program.command("sync").description("Synchronize translations using AI").option(
349
415
  });
350
416
  await service.sync();
351
417
  } catch (error) {
352
- console.error("Fatal error:", error);
418
+ console.error(import_chalk2.default.red("Fatal error:"), error);
353
419
  process.exit(1);
354
420
  }
355
421
  });
package/dist/cli.mjs CHANGED
@@ -6,38 +6,104 @@ import {
6
6
  // src/cli.ts
7
7
  import { Command } from "commander";
8
8
  import { resolve, join } from "path";
9
- import { readFile } from "fs/promises";
9
+ import { readFile, writeFile, mkdir } from "fs/promises";
10
10
  import dotenv from "dotenv";
11
+ import prompts from "prompts";
12
+ import chalk from "chalk";
11
13
  dotenv.config();
12
14
  var program = new Command();
13
15
  program.name("i18n-ai").description("AI-powered translation CLI").version("0.0.1");
14
- program.command("sync").description("Synchronize translations using AI").option("-d, --dir <path>", "Messages directory", "messages").option("-l, --locales <items>", "Comma separated list of locales").option("--default <locale>", "Default locale", "en").option("--lock <path>", "Lock file path", "translation-lock.json").action(async (options) => {
16
+ var CONFIG_FILE = "i18n-ai.config.json";
17
+ async function loadConfig() {
18
+ try {
19
+ const configPath = join(process.cwd(), CONFIG_FILE);
20
+ const content = await readFile(configPath, "utf-8");
21
+ return JSON.parse(content);
22
+ } catch {
23
+ return null;
24
+ }
25
+ }
26
+ program.command("init").description("Initialize i18n-ai configuration").action(async () => {
27
+ console.log(chalk.bold("\u{1F680} Initializing i18n-ai configuration\n"));
28
+ const response = await prompts([
29
+ {
30
+ type: "text",
31
+ name: "messagesDir",
32
+ message: "Where are your messages located?",
33
+ initial: "messages",
34
+ validate: (value) => value.length > 0 ? true : "Directory cannot be empty"
35
+ },
36
+ {
37
+ type: "text",
38
+ name: "defaultLocale",
39
+ message: "What is your default locale?",
40
+ initial: "en"
41
+ },
42
+ {
43
+ type: "list",
44
+ name: "locales",
45
+ message: "Which other locales do you support? (comma separated)",
46
+ initial: "es, fr",
47
+ separator: ","
48
+ }
49
+ ]);
50
+ if (!response.messagesDir || !response.defaultLocale) {
51
+ console.log(chalk.red("\n\u274C Initialization canceled"));
52
+ return;
53
+ }
54
+ const allLocales = [response.defaultLocale, ...response.locales.filter((l) => l !== response.defaultLocale)];
55
+ const config = {
56
+ defaultLocale: response.defaultLocale,
57
+ locales: allLocales,
58
+ messagesDir: response.messagesDir
59
+ };
60
+ await writeFile(join(process.cwd(), CONFIG_FILE), JSON.stringify(config, null, 2));
61
+ console.log(chalk.green(`
62
+ \u2705 Created ${CONFIG_FILE}`));
63
+ const absMessagesDir = resolve(process.cwd(), response.messagesDir);
64
+ await mkdir(absMessagesDir, { recursive: true });
65
+ const baseFilePath = join(absMessagesDir, `${response.defaultLocale}.json`);
66
+ try {
67
+ await readFile(baseFilePath);
68
+ } catch {
69
+ await writeFile(baseFilePath, JSON.stringify({ welcome: "Hello World" }, null, 2));
70
+ console.log(chalk.green(`\u2705 Created base file ${response.messagesDir}/${response.defaultLocale}.json`));
71
+ }
72
+ console.log(chalk.blue('\n\u{1F389} Setup complete! You can now run "i18n-ai sync"'));
73
+ });
74
+ program.command("sync").description("Synchronize translations using AI").option("-d, --dir <path>", "Messages directory").option("-l, --locales <items>", "Comma separated list of locales").option("--default <locale>", "Default locale").option("--lock <path>", "Lock file path", "translation-lock.json").action(async (options) => {
15
75
  try {
16
76
  const cwd = process.cwd();
17
- const messagesDir = resolve(cwd, options.dir);
18
- let locales = options.locales ? options.locales.split(",") : [];
19
- let defaultLocale = options.default;
20
- if (locales.length === 0) {
21
- try {
22
- const routingPath = join(cwd, "i18n", "routing.ts");
23
- const routingContent = await readFile(routingPath, "utf-8").catch(() => "");
24
- if (routingContent) {
25
- const localesMatch = routingContent.match(/locales:\s*\[([\s\S]*?)\]/);
26
- const defaultMatch = routingContent.match(/defaultLocale:\s*["'](\w+)["']/);
27
- if (localesMatch) {
28
- locales = localesMatch[1].split(",").map((l) => l.trim().replace(/['"]/g, "")).filter(Boolean);
29
- }
30
- if (defaultMatch && !defaultLocale) {
31
- defaultLocale = defaultMatch[1];
77
+ const config = await loadConfig();
78
+ let messagesDir = options.dir ? resolve(cwd, options.dir) : config ? resolve(cwd, config.messagesDir) : null;
79
+ let locales = options.locales ? options.locales.split(",") : config ? config.locales : [];
80
+ let defaultLocale = options.default || (config ? config.defaultLocale : null);
81
+ if (!messagesDir || locales.length === 0 || !defaultLocale) {
82
+ if (!config) {
83
+ try {
84
+ const routingPath = join(cwd, "i18n", "routing.ts");
85
+ const routingContent = await readFile(routingPath, "utf-8").catch(() => "");
86
+ if (routingContent) {
87
+ const localesMatch = routingContent.match(/locales:\s*\[([\s\S]*?)\]/);
88
+ const defaultMatch = routingContent.match(/defaultLocale:\s*["'](\w+)["']/);
89
+ if (localesMatch && locales.length === 0) {
90
+ locales = localesMatch[1].split(",").map((l) => l.trim().replace(/['"]/g, "")).filter(Boolean);
91
+ }
92
+ if (defaultMatch && !defaultLocale) {
93
+ defaultLocale = defaultMatch[1];
94
+ }
32
95
  }
96
+ } catch {
33
97
  }
34
- } catch (e) {
35
98
  }
99
+ if (!messagesDir) messagesDir = resolve(cwd, "messages");
100
+ if (!defaultLocale) defaultLocale = "en";
36
101
  }
37
102
  if (locales.length === 0) {
38
- console.error("\u274C No locales found. Please specify --locales or ensure i18n/routing.ts exists.");
103
+ console.error(chalk.red('\u274C No locales found. Run "i18n-ai init" or specify --locales.'));
39
104
  process.exit(1);
40
105
  }
106
+ console.log(chalk.dim(`Using Config: Default=${defaultLocale}, Locales=${locales.join(",")}, Dir=${messagesDir}`));
41
107
  const service = new TranslationService({
42
108
  locales,
43
109
  defaultLocale,
@@ -46,7 +112,7 @@ program.command("sync").description("Synchronize translations using AI").option(
46
112
  });
47
113
  await service.sync();
48
114
  } catch (error) {
49
- console.error("Fatal error:", error);
115
+ console.error(chalk.red("Fatal error:"), error);
50
116
  process.exit(1);
51
117
  }
52
118
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ccgp/i18n-ai",
3
- "version": "0.0.2",
3
+ "version": "0.1.0",
4
4
  "description": "AI-powered i18n translation and synchronization library",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -40,10 +40,12 @@
40
40
  "chalk": "^5.4.1",
41
41
  "commander": "^12.0.0",
42
42
  "dotenv": "^16.4.5",
43
+ "prompts": "^2.4.2",
43
44
  "zod": "^4.3.5"
44
45
  },
45
46
  "devDependencies": {
46
47
  "@types/node": "^20.11.24",
48
+ "@types/prompts": "^2.4.9",
47
49
  "tsup": "^8.3.5",
48
50
  "tsx": "^4.7.1",
49
51
  "typescript": "^5.3.3"