From 2b096c1af13fea5dbf2ec176b8e046e191cee2ca Mon Sep 17 00:00:00 2001 From: Nic Wortel Date: Fri, 7 Feb 2020 15:09:16 +0100 Subject: [PATCH 1/5] Introduce a translation:check command, to check the current translation status --- Command/CheckCommand.php | 118 ++++++++++++++++++ Resources/config/console.yaml | 10 ++ Tests/Functional/Command/CheckCommandTest.php | 90 +++++++++++++ 3 files changed, 218 insertions(+) create mode 100644 Command/CheckCommand.php create mode 100644 Tests/Functional/Command/CheckCommandTest.php diff --git a/Command/CheckCommand.php b/Command/CheckCommand.php new file mode 100644 index 00000000..33a1630c --- /dev/null +++ b/Command/CheckCommand.php @@ -0,0 +1,118 @@ +configurationManager = $configurationManager; + $this->catalogueFetcher = $catalogueFetcher; + $this->importer = $importer; + $this->catalogueCounter = $catalogueCounter; + } + + protected function configure(): void + { + $this + ->setName(self::$defaultName) + ->setDescription('Check that all translations for a given locale are extracted.') + ->addArgument('locale', InputArgument::REQUIRED, 'The locale to check') + ->addArgument('configuration', InputArgument::OPTIONAL, 'The configuration to use', 'default') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $config = $this->configurationManager->getConfiguration($input->getArgument('configuration')); + + $locale = $input->getArgument('locale'); + + $catalogues = $this->catalogueFetcher->getCatalogues($config, [$locale]); + $finder = $this->getConfiguredFinder($config); + + $result = $this->importer->extractToCatalogues( + $finder, + $catalogues, + [ + 'blacklist_domains' => $config->getBlacklistDomains(), + 'whitelist_domains' => $config->getWhitelistDomains(), + 'project_root' => $config->getProjectRoot(), + ] + ); + + $definedBefore = $this->catalogueCounter->getNumberOfDefinedMessages($catalogues[0]); + $definedAfter = $this->catalogueCounter->getNumberOfDefinedMessages($result->getMessageCatalogues()[0]); + + $newMessages = $definedAfter - $definedBefore; + + $io = new SymfonyStyle($input, $output); + + if ($newMessages > 0) { + $io->error(sprintf('%d new message(s) have been found, run bin/console translation:extract', $newMessages)); + + return 1; + } + + $io->success('No new translation messages'); + + return 0; + } + + private function getConfiguredFinder(Configuration $config): Finder + { + $finder = new Finder(); + $finder->in($config->getDirs()); + + foreach ($config->getExcludedDirs() as $exclude) { + $finder->notPath($exclude); + } + + foreach ($config->getExcludedNames() as $exclude) { + $finder->notName($exclude); + } + + return $finder; + } +} diff --git a/Resources/config/console.yaml b/Resources/config/console.yaml index f9eeb2a9..ed79c994 100644 --- a/Resources/config/console.yaml +++ b/Resources/config/console.yaml @@ -1,4 +1,14 @@ services: + Translation\Bundle\Command\CheckCommand: + public: true + arguments: + - '@Translation\Bundle\Service\ConfigurationManager' + - '@Translation\Bundle\Catalogue\CatalogueFetcher' + - '@Translation\Bundle\Service\Importer' + - '@Translation\Bundle\Catalogue\CatalogueCounter' + tags: + - { name: console.command, command: translation:check } + Translation\Bundle\Command\DeleteObsoleteCommand: public: true arguments: diff --git a/Tests/Functional/Command/CheckCommandTest.php b/Tests/Functional/Command/CheckCommandTest.php new file mode 100644 index 00000000..1af62b43 --- /dev/null +++ b/Tests/Functional/Command/CheckCommandTest.php @@ -0,0 +1,90 @@ +kernel->addConfigFile(__DIR__.'/../app/config/normal_config.yaml'); + $this->bootKernel(); + $this->application = new Application($this->kernel); + + \file_put_contents(__DIR__.'/../app/Resources/translations/messages.sv.xlf', <<<'XML' + + + + + + translated.heading + My translated heading + + + + + translated.paragraph0 + My translated paragraph0 + + + + + foobar.html.twig:9 + + + translated.paragraph1 + My translated paragraph1 + + + + + not.in.source + This is not in the source code + + + + +XML + ); + } + + public function testReportsMissingTranslations(): void + { + $commandTester = new CommandTester($this->application->find('translation:check')); + + $commandTester->execute(['locale' => 'sv', 'configuration' => 'app']); + + $this->assertStringContainsString( + '4 new message(s) have been found, run bin/console translation:extract', + $commandTester->getDisplay() + ); + $this->assertGreaterThan(0, $commandTester->getStatusCode()); + } + + public function testReportsNoNewTranslationMessages(): void + { + // run translation:extract first, so all translations are extracted + (new CommandTester($this->application->find('translation:extract')))->execute(['locale' => 'sv']); + + $commandTester = new CommandTester($this->application->find('translation:check')); + + $commandTester->execute(['locale' => 'sv', 'configuration' => 'app']); + + $this->assertStringContainsString( + 'No new translation messages', + $commandTester->getDisplay() + ); + $this->assertSame(0, $commandTester->getStatusCode()); + } +} From 360b332c8f28a4c87ed1e8b039b1151fa211fedd Mon Sep 17 00:00:00 2001 From: Nic Wortel Date: Fri, 7 Feb 2020 15:21:46 +0100 Subject: [PATCH 2/5] Also check for empty translation messages --- Command/CheckCommand.php | 34 +++++- Tests/Functional/Command/CheckCommandTest.php | 110 +++++++++++++++++- 2 files changed, 139 insertions(+), 5 deletions(-) diff --git a/Command/CheckCommand.php b/Command/CheckCommand.php index 33a1630c..c04b4de3 100644 --- a/Command/CheckCommand.php +++ b/Command/CheckCommand.php @@ -9,11 +9,14 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Finder\Finder; +use Symfony\Component\Translation\MessageCatalogueInterface; use Translation\Bundle\Catalogue\CatalogueCounter; use Translation\Bundle\Catalogue\CatalogueFetcher; use Translation\Bundle\Model\Configuration; use Translation\Bundle\Service\ConfigurationManager; use Translation\Bundle\Service\Importer; +use function array_filter; +use function count; final class CheckCommand extends Command { @@ -59,8 +62,7 @@ protected function configure(): void ->setName(self::$defaultName) ->setDescription('Check that all translations for a given locale are extracted.') ->addArgument('locale', InputArgument::REQUIRED, 'The locale to check') - ->addArgument('configuration', InputArgument::OPTIONAL, 'The configuration to use', 'default') - ; + ->addArgument('configuration', InputArgument::OPTIONAL, 'The configuration to use', 'default'); } protected function execute(InputInterface $input, OutputInterface $output): int @@ -95,6 +97,16 @@ protected function execute(InputInterface $input, OutputInterface $output): int return 1; } + $emptyTranslations = $this->countEmptyTranslations($result->getMessageCatalogues()[0]); + + if ($emptyTranslations > 0) { + $io->error( + sprintf('%d messages have empty translations, please provide translations for them', $emptyTranslations) + ); + + return 1; + } + $io->success('No new translation messages'); return 0; @@ -115,4 +127,22 @@ private function getConfiguredFinder(Configuration $config): Finder return $finder; } + + private function countEmptyTranslations(MessageCatalogueInterface $catalogue): int + { + $total = 0; + + foreach ($catalogue->getDomains() as $domain) { + $emptyTranslations = array_filter( + $catalogue->all($domain), + function (string $message): bool { + return $message === ''; + } + ); + + $total += count($emptyTranslations); + } + + return $total; + } } diff --git a/Tests/Functional/Command/CheckCommandTest.php b/Tests/Functional/Command/CheckCommandTest.php index 1af62b43..c5074baf 100644 --- a/Tests/Functional/Command/CheckCommandTest.php +++ b/Tests/Functional/Command/CheckCommandTest.php @@ -6,6 +6,7 @@ use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Component\Console\Tester\CommandTester; use Translation\Bundle\Tests\Functional\BaseTestCase; +use function file_put_contents; class CheckCommandTest extends BaseTestCase { @@ -18,11 +19,13 @@ protected function setUp(): void { parent::setUp(); - $this->kernel->addConfigFile(__DIR__.'/../app/config/normal_config.yaml'); + $this->kernel->addConfigFile(__DIR__ . '/../app/config/normal_config.yaml'); $this->bootKernel(); $this->application = new Application($this->kernel); - \file_put_contents(__DIR__.'/../app/Resources/translations/messages.sv.xlf', <<<'XML' + file_put_contents( + __DIR__ . '/../app/Resources/translations/messages.sv.xlf', + <<<'XML' @@ -72,7 +75,7 @@ public function testReportsMissingTranslations(): void $this->assertGreaterThan(0, $commandTester->getStatusCode()); } - public function testReportsNoNewTranslationMessages(): void + public function testReportsEmptyTranslationMessages(): void { // run translation:extract first, so all translations are extracted (new CommandTester($this->application->find('translation:extract')))->execute(['locale' => 'sv']); @@ -81,6 +84,107 @@ public function testReportsNoNewTranslationMessages(): void $commandTester->execute(['locale' => 'sv', 'configuration' => 'app']); + $this->assertStringContainsString( + '4 messages have empty translations, please provide translations for them', + $commandTester->getDisplay() + ); + $this->assertGreaterThan(0, $commandTester->getStatusCode()); + } + + public function testReportsNoNewTranslationMessages(): void + { + file_put_contents( + __DIR__ . '/../app/Resources/translations/messages.sv.xlf', + <<<'XML' + + + + + + Resources/views/translated.html.twig:5 + new + + + translated.title + My translated title + + + + + Resources/views/translated.html.twig:8 + + + translated.heading + My translated heading + + + + + Resources/views/translated.html.twig:9 + + + translated.paragraph0 + My translated paragraph0 + + + + + Resources/views/translated.html.twig:9 + + + translated.paragraph1 + My translated paragraph1 + + + + + Resources/views/translated.html.twig:11 + new + + + translated.paragraph2 + My translated paragraph2 + + + + + Resources/views/translated.html.twig:12 + Resources/views/translated.html.twig:12 + new + + + localized.email + My localized email + + + + + Resources/views/translated.html.twig:14 + new + + + translated.attribute + My translated attribute + + + + + obsolete + + + not.in.source + This is not in the source code + + + + +XML + ); + + $commandTester = new CommandTester($this->application->find('translation:check')); + + $commandTester->execute(['locale' => 'sv', 'configuration' => 'app']); + $this->assertStringContainsString( 'No new translation messages', $commandTester->getDisplay() From 59bc570a7b4b5bfe619306056de4cb4fbef8a5f6 Mon Sep 17 00:00:00 2001 From: Nic Wortel Date: Fri, 7 Feb 2020 15:28:56 +0100 Subject: [PATCH 3/5] Fix code style issues --- Command/CheckCommand.php | 13 ++++++------- Tests/Functional/Command/CheckCommandTest.php | 12 ++++++------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/Command/CheckCommand.php b/Command/CheckCommand.php index c04b4de3..40c0c0d9 100644 --- a/Command/CheckCommand.php +++ b/Command/CheckCommand.php @@ -1,4 +1,5 @@ 0) { - $io->error(sprintf('%d new message(s) have been found, run bin/console translation:extract', $newMessages)); + $io->error(\sprintf('%d new message(s) have been found, run bin/console translation:extract', $newMessages)); return 1; } @@ -101,7 +100,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ($emptyTranslations > 0) { $io->error( - sprintf('%d messages have empty translations, please provide translations for them', $emptyTranslations) + \sprintf('%d messages have empty translations, please provide translations for them', $emptyTranslations) ); return 1; @@ -133,14 +132,14 @@ private function countEmptyTranslations(MessageCatalogueInterface $catalogue): i $total = 0; foreach ($catalogue->getDomains() as $domain) { - $emptyTranslations = array_filter( + $emptyTranslations = \array_filter( $catalogue->all($domain), function (string $message): bool { - return $message === ''; + return '' === $message; } ); - $total += count($emptyTranslations); + $total += \count($emptyTranslations); } return $total; diff --git a/Tests/Functional/Command/CheckCommandTest.php b/Tests/Functional/Command/CheckCommandTest.php index c5074baf..cd41f61b 100644 --- a/Tests/Functional/Command/CheckCommandTest.php +++ b/Tests/Functional/Command/CheckCommandTest.php @@ -1,4 +1,5 @@ kernel->addConfigFile(__DIR__ . '/../app/config/normal_config.yaml'); + $this->kernel->addConfigFile(__DIR__.'/../app/config/normal_config.yaml'); $this->bootKernel(); $this->application = new Application($this->kernel); - file_put_contents( - __DIR__ . '/../app/Resources/translations/messages.sv.xlf', + \file_put_contents( + __DIR__.'/../app/Resources/translations/messages.sv.xlf', <<<'XML' @@ -93,8 +93,8 @@ public function testReportsEmptyTranslationMessages(): void public function testReportsNoNewTranslationMessages(): void { - file_put_contents( - __DIR__ . '/../app/Resources/translations/messages.sv.xlf', + \file_put_contents( + __DIR__.'/../app/Resources/translations/messages.sv.xlf', <<<'XML' From aed52a4c8824d229574c469ef5d97ca84c88a184 Mon Sep 17 00:00:00 2001 From: Nic Wortel Date: Fri, 7 Feb 2020 15:36:24 +0100 Subject: [PATCH 4/5] Search for a shorter string in the test This prevents issues in CI with a smaller console width. --- Tests/Functional/Command/CheckCommandTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Functional/Command/CheckCommandTest.php b/Tests/Functional/Command/CheckCommandTest.php index cd41f61b..897b5a61 100644 --- a/Tests/Functional/Command/CheckCommandTest.php +++ b/Tests/Functional/Command/CheckCommandTest.php @@ -85,7 +85,7 @@ public function testReportsEmptyTranslationMessages(): void $commandTester->execute(['locale' => 'sv', 'configuration' => 'app']); $this->assertStringContainsString( - '4 messages have empty translations, please provide translations for them', + '4 messages have empty translations, please provide translations', $commandTester->getDisplay() ); $this->assertGreaterThan(0, $commandTester->getStatusCode()); From f6609f1f1b428612897575cc37a832db6b00b7d5 Mon Sep 17 00:00:00 2001 From: Nic Wortel Date: Tue, 18 Feb 2020 13:26:45 +0100 Subject: [PATCH 5/5] Rename translation:check to translation:check-missing --- Command/{CheckCommand.php => CheckMissingCommand.php} | 4 ++-- Resources/config/console.yaml | 4 ++-- .../{CheckCommandTest.php => CheckMissingCommandTest.php} | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) rename Command/{CheckCommand.php => CheckMissingCommand.php} (97%) rename Tests/Functional/Command/{CheckCommandTest.php => CheckMissingCommandTest.php} (97%) diff --git a/Command/CheckCommand.php b/Command/CheckMissingCommand.php similarity index 97% rename from Command/CheckCommand.php rename to Command/CheckMissingCommand.php index 40c0c0d9..e0162f19 100644 --- a/Command/CheckCommand.php +++ b/Command/CheckMissingCommand.php @@ -17,9 +17,9 @@ use Translation\Bundle\Service\ConfigurationManager; use Translation\Bundle\Service\Importer; -final class CheckCommand extends Command +final class CheckMissingCommand extends Command { - protected static $defaultName = 'translation:check'; + protected static $defaultName = 'translation:check-missing'; /** * @var ConfigurationManager diff --git a/Resources/config/console.yaml b/Resources/config/console.yaml index ed79c994..e2daeaf4 100644 --- a/Resources/config/console.yaml +++ b/Resources/config/console.yaml @@ -1,5 +1,5 @@ services: - Translation\Bundle\Command\CheckCommand: + Translation\Bundle\Command\CheckMissingCommand: public: true arguments: - '@Translation\Bundle\Service\ConfigurationManager' @@ -7,7 +7,7 @@ services: - '@Translation\Bundle\Service\Importer' - '@Translation\Bundle\Catalogue\CatalogueCounter' tags: - - { name: console.command, command: translation:check } + - { name: console.command, command: translation:check-missing } Translation\Bundle\Command\DeleteObsoleteCommand: public: true diff --git a/Tests/Functional/Command/CheckCommandTest.php b/Tests/Functional/Command/CheckMissingCommandTest.php similarity index 97% rename from Tests/Functional/Command/CheckCommandTest.php rename to Tests/Functional/Command/CheckMissingCommandTest.php index 897b5a61..f64dfbc9 100644 --- a/Tests/Functional/Command/CheckCommandTest.php +++ b/Tests/Functional/Command/CheckMissingCommandTest.php @@ -8,7 +8,7 @@ use Symfony\Component\Console\Tester\CommandTester; use Translation\Bundle\Tests\Functional\BaseTestCase; -class CheckCommandTest extends BaseTestCase +class CheckMissingCommandTest extends BaseTestCase { /** * @var Application @@ -64,7 +64,7 @@ protected function setUp(): void public function testReportsMissingTranslations(): void { - $commandTester = new CommandTester($this->application->find('translation:check')); + $commandTester = new CommandTester($this->application->find('translation:check-missing')); $commandTester->execute(['locale' => 'sv', 'configuration' => 'app']); @@ -80,7 +80,7 @@ public function testReportsEmptyTranslationMessages(): void // run translation:extract first, so all translations are extracted (new CommandTester($this->application->find('translation:extract')))->execute(['locale' => 'sv']); - $commandTester = new CommandTester($this->application->find('translation:check')); + $commandTester = new CommandTester($this->application->find('translation:check-missing')); $commandTester->execute(['locale' => 'sv', 'configuration' => 'app']); @@ -181,7 +181,7 @@ public function testReportsNoNewTranslationMessages(): void XML ); - $commandTester = new CommandTester($this->application->find('translation:check')); + $commandTester = new CommandTester($this->application->find('translation:check-missing')); $commandTester->execute(['locale' => 'sv', 'configuration' => 'app']);