i18nizer 0.5.1 â 0.6.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/dist/commands/extract.js +6 -0
- package/dist/commands/start.js +10 -0
- package/dist/commands/translate.js +7 -1
- package/dist/core/config/gitignore-manager.js +37 -0
- package/dist/core/i18n/generate-aggregator.js +86 -0
- package/dist/types/config.js +1 -0
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
package/dist/commands/extract.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Args, Command, Flags } from "@oclif/core";
|
|
2
2
|
import chalk from "chalk";
|
|
3
|
+
import os from "node:os";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import ora from "ora";
|
|
5
6
|
import { generateTranslations } from "../core/ai/client.js";
|
|
@@ -10,6 +11,7 @@ import { parseFile } from "../core/ast/parse-file.js";
|
|
|
10
11
|
import { replaceTempKeysWithT } from "../core/ast/replace-text-with-text.js";
|
|
11
12
|
import { TranslationCache } from "../core/cache/translation-cache.js";
|
|
12
13
|
import { Deduplicator } from "../core/deduplication/deduplicator.js";
|
|
14
|
+
import { generateAggregator } from "../core/i18n/generate-aggregator.js";
|
|
13
15
|
import { parseAiJson } from "../core/i18n/parse-ai-json.js";
|
|
14
16
|
import { saveSourceFile } from "../core/i18n/sace-source-file.js";
|
|
15
17
|
import { writeLocaleFiles } from "../core/i18n/write-files.js";
|
|
@@ -149,6 +151,10 @@ export default class Extract extends Command {
|
|
|
149
151
|
saveSourceFile(sourceFile);
|
|
150
152
|
// Save cache
|
|
151
153
|
cache.save();
|
|
154
|
+
// Generate aggregator (extract uses home directory for standalone mode)
|
|
155
|
+
const homeDir = os.homedir();
|
|
156
|
+
const standaloneMessagesDir = path.join(homeDir, ".i18nizer", "messages");
|
|
157
|
+
generateAggregator(standaloneMessagesDir);
|
|
152
158
|
this.log(chalk.green("⨠Component rewritten with t() calls"));
|
|
153
159
|
this.log(chalk.green(`đ JSON files generated using ${provider}`));
|
|
154
160
|
}
|
package/dist/commands/start.js
CHANGED
|
@@ -3,6 +3,7 @@ import chalk from "chalk";
|
|
|
3
3
|
import inquirer from "inquirer";
|
|
4
4
|
import ora from "ora";
|
|
5
5
|
import { detectFramework, detectI18nLibrary, generateConfig, getMessagesDir, getProjectDir, isProjectInitialized, normalizeI18nLibrary, writeConfig, } from "../core/config/config-manager.js";
|
|
6
|
+
import { ensureI18nizerDirInGitignore } from "../core/config/gitignore-manager.js";
|
|
6
7
|
export default class Start extends Command {
|
|
7
8
|
static description = "đ Initialize i18nizer in your project with framework presets";
|
|
8
9
|
static flags = {
|
|
@@ -121,6 +122,15 @@ export default class Start extends Command {
|
|
|
121
122
|
spinner.start("Setting up messages directory...");
|
|
122
123
|
const messagesDir = getMessagesDir(cwd, config);
|
|
123
124
|
spinner.succeed(`â
Created ${chalk.cyan(config.messages.path + "/")} directory`);
|
|
125
|
+
// Add .i18nizer/ to .gitignore
|
|
126
|
+
spinner.start("Adding .i18nizer/ to .gitignore...");
|
|
127
|
+
const addedToGitignore = ensureI18nizerDirInGitignore(cwd);
|
|
128
|
+
if (addedToGitignore) {
|
|
129
|
+
spinner.succeed(`â
Added ${chalk.cyan(".i18nizer/")} to .gitignore`);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
spinner.info(`âšī¸ ${chalk.cyan(".i18nizer/")} already in .gitignore`);
|
|
133
|
+
}
|
|
124
134
|
// Summary
|
|
125
135
|
this.log("");
|
|
126
136
|
this.log(chalk.green("đ i18nizer initialized successfully!"));
|
|
@@ -11,6 +11,7 @@ import { replaceTempKeysWithT } from "../core/ast/replace-text-with-text.js";
|
|
|
11
11
|
import { TranslationCache } from "../core/cache/translation-cache.js";
|
|
12
12
|
import { detectFramework, generateConfig, getMessagesDir, getProjectDir, isProjectInitialized, loadConfig, } from "../core/config/config-manager.js";
|
|
13
13
|
import { Deduplicator } from "../core/deduplication/deduplicator.js";
|
|
14
|
+
import { generateAggregator } from "../core/i18n/generate-aggregator.js";
|
|
14
15
|
import { parseAiJson } from "../core/i18n/parse-ai-json.js";
|
|
15
16
|
import { saveSourceFile } from "../core/i18n/sace-source-file.js";
|
|
16
17
|
import { writeLocaleFiles } from "../core/i18n/write-files.js";
|
|
@@ -76,7 +77,7 @@ export default class Translate extends Command {
|
|
|
76
77
|
// Override config with flags if provided
|
|
77
78
|
const locales = flags.locales
|
|
78
79
|
? flags.locales.split(",")
|
|
79
|
-
: [config.messages.defaultLocale, "es"]; //
|
|
80
|
+
: config.messages.locales || [config.messages.defaultLocale, "es"]; // Use config locales or default fallback
|
|
80
81
|
let provider = "huggingface";
|
|
81
82
|
if (flags.provider) {
|
|
82
83
|
const p = flags.provider.toLowerCase();
|
|
@@ -243,6 +244,11 @@ export default class Translate extends Command {
|
|
|
243
244
|
// Save cache
|
|
244
245
|
if (!flags["dry-run"]) {
|
|
245
246
|
cache.save();
|
|
247
|
+
// Generate aggregator if project is initialized
|
|
248
|
+
if (isInitialized) {
|
|
249
|
+
const messagesDir = getMessagesDir(cwd, config);
|
|
250
|
+
generateAggregator(messagesDir);
|
|
251
|
+
}
|
|
246
252
|
}
|
|
247
253
|
// Get statistics from deduplicator
|
|
248
254
|
const stats = deduplicator.getStats();
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const I18NIZER_DIR = ".i18nizer/";
|
|
4
|
+
/**
|
|
5
|
+
* Ensures that .i18nizer/ folder is added to .gitignore
|
|
6
|
+
*/
|
|
7
|
+
export function ensureI18nizerDirInGitignore(cwd) {
|
|
8
|
+
const gitignorePath = path.join(cwd, ".gitignore");
|
|
9
|
+
const i18nizerDirPath = path.join(cwd, ".i18nizer");
|
|
10
|
+
// Check if .i18nizer directory exists
|
|
11
|
+
if (!fs.existsSync(i18nizerDirPath)) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
// Create .gitignore if it doesn't exist
|
|
15
|
+
if (!fs.existsSync(gitignorePath)) {
|
|
16
|
+
fs.writeFileSync(gitignorePath, "", "utf8");
|
|
17
|
+
}
|
|
18
|
+
// Read current .gitignore content
|
|
19
|
+
const gitignoreContent = fs.readFileSync(gitignorePath, "utf8");
|
|
20
|
+
const lines = gitignoreContent.split("\n");
|
|
21
|
+
// Check if .i18nizer/ is already ignored (exact match or pattern match)
|
|
22
|
+
const isAlreadyIgnored = lines.some((line) => line.trim() === I18NIZER_DIR ||
|
|
23
|
+
line.trim() === `/${I18NIZER_DIR}` ||
|
|
24
|
+
line.trim() === ".i18nizer" ||
|
|
25
|
+
line.trim() === "/.i18nizer" ||
|
|
26
|
+
line.trim() === "**/.i18nizer/" ||
|
|
27
|
+
line.trim() === "**/.i18nizer");
|
|
28
|
+
if (isAlreadyIgnored) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
// Add .i18nizer/ to .gitignore
|
|
32
|
+
const newContent = gitignoreContent.trim() === ""
|
|
33
|
+
? `${I18NIZER_DIR}\n`
|
|
34
|
+
: `${gitignoreContent}${gitignoreContent.endsWith("\n") ? "" : "\n"}${I18NIZER_DIR}\n`;
|
|
35
|
+
fs.writeFileSync(gitignorePath, newContent, "utf8");
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
/**
|
|
5
|
+
* Generates the aggregator TypeScript file that imports all translation JSON files
|
|
6
|
+
* and exports them as a single messages object.
|
|
7
|
+
*/
|
|
8
|
+
export function generateAggregator(messagesDir, outputDir) {
|
|
9
|
+
const baseMessagesDir = path.resolve(messagesDir);
|
|
10
|
+
if (!fs.existsSync(baseMessagesDir)) {
|
|
11
|
+
console.log(chalk.yellow("â ī¸ Messages directory does not exist, skipping aggregator generation"));
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
// Scan for locales and namespaces
|
|
15
|
+
const locales = fs
|
|
16
|
+
.readdirSync(baseMessagesDir, { withFileTypes: true })
|
|
17
|
+
.filter((dirent) => dirent.isDirectory())
|
|
18
|
+
.map((dirent) => dirent.name)
|
|
19
|
+
.sort();
|
|
20
|
+
if (locales.length === 0) {
|
|
21
|
+
console.log(chalk.yellow("â ī¸ No locale directories found, skipping aggregator generation"));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
// Collect all JSON files organized by locale
|
|
25
|
+
const filesByLocale = new Map();
|
|
26
|
+
for (const locale of locales) {
|
|
27
|
+
const localeDir = path.join(baseMessagesDir, locale);
|
|
28
|
+
const files = fs
|
|
29
|
+
.readdirSync(localeDir)
|
|
30
|
+
.filter((file) => file.endsWith(".json"))
|
|
31
|
+
.sort();
|
|
32
|
+
if (files.length > 0) {
|
|
33
|
+
filesByLocale.set(locale, files);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (filesByLocale.size === 0) {
|
|
37
|
+
console.log(chalk.yellow("â ī¸ No JSON files found, skipping aggregator generation"));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// Generate imports and exports
|
|
41
|
+
const imports = [];
|
|
42
|
+
const exportsByLocale = new Map();
|
|
43
|
+
for (const [locale, files] of filesByLocale) {
|
|
44
|
+
const localeExports = [];
|
|
45
|
+
for (const file of files) {
|
|
46
|
+
const namespace = path.basename(file, ".json");
|
|
47
|
+
const importName = `${namespace}_${locale}`;
|
|
48
|
+
const relativePath = `../messages/${locale}/${file}`;
|
|
49
|
+
imports.push(`import ${importName} from "${relativePath}";`);
|
|
50
|
+
localeExports.push(` ...${importName},`);
|
|
51
|
+
}
|
|
52
|
+
exportsByLocale.set(locale, localeExports);
|
|
53
|
+
}
|
|
54
|
+
// Build the output content
|
|
55
|
+
const lines = [
|
|
56
|
+
"/**",
|
|
57
|
+
" * Auto-generated aggregator file.",
|
|
58
|
+
" * DO NOT EDIT MANUALLY - This file is regenerated by i18nizer.",
|
|
59
|
+
" */",
|
|
60
|
+
"",
|
|
61
|
+
...imports,
|
|
62
|
+
"",
|
|
63
|
+
"export const messages = {",
|
|
64
|
+
];
|
|
65
|
+
for (const [locale, exports] of exportsByLocale) {
|
|
66
|
+
lines.push(` ${locale}: {`);
|
|
67
|
+
lines.push(...exports);
|
|
68
|
+
lines.push(" },");
|
|
69
|
+
}
|
|
70
|
+
lines.push("} as const;");
|
|
71
|
+
lines.push("");
|
|
72
|
+
// Write the file
|
|
73
|
+
const i18nDir = outputDir ?? path.join(path.dirname(baseMessagesDir), "i18n");
|
|
74
|
+
fs.mkdirSync(i18nDir, { recursive: true });
|
|
75
|
+
const outputPath = path.join(i18nDir, "messages.generated.ts");
|
|
76
|
+
fs.writeFileSync(outputPath, lines.join("\n"), "utf8");
|
|
77
|
+
console.log(chalk.green(`⨠Aggregator generated: ${outputPath}`));
|
|
78
|
+
// Log statistics
|
|
79
|
+
const namespaces = new Set();
|
|
80
|
+
for (const files of filesByLocale.values()) {
|
|
81
|
+
for (const file of files) {
|
|
82
|
+
namespaces.add(path.basename(file, ".json"));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
console.log(chalk.cyan(`đĻ Processed ${locales.length} locales, ${namespaces.size} namespaces`));
|
|
86
|
+
}
|
package/dist/types/config.js
CHANGED
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "i18nizer",
|
|
3
3
|
"description": "CLI to extract texts from JSX/TSX and generate i18n JSON with AI translations",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.6.0",
|
|
5
5
|
"author": "Yoannis Sanchez Soto",
|
|
6
6
|
"bin": "./bin/run.js",
|
|
7
7
|
"bugs": "https://github.com/yossTheDev/i18nizer/issues",
|