@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 +1 -3
- package/dist/cli.js +85 -19
- package/dist/cli.mjs +86 -20
- package/package.json +3 -1
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.
|
|
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
|
-
|
|
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
|
|
321
|
-
let
|
|
322
|
-
let
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
18
|
-
let
|
|
19
|
-
let
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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(
|
|
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
|
|
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"
|