check-i18n.js
1 #!/usr/bin/env node 2 3 import { readdirSync, readFileSync, statSync } from "fs"; 4 import { dirname, join } from "path"; 5 import process from "process"; 6 import { fileURLToPath } from "url"; 7 8 const __dirname = dirname(fileURLToPath(import.meta.url)); 9 10 const LOCALES_DIR = join(__dirname, "../public/locales"); 11 const I18N_FILE = join(__dirname, "../src/i18n.ts"); 12 13 const minLocaleFileSize = 1024 * 10; // Minimum 10kB for a locale file to be considered 14 function getLocaleFolders() { 15 return readdirSync(LOCALES_DIR).filter((folder) => { 16 const folderPath = join(LOCALES_DIR, folder); 17 const commonFilePath = join(folderPath, "common.json"); 18 return ( 19 statSync(folderPath).isDirectory() && 20 statSync(commonFilePath).isFile() && 21 statSync(commonFilePath).size >= minLocaleFileSize 22 ); 23 }); 24 } 25 26 function getDeclaredLanguages() { 27 const i18nContent = readFileSync(I18N_FILE, "utf8"); 28 const languageMatches = [...i18nContent.matchAll(/\["(.*?)"\]:/g)]; 29 return languageMatches.map((match) => match[1]); 30 } 31 32 function main() { 33 const foundLocales = new Set(getLocaleFolders()); 34 const declaredLocales = new Set(getDeclaredLanguages()); 35 36 const missingLocales = [...foundLocales].filter((locale) => !declaredLocales.has(locale)); 37 38 if (missingLocales.length > 0) { 39 console.error("❌ The following locales are missing from src/i18n.ts:"); 40 missingLocales.forEach((locale) => console.error(` - ${locale}`)); 41 console.error("⚠️ Please add them to the `languages` object in i18n.ts."); 42 console.log("Template:"); 43 for (const locale of missingLocales) { 44 console.log(`["${locale}"]: { 45 name: "", 46 fullCode: "", 47 djs: () => import("dayjs/locale/${locale.toLowerCase()}"), 48 },`); 49 } 50 process.exit(1); 51 } 52 53 console.log("✅ All locales are properly declared in i18n.ts."); 54 process.exit(0); 55 } 56 57 main();