diff --git a/tools/i18n_collect_keys.php b/tools/i18n_collect_keys.php index 87eb538..4fd5096 100644 --- a/tools/i18n_collect_keys.php +++ b/tools/i18n_collect_keys.php @@ -1,12 +1,12 @@ '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; +}