#!/usr/bin/env node /** * Setzt bewusste Cognates/Produktnamen explizit in allen Locales (Wert = en-US oder de). */ const fs = require('fs'); const path = require('path'); const ROOT = path.resolve(__dirname, '..'); const LOCALES_DIR = path.join(ROOT, 'frontend', 'src', 'i18n', 'locales'); const LOCALES = [ 'fr', 'es', 'it', 'pl', 'ja', 'zh', 'th', 'tl', 'fil', 'en-US', 'en-GB', 'en-AU', ]; // Keys, die in Nicht-DE-Locales explizit gesetzt werden (Wert aus en-US-Merge) const COGNATE_KEYS = [ 'common.filter', 'common.details', 'common.name', 'common.status', 'common.optional', 'common.in', 'common.ok', 'navigation.clickTtBrowser', 'members.rowTooltipSeparator', 'members.ttrQttr', 'members.status', 'members.clickTtRequestShort', 'diary.standardDurationShort', 'diary.min', 'trainingStats.name', 'trainingStats.ttr', 'trainingStats.qttr', 'tournaments.knockoutLabel', 'tournaments.genderMixed', 'tournaments.optional', 'tournaments.index', 'tournaments.diff', 'billing.iban', 'schedule.code', 'schedule.team', 'schedule.teams', 'schedule.vs', 'teamManagement.team', 'teamManagement.teams', 'courtDrawingTool.strokeTypeTopspin', 'courtDrawingTool.strokeTypeFlip', 'courtDrawingTool.strokeTypeBlock', 'matchReportApi.vs', 'courtDrawing.ok', 'pdfGenerator.diff', 'pdfGenerator.lineupQttr', 'pendingApprovals.email', 'permissions.email', 'logs.details', 'logs.logDetailsLabels.ip', 'dialogs.info.ok', 'dialogs.confirm.ok', 'memberTransferDialog.server', 'memberTransferDialog.endpoint', 'memberTransferDialog.format', 'myTischtennis.title', 'myTischtennisAccount.title', 'myTischtennisAccount.teams', 'memberNotes.tags', 'nuscoreAnalyzer.title', 'predefinedActivities.min', ]; const EXTRA = { fr: { 'members.address': 'Adresse' }, es: { 'members.address': 'Dirección' }, it: { 'members.address': 'Indirizzo' }, pl: { 'members.address': 'Adres' }, ja: { 'members.address': '住所' }, zh: { 'members.address': '地址' }, th: { 'members.address': 'ที่อยู่' }, tl: { 'members.address': 'Address', 'languages.th': 'Thai (ไทย)' }, fil: { 'members.address': 'Address', 'languages.th': 'Thai (ไทย)' }, }; function flatten(obj, prefix = '', out = {}) { for (const [key, value] of Object.entries(obj || {})) { const nextKey = prefix ? `${prefix}.${key}` : key; if (value && typeof value === 'object' && !Array.isArray(value)) { flatten(value, nextKey, out); } else if (typeof value === 'string') { out[nextKey] = value; } } return out; } function deepMerge(base, override) { if (!base || typeof base !== 'object' || Array.isArray(base)) return override ?? base; const result = { ...base }; for (const [key, value] of Object.entries(override || {})) { if ( value && typeof value === 'object' && !Array.isArray(value) && result[key] && typeof result[key] === 'object' && !Array.isArray(result[key]) ) { result[key] = deepMerge(result[key], value); } else { result[key] = value; } } return result; } function setByPath(obj, dotPath, value) { const parts = dotPath.split('.'); let cur = obj; for (let i = 0; i < parts.length - 1; i++) { if (!cur[parts[i]] || typeof cur[parts[i]] !== 'object') cur[parts[i]] = {}; cur = cur[parts[i]]; } cur[parts[parts.length - 1]] = value; } function buildOverrides(deFlat, targetFlat) { const out = {}; for (const [key, value] of Object.entries(targetFlat)) { if (value !== deFlat[key]) setByPath(out, key, value); } return out; } const de = JSON.parse(fs.readFileSync(path.join(LOCALES_DIR, 'de.json'), 'utf8')); const enUs = JSON.parse(fs.readFileSync(path.join(LOCALES_DIR, 'en-US.json'), 'utf8')); const deFlat = flatten(de); const enMerged = flatten(deepMerge(JSON.parse(JSON.stringify(de)), enUs)); for (const locale of LOCALES) { const localePath = path.join(LOCALES_DIR, `${locale}.json`); if (!fs.existsSync(localePath)) continue; const localeJson = JSON.parse(fs.readFileSync(localePath, 'utf8')); const merged = flatten(deepMerge(JSON.parse(JSON.stringify(de)), localeJson)); for (const key of COGNATE_KEYS) { const val = enMerged[key] ?? deFlat[key]; if (val !== undefined) merged[key] = val; } for (const [key, val] of Object.entries(EXTRA[locale] || {})) { merged[key] = val; } const overrides = buildOverrides(deFlat, merged); fs.writeFileSync(localePath, `${JSON.stringify(overrides, null, 2)}\n`, 'utf8'); const stillDe = Object.keys(deFlat).filter((k) => merged[k] === deFlat[k]).length; console.log(`${locale}: overrides=${Object.keys(flatten(overrides)).length}, stillDe=${stillDe}`); }