isFile()) { $ext = $file->getExtension(); if ($ext === 'php' || $ext === 'blade') { $files[] = $file->getPathname(); } } } } echo "Scanning " . count($files) . " files (PHP + Blade)...\n\n"; // Find all translation calls $foundStrings = []; $patterns = [ '/__\(\s*[\'"]([^\'"]+)[\'"]\s*\)/', // __('string') '/__\(\s*"([^"]+)"\s*\)/', // __("string") '/trans\(\s*[\'"]([^\'"]+)[\'"]\s*\)/', // trans('string') '/@lang\(\s*[\'"]([^\'"]+)[\'"]\s*\)/', // @lang('string') '/\{\{\s*__\(\s*[\'"]([^\'"]+)[\'"]\s*\)\s*\}\}/', // {{ __('string') }} '/\{\{\s*trans\(\s*[\'"]([^\'"]+)[\'"]\s*\)\s*\}\}/', // {{ trans('string') }} ]; $totalFound = 0; foreach ($files as $file) { $content = file_get_contents($file); $relativePath = str_replace(getcwd() . '/', '', $file); foreach ($patterns as $pattern) { if (preg_match_all($pattern, $content, $matches)) { foreach ($matches[1] as $string) { // Skip if it's a variable or complex expression if (strpos($string, '$') !== false) continue; if (strpos($string, '{') !== false) continue; if (strpos($string, '.') === 0) continue; if (empty(trim($string))) continue; $totalFound++; if (!isset($foundStrings[$string])) { $foundStrings[$string] = [ 'string' => $string, 'files' => [] ]; } $foundStrings[$string]['files'][] = $relativePath; } } } } echo "Found " . $totalFound . " translation calls\n"; echo "Found " . count($foundStrings) . " unique translation strings\n\n"; // Find missing strings $missing = []; foreach ($foundStrings as $string => $info) { if (!isset($existing[$string])) { $missing[$string] = $info; } } echo "Missing from en.json: " . count($missing) . " strings\n"; echo str_repeat('=', 100) . "\n\n"; if (count($missing) > 0) { echo "Missing translation strings:\n"; echo str_repeat('-', 100) . "\n"; // Group by first occurrence file foreach ($missing as $string => $info) { $firstFile = $info['files'][0]; $fileCount = count($info['files']); $fileInfo = $fileCount > 1 ? " (used in $fileCount files)" : ""; echo sprintf("%-60s %s%s\n", substr($string, 0, 60), basename($firstFile), $fileInfo ); } echo "\n" . str_repeat('=', 100) . "\n\n"; // Automatically add them echo "Adding " . count($missing) . " new keys to en.json...\n"; // Add missing strings to en.json foreach ($missing as $string => $info) { $existing[$string] = $string; // Use the string itself as the value } // Sort alphabetically ksort($existing); // Backup first copy($enFile, $enFile . '.backup'); // Save to file file_put_contents( $enFile, json_encode($existing, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL ); echo "\nāœ“ Added " . count($missing) . " new keys to en.json\n"; echo "āœ“ Backup created: en.json.backup\n"; echo "āœ“ Total keys in en.json: " . count($existing) . "\n\n"; // Save detailed report $report = "Missing translation strings found:\n\n"; foreach ($missing as $string => $info) { $report .= "Key: $string\n"; $report .= "Used in:\n"; foreach ($info['files'] as $file) { $report .= " - $file\n"; } $report .= "\n"; } file_put_contents('/tmp/missing-translations-report.txt', $report); echo "Detailed report saved to: /tmp/missing-translations-report.txt\n\n"; echo "Next steps:\n"; echo "1. Review the new keys in en.json and edit values if needed\n"; echo "2. Run: ./translate-new-keys.sh\n"; echo "3. Clear cache: php artisan config:clear && php artisan cache:clear\n"; } else { echo "āœ“ All translation strings are already in en.json!\n"; echo "\nSummary:\n"; echo " - Scanned " . count($files) . " files\n"; echo " - Found " . $totalFound . " translation calls\n"; echo " - All " . count($foundStrings) . " unique strings are in en.json\n"; }