| #!/usr/bin/env node
|
| |
| |
| |
| |
| |
| |
|
|
| import { readFileSync, readdirSync, statSync } from "node:fs";
|
| import { join, dirname } from "node:path";
|
| import { fileURLToPath } from "node:url";
|
|
|
| const __dirname = dirname(fileURLToPath(import.meta.url));
|
| const ROOT = join(__dirname, "..", "..");
|
| const COMMANDS_DIR = join(ROOT, "bin", "cli", "commands");
|
| const LOCALES_DIR = join(ROOT, "bin", "cli", "locales");
|
|
|
|
|
| const IGNORE_AS_KEY = new Set([".", ".."]);
|
| const IMPORT_PATH_RE = /^(\.\.?\/|node:|\/)/;
|
|
|
| function walk(dir) {
|
| const results = [];
|
| for (const entry of readdirSync(dir)) {
|
| const full = join(dir, entry);
|
| if (statSync(full).isDirectory()) {
|
| results.push(...walk(full));
|
| } else if (entry.endsWith(".mjs") || entry.endsWith(".js")) {
|
| results.push(full);
|
| }
|
| }
|
| return results;
|
| }
|
|
|
| function flattenKeys(obj, prefix = "") {
|
| const keys = new Set();
|
| for (const [k, v] of Object.entries(obj)) {
|
| const full = prefix ? `${prefix}.${k}` : k;
|
| if (v !== null && typeof v === "object" && !Array.isArray(v)) {
|
| for (const sub of flattenKeys(v, full)) keys.add(sub);
|
| } else {
|
| keys.add(full);
|
| }
|
| }
|
| return keys;
|
| }
|
|
|
| function collectTKeys(files) {
|
| const used = new Set();
|
| const re = /\bt\(\s*["']([^"']+)["']/g;
|
| for (const file of files) {
|
| const src = readFileSync(file, "utf8");
|
| let m;
|
| re.lastIndex = 0;
|
| while ((m = re.exec(src)) !== null) {
|
| const key = m[1];
|
| if (IGNORE_AS_KEY.has(key) || IMPORT_PATH_RE.test(key)) continue;
|
| used.add(key);
|
| }
|
| }
|
| return used;
|
| }
|
|
|
| function loadJson(file) {
|
| return JSON.parse(readFileSync(file, "utf8"));
|
| }
|
|
|
| const files = walk(COMMANDS_DIR);
|
| const usedKeys = collectTKeys(files);
|
| const en = loadJson(join(LOCALES_DIR, "en.json"));
|
| const ptBR = loadJson(join(LOCALES_DIR, "pt-BR.json"));
|
| const enKeys = flattenKeys(en);
|
|
|
| let errors = 0;
|
|
|
|
|
| const missingInEn = [...usedKeys].filter((k) => !enKeys.has(k));
|
| if (missingInEn.length > 0) {
|
| console.error("[cli-i18n] Keys used in commands but missing in en.json:");
|
| for (const k of missingInEn) console.error(` β ${k}`);
|
| errors += missingInEn.length;
|
| } else {
|
| console.log(`[cli-i18n] β All ${usedKeys.size} t() keys found in en.json`);
|
| }
|
|
|
|
|
| const enTopLevel = Object.keys(en);
|
| const ptTopLevel = new Set(Object.keys(ptBR));
|
| const missingTopLevel = enTopLevel.filter((k) => !ptTopLevel.has(k));
|
| if (missingTopLevel.length > 0) {
|
| console.error("[cli-i18n] Top-level sections in en.json missing from pt-BR.json:");
|
| for (const k of missingTopLevel) console.error(` β ${k}`);
|
| errors += missingTopLevel.length;
|
| } else {
|
| console.log(`[cli-i18n] β pt-BR.json has all ${enTopLevel.length} top-level sections`);
|
| }
|
|
|
| if (errors > 0) {
|
| console.error(`[cli-i18n] FAIL β ${errors} error(s) found`);
|
| process.exit(1);
|
| } else {
|
| console.log("[cli-i18n] PASS β CLI i18n is consistent");
|
| }
|
|
|