'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; } /** * Fügt einen einfachen Key irgendwo in der Struktur ein * (früher unter $data['auto'], jetzt nur noch für Reste). * * WICHTIG: $defaultValue ist explizit nullable → keine Deprecation. */ 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 → (Rest) unter "auto". * * Dynamische Keys mit $ (z.B. pages.$pageKey.meta.title) werden ignoriert. */ function addKeyToData(array &$data, string $key, ?string $defaultValue = null): void { $key = trim($key); if ($key === '') { return; } // Dynamische Keys mit Variablen wie pages.$pageKey.meta.title ignorieren if (strpos($key, '$') !== false) { 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 * 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, ... ] * * Dynamische Keys mit $ (pages.$pageKey...) werden bereits hier * ignoriert. */ 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]); if (strpos($key, '$') !== false) { continue; } // 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]); if (strpos($key, '$') !== false) { continue; } $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]); if (strpos($key, '$') !== false) { continue; } $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(); $relPath = str_replace($baseDir . DIRECTORY_SEPARATOR, '', $filePath); $keysInFile = collectKeysFromFile($filePath); foreach ($keysInFile as $key => $defaultText) { $origKey = $key; // Nur für einfache Keys (ohne Punkt) Kontext-basiert umschreiben if (strpos($key, '.') === false) { // 4a) partials/landing/{slug}/{section}.php if (preg_match('~^partials/landing/([^/]+)/([^/]+)\.(php|html?|phtml)$~', $relPath, $m)) { $slug = $m[1]; // z.B. landing, fakecheck, login, dashboard $section = $m[2]; // z.B. hero, how, problem, features, security, faq, main $key = "pages.$slug.sections.$section.$key"; // 4b) public/landingpage/{slug}/*.php → Fallback: section "main" } elseif (preg_match('~^public/landingpage/([^/]+)/.+\.(php|html?|phtml)$~', $relPath, $m)) { $slug = $m[1]; // z.B. landing, fakecheck, login, dashboard $key = "pages.$slug.sections.main.$key"; // 4c) partials/structure/{name}.php → z.B. header.*, footer.*, layout_* } elseif (preg_match('~^partials/structure/([^/]+)\.(php|html?|phtml)$~', $relPath, $m)) { $section = $m[1]; // header, footer, layout_start, layout_end, app_config, ... // NEU: unter partials.structure.{section}.{key} $key = "partials.structure.$section.$key"; } } // Gefilterten Key übernehmen 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; } } // ------------------------------- // 5b) Metadaten für alle Landingpages ergänzen // /public/landingpage/{slug}/ → pages.{slug}.meta.{title,description} // ------------------------------- $landingRoot = $baseDir . '/public/landingpage'; if (is_dir($landingRoot)) { foreach (new DirectoryIterator($landingRoot) as $entry) { if ($entry->isDot() || !$entry->isDir()) { continue; } $slug = $entry->getFilename(); // einfache Heuristik: Landingpage gilt als "aktiv", wenn irgendeine .php drin liegt $hasPhp = false; foreach (new DirectoryIterator($entry->getPathname()) as $file) { if ($file->isFile() && strtolower($file->getExtension()) === 'php') { $hasPhp = true; break; } } if (!$hasPhp) { continue; } $titleKey = "pages.$slug.meta.title"; $descKey = "pages.$slug.meta.description"; if (!dotKeyExists($data, $titleKey)) { $defaultTitle = '{{primary_domain}} – ' . ucfirst($slug); addDotKey($data, $titleKey, $defaultTitle); $newKeys[] = $titleKey; $addedCount++; } if (!dotKeyExists($data, $descKey)) { $defaultDesc = 'Beschreibung für ' . ucfirst($slug) . ' auf {{primary_domain}}'; addDotKey($data, $descKey, $defaultDesc); $newKeys[] = $descKey; $addedCount++; } } } // ------------------------------- // 5c) meta-Keys in jedem Block nach vorne ziehen // ------------------------------- function reorderMetaFirstRecursive(array &$node): void { // Zuerst rekursiv in die Tiefe gehen foreach ($node as $k => &$v) { if (is_array($v)) { reorderMetaFirstRecursive($v); } } unset($v); // Wenn es in diesem Block einen 'meta'-Key gibt, diesen an erste Stelle setzen if (array_key_exists('meta', $node) && count($node) > 1) { $metaValue = $node['meta']; unset($node['meta']); // Rest in aktueller Reihenfolge behalten $rest = $node; // Neu zusammensetzen: meta zuerst $node = ['meta' => $metaValue] + $rest; } } reorderMetaFirstRecursive($data); // ------------------------------- // 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 { if (!headers_sent()) { header('Content-Type: text/plain; charset=utf-8'); } echo $output; }