feat(i18n): update Cebuano localization for minigames and enhance UI text
All checks were successful
Deploy to production / deploy (push) Successful in 1m56s
All checks were successful
Deploy to production / deploy (push) Successful in 1m56s
- Added new localized strings for minigames in Cebuano, including loading hints, objectives, and play focus descriptions to improve user experience. - Updated existing translations for clarity and consistency across various game elements. - Enhanced the Match3Game component to utilize localized strings, ensuring dynamic text rendering based on user language preferences. - Included a new entry in .gitignore for the locale audit report to maintain a clean repository.
This commit is contained in:
140
frontend/scripts/audit-ceb-locale.mjs
Normal file
140
frontend/scripts/audit-ceb-locale.mjs
Normal file
@@ -0,0 +1,140 @@
|
||||
#!/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();
|
||||
Reference in New Issue
Block a user