Blame | Letzte Änderung | Log anzeigen | RSS feed
<?php/** This file is part of the Symfony package.** (c) Fabien Potencier <fabien@symfony.com>** For the full copyright and license information, please view the LICENSE* file that was distributed with this source code.*/if ('cli' !== \PHP_SAPI) {throw new Exception('This script must be run from the command line.');}$usageInstructions = <<<ENDUsage instructions-------------------------------------------------------------------------------$ cd symfony-code-root-directory/# show the translation status of all locales$ php translation-status.php# only show the translation status of incomplete or erroneous locales$ php translation-status.php --incomplete# show the translation status of all locales, all their missing translations and mismatches between trans-unit id and source$ php translation-status.php -v# show the status of a single locale$ php translation-status.php fr# show the status of a single locale, missing translations and mismatches between trans-unit id and source$ php translation-status.php fr -vEND;$config = [// if TRUE, the full list of missing translations is displayed'verbose_output' => false,// NULL = analyze all locales'locale_to_analyze' => null,// append --incomplete to only show incomplete languages'include_completed_languages' => true,// the reference files all the other translations are compared to'original_files' => ['src/Symfony/Component/Form/Resources/translations/validators.en.xlf','src/Symfony/Component/Security/Core/Resources/translations/security.en.xlf','src/Symfony/Component/Validator/Resources/translations/validators.en.xlf',],];$argc = $_SERVER['argc'];$argv = $_SERVER['argv'];if ($argc > 4) {echo str_replace('translation-status.php', $argv[0], $usageInstructions);exit(1);}foreach (array_slice($argv, 1) as $argumentOrOption) {if ('--incomplete' === $argumentOrOption) {$config['include_completed_languages'] = false;continue;}if (str_starts_with($argumentOrOption, '-')) {$config['verbose_output'] = true;} else {$config['locale_to_analyze'] = $argumentOrOption;}}foreach ($config['original_files'] as $originalFilePath) {if (!file_exists($originalFilePath)) {echo sprintf('The following file does not exist. Make sure that you execute this command at the root dir of the Symfony code repository.%s %s', \PHP_EOL, $originalFilePath);exit(1);}}$totalMissingTranslations = 0;$totalTranslationMismatches = 0;foreach ($config['original_files'] as $originalFilePath) {$translationFilePaths = findTranslationFiles($originalFilePath, $config['locale_to_analyze']);$translationStatus = calculateTranslationStatus($originalFilePath, $translationFilePaths);$totalMissingTranslations += array_sum(array_map(function ($translation) {return count($translation['missingKeys']);}, array_values($translationStatus)));$totalTranslationMismatches += array_sum(array_map(function ($translation) {return count($translation['mismatches']);}, array_values($translationStatus)));printTranslationStatus($originalFilePath, $translationStatus, $config['verbose_output'], $config['include_completed_languages']);}exit($totalTranslationMismatches > 0 ? 1 : 0);function findTranslationFiles($originalFilePath, $localeToAnalyze){$translations = [];$translationsDir = dirname($originalFilePath);$originalFileName = basename($originalFilePath);$translationFileNamePattern = str_replace('.en.', '.*.', $originalFileName);$translationFiles = glob($translationsDir.'/'.$translationFileNamePattern, \GLOB_NOSORT);sort($translationFiles);foreach ($translationFiles as $filePath) {$locale = extractLocaleFromFilePath($filePath);if (null !== $localeToAnalyze && $locale !== $localeToAnalyze) {continue;}$translations[$locale] = $filePath;}return $translations;}function calculateTranslationStatus($originalFilePath, $translationFilePaths){$translationStatus = [];$allTranslationKeys = extractTranslationKeys($originalFilePath);foreach ($translationFilePaths as $locale => $translationPath) {$translatedKeys = extractTranslationKeys($translationPath);$missingKeys = array_diff_key($allTranslationKeys, $translatedKeys);$mismatches = findTransUnitMismatches($allTranslationKeys, $translatedKeys);$translationStatus[$locale] = ['total' => count($allTranslationKeys),'translated' => count($translatedKeys),'missingKeys' => $missingKeys,'mismatches' => $mismatches,];$translationStatus[$locale]['is_completed'] = isTranslationCompleted($translationStatus[$locale]);}return $translationStatus;}function isTranslationCompleted(array $translationStatus): bool{return $translationStatus['total'] === $translationStatus['translated'] && 0 === count($translationStatus['mismatches']);}function printTranslationStatus($originalFilePath, $translationStatus, $verboseOutput, $includeCompletedLanguages){printTitle($originalFilePath);printTable($translationStatus, $verboseOutput, $includeCompletedLanguages);echo \PHP_EOL.\PHP_EOL;}function extractLocaleFromFilePath($filePath){$parts = explode('.', $filePath);return $parts[count($parts) - 2];}function extractTranslationKeys($filePath){$translationKeys = [];$contents = new \SimpleXMLElement(file_get_contents($filePath));foreach ($contents->file->body->{'trans-unit'} as $translationKey) {$translationId = (string) $translationKey['id'];$translationKey = (string) $translationKey->source;$translationKeys[$translationId] = $translationKey;}return $translationKeys;}/*** Check whether the trans-unit id and source match with the base translation.*/function findTransUnitMismatches(array $baseTranslationKeys, array $translatedKeys): array{$mismatches = [];foreach ($baseTranslationKeys as $translationId => $translationKey) {if (!isset($translatedKeys[$translationId])) {continue;}if ($translatedKeys[$translationId] !== $translationKey) {$mismatches[$translationId] = ['found' => $translatedKeys[$translationId],'expected' => $translationKey,];}}return $mismatches;}function printTitle($title){echo $title.\PHP_EOL;echo str_repeat('=', strlen($title)).\PHP_EOL.\PHP_EOL;}function printTable($translations, $verboseOutput, bool $includeCompletedLanguages){if (0 === count($translations)) {echo 'No translations found';return;}$longestLocaleNameLength = max(array_map('strlen', array_keys($translations)));foreach ($translations as $locale => $translation) {if (!$includeCompletedLanguages && $translation['is_completed']) {continue;}if ($translation['translated'] > $translation['total']) {textColorRed();} elseif (count($translation['mismatches']) > 0) {textColorRed();} elseif ($translation['is_completed']) {textColorGreen();}echo sprintf('| Locale: %-'.$longestLocaleNameLength.'s | Translated: %2d/%2d | Mismatches: %d |',$locale,$translation['translated'],$translation['total'],count($translation['mismatches'])).\PHP_EOL;textColorNormal();$shouldBeClosed = false;if (true === $verboseOutput && count($translation['missingKeys']) > 0) {echo '| Missing Translations:'.\PHP_EOL;foreach ($translation['missingKeys'] as $id => $content) {echo sprintf('| (id=%s) %s', $id, $content).\PHP_EOL;}$shouldBeClosed = true;}if (true === $verboseOutput && count($translation['mismatches']) > 0) {echo '| Mismatches between trans-unit id and source:'.\PHP_EOL;foreach ($translation['mismatches'] as $id => $content) {echo sprintf('| (id=%s) Expected: %s', $id, $content['expected']).\PHP_EOL;echo sprintf('| Found: %s', $content['found']).\PHP_EOL;}$shouldBeClosed = true;}if ($shouldBeClosed) {echo str_repeat('-', 80).\PHP_EOL;}}}function textColorGreen(){echo "\033[32m";}function textColorRed(){echo "\033[31m";}function textColorNormal(){echo "\033[0m";}