// Проверяет словари: одинаковые ключи во всех языках и совпадение плейсхолдеров {{...}}. import { readdirSync, readFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; const dir = join(dirname(fileURLToPath(import.meta.url)), '..', 'locales'); const locales = readdirSync(dir); const flat = (obj, prefix = '') => Object.entries(obj).flatMap(([k, v]) => typeof v === 'object' ? flat(v, `${prefix}${k}.`) : [[`${prefix}${k}`, String(v)]], ); const placeholders = (s) => [...s.matchAll(/\{\{\s*(\w+)\s*\}\}/g)].map((m) => m[1]).sort().join(','); const data = {}; for (const loc of locales) { for (const file of readdirSync(join(dir, loc))) { const ns = file.replace(/\.json$/, ''); for (const [k, v] of flat(JSON.parse(readFileSync(join(dir, loc, file), 'utf8')))) { (data[`${ns}:${k}`] ??= {})[loc] = v; } } } const errors = []; for (const [key, byLoc] of Object.entries(data)) { for (const loc of locales) { if (!(loc in byLoc)) errors.push(`${loc}: отсутствует ключ ${key}`); } const sets = new Set(Object.values(byLoc).map(placeholders)); if (sets.size > 1) errors.push(`${key}: плейсхолдеры различаются между языками`); } if (errors.length) { console.error(errors.join('\n')); process.exit(1); } console.log(`i18n: ${Object.keys(data).length} ключей × ${locales.length} языка — ок`);