From 41f13e29e093e30c9e0f2831b4141475844d944f Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Fri, 20 Sep 2024 12:16:31 +0200 Subject: [PATCH] Expand detection of duplicates in translations command Added functionality to specify key namespaces that should be exluded from duplicate detection Added functionality to check the accidental creation of duplicates based on an md5 hash of translation duplicates. --- .../DetectTranslationDuplicatesCommand.php | 79 +++++++++++++++---- 1 file changed, 65 insertions(+), 14 deletions(-) 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(); + } }