diff --git a/src/Bundle/ChillMainBundle/Command/DetectTranslationDuplicatesCommand.php b/src/Bundle/ChillMainBundle/Command/DetectTranslationDuplicatesCommand.php index 16e0c0b24..a2038a309 100644 --- a/src/Bundle/ChillMainBundle/Command/DetectTranslationDuplicatesCommand.php +++ b/src/Bundle/ChillMainBundle/Command/DetectTranslationDuplicatesCommand.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\MainBundle\Command; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -19,6 +20,7 @@ use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Component\Console\Helper\Table; + class DetectTranslationDuplicatesCommand extends Command { protected static $defaultName = 'app:detect-duplicate-translations'; @@ -32,12 +34,16 @@ class DetectTranslationDuplicatesCommand extends Command { $this ->setDescription('Detects duplicate translations in YAML files.') - ->addOption('locale', null, InputOption::VALUE_REQUIRED, 'Locale to check for duplicate translations', 'en'); + ->addOption('locale', null, InputOption::VALUE_REQUIRED, 'Locale to check for duplicate translations', 'en') + ->addOption('exclude-namespaces', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Namespaces to exclude from duplicate detection', []) + ->addArgument('verify-hash', InputArgument::OPTIONAL, 'The expected hash to verify translation integrity'); } protected function execute(InputInterface $input, OutputInterface $output): int { $locale = $input->getOption('locale'); + $excludedNamespaces = $input->getOption('exclude-namespaces'); + $expectedHash = $input->getArgument('verify-hash'); // Loop through all bundles and get the translation directories foreach ($this->kernel->getBundles() as $bundle) { @@ -58,6 +64,9 @@ class DetectTranslationDuplicatesCommand extends Command // Iterate through each domain in the catalogue foreach ($catalogue->all() as $domain => $translations) { foreach ($translations as $key => $value) { + if ($this->isExcludedNamespace("$domain.$key", $excludedNamespaces)) { + continue; + } if (is_array($value)) { $this->flattenTranslation($value, "$domain.$key", $allTranslations); } else { @@ -74,25 +83,27 @@ class DetectTranslationDuplicatesCommand extends Command return count($keys) > 1; }); - if (empty($duplicates)) { - $output->writeln("No duplicate translations found for locale '$locale'."); - } else { - $output->writeln("Duplicate translations found for locale '$locale':"); + $duplicatesHash = $this->generateDuplicatesHash($duplicates); - // Display the duplicates in a table - $table = new Table($output); - $table->setHeaders(['Translation', 'Used in Keys']); + if ($expectedHash) { + if ($duplicatesHash === $expectedHash) { + $output->writeln('Translations are consistent with the expected hash.'); - foreach ($duplicates as $translation => $keys) { - $wrappedTranslation = $this->wrapText($translation, 40); - $wrappedKeys = $this->wrapText(implode(', ', $keys), 80); + $output->writeln("Current duplicate hash: $duplicatesHash"); + return Command::SUCCESS; + } else { + $output->writeln('Translation hash mismatch! Potential duplicate added.'); + $this->renderDuplicatesTable($output, $duplicates, $locale); - $table->addRow([$wrappedTranslation, $wrappedKeys]); + $output->writeln("Current duplicate hash: $duplicatesHash"); + return Command::FAILURE; } - - $table->render(); } + $this->renderDuplicatesTable($output, $duplicates, $locale); + + $output->writeln("Current duplicate hash: $duplicatesHash"); + return Command::SUCCESS; } @@ -126,4 +137,44 @@ class DetectTranslationDuplicatesCommand extends Command { return wordwrap($text, $width, "\n", true); } + + private function isExcludedNamespace(string $key, array $excludedNamespaces): bool + { + foreach ($excludedNamespaces as $namespace) { + if (str_starts_with($key, $namespace)) { + return true; + } + } + return false; + } + + private function generateDuplicatesHash(array $duplicates): string + { + ksort($duplicates); + foreach ($duplicates as $translation => $keys) { + sort($keys); + } + + return hash('md5', serialize($duplicates)); + } + + private function renderDuplicatesTable(OutputInterface $output, array $duplicates, string $locale): void + { + if (empty($duplicates)) { + $output->writeln("No duplicate translations found for locale '$locale'."); + return; + } + + $output->writeln("Duplicate translations found for locale '$locale':"); + $table = new Table($output); + $table->setHeaders(['Translation', 'Used in Keys']); + + foreach ($duplicates as $translation => $keys) { + $wrappedTranslation = $this->wrapText($translation, 40); + $wrappedKeys = $this->wrapText(implode(', ', $keys), 80); + $table->addRow([$wrappedTranslation, $wrappedKeys]); + } + + $table->render(); + } }