From c05aac33c12e019646c49a947b8aa382700d09fe Mon Sep 17 00:00:00 2001 From: jawira Date: Sun, 6 Oct 2024 10:45:55 +0200 Subject: [PATCH] [FrameworkBundle] Add `--no-fill` option to `translation:extract` command --- .../Bundle/FrameworkBundle/CHANGELOG.md | 1 + .../Command/TranslationUpdateCommand.php | 25 +++++++++--- .../Command/TranslationUpdateCommandTest.php | 40 +++++++++++++++++++ 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index 3d6acb26a1368..3227eddc20e21 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -20,6 +20,7 @@ CHANGELOG * Add the ability to use an existing service as a lock/semaphore resource * Add support for configuring multiple serializer instances via the configuration * Add support for `SYMFONY_TRUSTED_PROXIES`, `SYMFONY_TRUSTED_HEADERS`, `SYMFONY_TRUST_X_SENDFILE_TYPE_HEADER` and `SYMFONY_TRUSTED_HOSTS` env vars + * Add `--no-fill` option to `translation:extract` command 7.1 --- diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php index 04f812146d167..b26d3f9ad20dd 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php @@ -19,7 +19,6 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\HttpKernel\KernelInterface; @@ -49,6 +48,7 @@ class TranslationUpdateCommand extends Command 'xlf12' => ['xlf', '1.2'], 'xlf20' => ['xlf', '2.0'], ]; + private const NO_FILL_PREFIX = "\0NoFill\0"; public function __construct( private TranslationWriterInterface $writer, @@ -71,6 +71,7 @@ protected function configure(): void new InputArgument('locale', InputArgument::REQUIRED, 'The locale'), new InputArgument('bundle', InputArgument::OPTIONAL, 'The bundle name or directory where to load the messages'), new InputOption('prefix', null, InputOption::VALUE_OPTIONAL, 'Override the default prefix', '__'), + new InputOption('no-fill', null, InputOption::VALUE_NONE, 'Extract translation keys without filling in values'), new InputOption('format', null, InputOption::VALUE_OPTIONAL, 'Override the default output format', 'xlf12'), new InputOption('dump-messages', null, InputOption::VALUE_NONE, 'Should the messages be dumped in the console'), new InputOption('force', null, InputOption::VALUE_NONE, 'Should the extract be done'), @@ -85,7 +86,8 @@ protected function configure(): void the new ones into the translation files. When new translation strings are found it can automatically add a prefix to the translation -message. +message. However, if the --no-fill option is used, the --prefix +option has no effect, since the translation values are left empty. Example running against a Bundle (AcmeBundle) @@ -113,9 +115,6 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $io = new SymfonyStyle($input, $output); - $errorIo = $output instanceof ConsoleOutputInterface ? new SymfonyStyle($input, $output->getErrorOutput()) : $io; - $io = new SymfonyStyle($input, $output); $errorIo = $io->getErrorStyle(); @@ -181,7 +180,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io->comment(\sprintf('Generating "%s" translation files for "%s"', $input->getArgument('locale'), $currentName)); $io->comment('Parsing templates...'); - $extractedCatalogue = $this->extractMessages($input->getArgument('locale'), $codePaths, $input->getOption('prefix')); + $prefix = $input->getOption('no-fill') ? self::NO_FILL_PREFIX : $input->getOption('prefix'); + $extractedCatalogue = $this->extractMessages($input->getArgument('locale'), $codePaths, $prefix); $io->comment('Loading translation files...'); $currentCatalogue = $this->loadCurrentMessages($input->getArgument('locale'), $transPaths); @@ -271,6 +271,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int $operationResult = $this->sortCatalogue($operationResult, $sort); } + if (true === $input->getOption('no-fill')) { + $this->removeNoFillTranslations($operationResult); + } + $this->writer->write($operationResult, $format, ['path' => $bundleTransPath, 'default_locale' => $this->defaultLocale, 'xliff_version' => $xliffVersion, 'as_tree' => $input->getOption('as-tree'), 'inline' => $input->getOption('as-tree') ?? 0]); if (true === $input->getOption('dump-messages')) { @@ -485,4 +489,13 @@ private function getRootCodePaths(KernelInterface $kernel): array return $codePaths; } + + private function removeNoFillTranslations(MessageCatalogueInterface $operation): void + { + foreach ($operation->all('messages') as $key => $message) { + if (str_starts_with($message, self::NO_FILL_PREFIX)) { + $operation->set($key, '', 'messages'); + } + } + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php index 2304e75addbef..3a1bd8e3788c8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php @@ -21,6 +21,7 @@ use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Translation\Extractor\ExtractorInterface; use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\MessageCatalogueInterface; use Symfony\Component\Translation\Reader\TranslationReader; use Symfony\Component\Translation\Translator; use Symfony\Component\Translation\Writer\TranslationWriter; @@ -176,6 +177,45 @@ public function testFilterDuplicateTransPaths() $this->assertEquals($expectedPaths, $filteredTransPaths); } + /** + * @dataProvider removeNoFillProvider + */ + public function testRemoveNoFillTranslationsMethod($noFillCounter, $messages) + { + // Preparing mock + $operation = $this->createMock(MessageCatalogueInterface::class); + $operation + ->method('all') + ->with('messages') + ->willReturn($messages); + $operation + ->expects($this->exactly($noFillCounter)) + ->method('set'); + + // Calling private method + $translationUpdate = $this->createMock(TranslationUpdateCommand::class); + $reflection = new \ReflectionObject($translationUpdate); + $method = $reflection->getMethod('removeNoFillTranslations'); + $method->invokeArgs($translationUpdate, [$operation]); + } + + public function removeNoFillProvider(): array + { + return [ + [0, []], + [0, ['foo' => 'foo', 'bar' => 'bar', 'baz' => 'baz']], + [0, ['foo' => "\0foo"]], + [0, ['foo' => "foo\0NoFill\0"]], + [0, ['foo' => "f\0NoFill\000"]], + [0, ['foo' => 'foo', 'bar' => 'bar']], + [1, ['foo' => "\0NoFill\0foo"]], + [1, ['foo' => "\0NoFill\0foo", 'bar' => 'bar']], + [1, ['foo' => 'foo', 'bar' => "\0NoFill\0bar"]], + [2, ['foo' => "\0NoFill\0foo", 'bar' => "\0NoFill\0bar"]], + [3, ['foo' => "\0NoFill\0foo", 'bar' => "\0NoFill\0bar", 'baz' => "\0NoFill\0baz"]], + ]; + } + protected function setUp(): void { $this->fs = new Filesystem();