#!/usr/bin/env node /** * Vergleicht en- mit ceb-Locale-Chunks (wie in src/i18n/index.js). * - identical: Blattstrings, die nach Merge noch gleich en sind (typisch: fehlende Bisaya-Übersetzung). * - germanHints: Blattstrings in ceb mit deutschen Anzeichen (Umlaute / typische Wörter). * * Usage: node frontend/scripts/audit-ceb-locale.mjs */ import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const localesRoot = path.join(__dirname, '../src/i18n/locales'); const EN_CHUNKS = [ 'general.json', 'header.json', 'navigation.json', 'home.json', 'chat.json', 'register.json', 'passwordReset.json', 'error.json', 'activate.json', 'settings.json', 'admin.json', 'socialnetwork.json', 'friends.json', 'falukant.json', 'blog.json', 'minigames.json', 'message.json', 'personal.json', 'seo.json', ]; function isPlainObject(value) { return value !== null && typeof value === 'object' && !Array.isArray(value); } function deepMerge(target, source) { if (!isPlainObject(source)) return target; const base = isPlainObject(target) ? { ...target } : {}; for (const key of Object.keys(source)) { const sv = source[key]; const tv = base[key]; if (isPlainObject(sv) && isPlainObject(tv)) { base[key] = deepMerge(tv, sv); } else if (isPlainObject(sv)) { base[key] = deepMerge({}, sv); } else { base[key] = sv; } } return base; } function loadJson(rel) { const p = path.join(localesRoot, rel); const raw = fs.readFileSync(p, 'utf8'); return JSON.parse(raw); } function flatten(obj, prefix = '', out = {}) { if (!isPlainObject(obj)) return out; for (const k of Object.keys(obj)) { const v = obj[k]; const key = prefix ? `${prefix}.${k}` : k; if (isPlainObject(v)) { flatten(v, key, out); } else if (typeof v === 'string') { out[key] = v; } } return out; } const germanWordHints = /\b(der|die|das|und|nicht|wird|werden|schon|keine|kein|Ziel|Ziele|Zug|Züge|Spiel|Level|Bitte|Hier|wurde|können|müssen)\b/i; const germanChars = /[äöüÄÖÜß]/; function audit() { let enMerged = {}; let cebMerged = {}; for (const f of EN_CHUNKS) { const enPath = path.join('en', f); const cebPath = path.join('ceb', f); if (!fs.existsSync(path.join(localesRoot, cebPath))) { console.warn(`Warnung: fehlt ${cebPath} (übersprungen)`); continue; } enMerged = deepMerge(enMerged, loadJson(enPath)); cebMerged = deepMerge(cebMerged, loadJson(enPath)); cebMerged = deepMerge(cebMerged, loadJson(cebPath)); } const enFlat = flatten(enMerged); const cebFlat = flatten(cebMerged); const identical = []; const germanHints = []; for (const key of Object.keys(cebFlat)) { const ev = enFlat[key]; const cv = cebFlat[key]; if (typeof cv !== 'string') continue; if (ev === cv && typeof ev === 'string') { identical.push(key); } if (germanChars.test(cv) || germanWordHints.test(cv)) { germanHints.push({ key, sample: cv.slice(0, 120) }); } } identical.sort(); germanHints.sort((a, b) => a.key.localeCompare(b.key)); const report = { generated: new Date().toISOString(), summary: { totalLeafStringsCeb: Object.keys(cebFlat).length, identicalToEnCount: identical.length, germanHintCount: germanHints.length, }, identicalToEn: identical, germanHintsInCeb: germanHints, }; const outFile = path.join(__dirname, '../ceb-locale-audit-report.json'); fs.writeFileSync(outFile, JSON.stringify(report, null, 2), 'utf8'); console.log(`Bericht geschrieben: ${outFile}`); console.log( `Zusammenfassung: ${report.summary.identicalToEnCount} Blätter noch identisch mit EN, ` + `${report.summary.germanHintCount} Strings mit DE-Hinweis (heuristisch).` ); } audit();