From f823c24fed06e8eae0b3cbe52bba750f35597abf Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Thu, 27 Nov 2025 01:32:35 +0100 Subject: [PATCH] sub --- public/landingpage/tools/i18n.php | 2 +- tools/i18n_collect_keys.php | 350 ++++++++++++++++++++++++++++++ 2 files changed, 351 insertions(+), 1 deletion(-) diff --git a/public/landingpage/tools/i18n.php b/public/landingpage/tools/i18n.php index ec69c02..c7630f4 100644 --- a/public/landingpage/tools/i18n.php +++ b/public/landingpage/tools/i18n.php @@ -2,7 +2,7 @@ // public/landingpages/tools/i18n_collect.php // Projektbasis ermitteln: /public/landingpages/tools -> / (Projektroot) -$baseDir = $_SERVER['DOCUMENT_ROOT']."../"; +$baseDir = $_SERVER['DOCUMENT_ROOT'].".."; diff --git a/tools/i18n_collect_keys.php b/tools/i18n_collect_keys.php index 41a7406..6b36e8d 100644 --- a/tools/i18n_collect_keys.php +++ b/tools/i18n_collect_keys.php @@ -21,3 +21,353 @@ declare(strict_types=1); $baseDir = dirname(__DIR__); echo $deJson = $baseDir . '/public/assets/i18n/de.json'; + +// ------------------------------- +// 1) Bestehende de.json laden +// ------------------------------- +if (!is_file($deJson)) { + die("de.json nicht gefunden unter: $deJson\n"); +} + +$raw = file_get_contents($deJson); +if ($raw === false) { + die("Konnte de.json nicht lesen: $deJson\n"); +} + +$data = json_decode($raw, true); +if (!is_array($data)) { + die("Ungültiges JSON in de.json\n"); +} + +if (!isset($data['meta']) || !is_array($data['meta'])) { + $data['meta'] = [ + 'code' => 'de', + 'label' => 'Deutsch', + 'flag' => '🇩🇪', + ]; +} + +// ------------------------------- +// 2) Verzeichnisse zum Scannen +// ------------------------------- +$scanDirs = [ + $baseDir . '/public/landingpage', + $baseDir . '/partials/landing', + $baseDir . '/partials/structure', + $baseDir . '/partials/partials', // nur, falls vorhanden +]; + +$allowedExtensions = ['php', 'html', 'htm']; + +// ------------------------------- +// 3) Helper-Funktionen +// ------------------------------- + +function dotKeyExists(array $data, string $key): bool +{ + $segments = explode('.', $key); + $node = $data; + + foreach ($segments as $seg) { + if (!is_array($node) || !array_key_exists($seg, $node)) { + return false; + } + $node = $node[$seg]; + } + return true; +} + +function addDotKey(array &$data, string $key, string $default = ''): void +{ + if (dotKeyExists($data, $key)) { + return; + } + + $segments = explode('.', $key); + $node =& $data; + + $last = array_pop($segments); + foreach ($segments as $seg) { + if (!isset($node[$seg]) || !is_array($node[$seg])) { + $node[$seg] = []; + } + $node =& $node[$seg]; + } + + if (!array_key_exists($last, $node)) { + $node[$last] = $default; + } +} + +function simpleKeyExistsRecursive(array $data, string $key): bool +{ + foreach ($data as $k => $v) { + if ($k === $key) { + return true; + } + if (is_array($v) && simpleKeyExistsRecursive($v, $key)) { + return true; + } + } + return false; +} + +function addSimpleKey(array &$data, string $key, string $defaultValue = null): void +{ + if (simpleKeyExistsRecursive($data, $key)) { + return; + } + + if (!isset($data['auto']) || !is_array($data['auto'])) { + $data['auto'] = []; + } + + if ($defaultValue === null || $defaultValue === '') { + $defaultValue = $key; + } + + if (!array_key_exists($key, $data['auto'])) { + $data['auto'][$key] = $defaultValue; + } +} + +/** + * Key einfügen, mit optionalem Default-Text. + * Dot-Notation → verschachtelt, + * Simple-Key → unter "auto". + */ +function addKeyToData(array &$data, string $key, ?string $defaultValue = null): void +{ + $key = trim($key); + if ($key === '') { + return; + } + + $default = ($defaultValue !== null && $defaultValue !== '') + ? $defaultValue + : $key; + + if (strpos($key, '.') !== false) { + addDotKey($data, $key, $default); + } else { + addSimpleKey($data, $key, $default); + } +} + +/** + * Extrahiert den „aktuellen Inhalt“ eines data-i18n-Elements + * ganz grob über Regex: + * ... data-i18n="key"> Inhalt ', $start); + if ($gtPos === false) { + return null; + } + + $closePos = strpos($content, '<', $gtPos + 1); + if ($closePos === false) { + return null; + } + + $inner = substr($content, $gtPos + 1, $closePos - $gtPos - 1); + $inner = trim($inner); + + if ($inner === '') { + return null; + } + + // Ganz grob Tags entfernen + $inner = strip_tags($inner); + $inner = trim($inner); + + return $inner !== '' ? $inner : null; +} + +/** + * Scannt eine Datei nach i18n-Keys und möglichen Default-Texten. + * + * Rückgabe: + * [ 'key1' => 'Default-Text oder null', 'key2' => null, ... ] + */ +function collectKeysFromFile(string $file): array +{ + $content = file_get_contents($file); + if ($content === false || $content === '') { + return []; + } + + $keysWithDefaults = []; + + // --- data-i18n="key" / 'key' --- + if (preg_match_all('/data-i18n\s*=\s*(["\'])(.+?)\1/i', $content, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) { + foreach ($matches as $m) { + $fullMatch = $m[0][0]; + $fullOffset = $m[0][1]; + $key = trim($m[2][0]); + + // Versuche, den Inline-Text zu extrahieren: >Text< + $inlineText = extractInlineTextForDataI18n($content, $fullOffset, strlen($fullMatch)); + + if (!array_key_exists($key, $keysWithDefaults)) { + $keysWithDefaults[$key] = $inlineText; + } elseif ($keysWithDefaults[$key] === null && $inlineText !== null) { + // Falls wir bisher keinen Default hatten, aber jetzt einen finden: + $keysWithDefaults[$key] = $inlineText; + } + } + } + + // --- i18n_get('path.to.key', 'Default') --- + if (preg_match_all( + '/\bi18n_get\s*\(\s*(["\'])([^"\']+)\1\s*(,\s*(["\'])(.*?)\4)?/i', + $content, + $m2, + PREG_SET_ORDER + )) { + foreach ($m2 as $match) { + $key = trim($match[2]); + $default = isset($match[5]) ? trim($match[5]) : null; + + if ($default !== null && $default !== '') { + // expliziter Default im PHP-Code → höchste Priorität + $keysWithDefaults[$key] = $default; + } else { + if (!array_key_exists($key, $keysWithDefaults)) { + $keysWithDefaults[$key] = null; + } + } + } + } + + // --- i18n_get_fmt("path.to.key", "Default", ...) --- + if (preg_match_all( + '/\bi18n_get_fmt\s*\(\s*(["\'])([^"\']+)\1\s*(,\s*(["\'])(.*?)\4)?/i', + $content, + $m3, + PREG_SET_ORDER + )) { + foreach ($m3 as $match) { + $key = trim($match[2]); + $default = isset($match[5]) ? trim($match[5]) : null; + + if ($default !== null && $default !== '') { + $keysWithDefaults[$key] = $default; + } else { + if (!array_key_exists($key, $keysWithDefaults)) { + $keysWithDefaults[$key] = null; + } + } + } + } + + return $keysWithDefaults; +} + +// ------------------------------- +// 4) Dateien durchlaufen & Keys sammeln +// ------------------------------- +$foundKeys = []; // key => defaultText|null + +foreach ($scanDirs as $dir) { + if (!is_dir($dir)) { + continue; + } + + $it = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS) + ); + + foreach ($it as $fileInfo) { + /** @var SplFileInfo $fileInfo */ + if (!$fileInfo->isFile()) { + continue; + } + + $ext = strtolower($fileInfo->getExtension()); + if (!in_array($ext, $allowedExtensions, true)) { + continue; + } + + $filePath = $fileInfo->getPathname(); + $keysInFile = collectKeysFromFile($filePath); + + foreach ($keysInFile as $key => $defaultText) { + if (!array_key_exists($key, $foundKeys)) { + $foundKeys[$key] = $defaultText; + } else { + // Wenn wir bisher keinen Default hatten, aber jetzt einen haben → übernehmen + if (($foundKeys[$key] === null || $foundKeys[$key] === '') + && $defaultText !== null + && $defaultText !== '' + ) { + $foundKeys[$key] = $defaultText; + } + } + } + } +} + +// ------------------------------- +// 5) Gefundene Keys in de.json eintragen +// ------------------------------- +$addedCount = 0; +$skippedCount = 0; +$newKeys = []; + +foreach ($foundKeys as $key => $defaultText) { + $before = json_encode($data); + addKeyToData($data, $key, $defaultText); + $after = json_encode($data); + + if ($before === $after) { + $skippedCount++; + } else { + $addedCount++; + $newKeys[] = $key; + } +} + +// ------------------------------- +// 6) de.json zurückschreiben +// ------------------------------- +$newJson = json_encode( + $data, + JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES +); + +if ($newJson === false) { + die("Fehler beim JSON-Encode von de.json\n"); +} + +if (file_put_contents($deJson, $newJson) === false) { + die("Konnte de.json nicht schreiben: $deJson\n"); +} + +// ------------------------------- +// 7) Ausgabe +// ------------------------------- +$isCli = (php_sapi_name() === 'cli'); + +$output = "i18n-Collect abgeschlossen.\n"; +$output .= "Gefundene Keys gesamt: " . count($foundKeys) . "\n"; +$output .= "Neu hinzugefügt: " . $addedCount . "\n"; +$output .= "Übersprungen (bereits vorhanden): $skippedCount\n"; +$output .= "Datei aktualisiert: $deJson\n"; + +if (!empty($newKeys)) { + $output .= "\nNeu angelegte Keys:\n"; + foreach ($newKeys as $k) { + $output .= " - " . $k . "\n"; + } +} + +if ($isCli) { + echo $output; +} else { + header('Content-Type: text/plain; charset=utf-8'); + echo $output; +}