Add support for translator paths and twig paths in translation commands

This commit is contained in:
Yonel Ceruto 2018-11-07 06:59:44 -05:00
parent 5ed68eec7e
commit 31d7a09bf5
16 changed files with 171 additions and 21 deletions

View File

@ -15,6 +15,7 @@ CHANGELOG
* Added information about deprecated aliases in `debug:autowiring`
* Added php ini session options `sid_length` and `sid_bits_per_character`
to the `session` section of the configuration
* Added support for Translator paths, Twig paths in translation commands.
4.2.0
-----

View File

@ -50,11 +50,13 @@ class TranslationDebugCommand extends Command
private $extractor;
private $defaultTransPath;
private $defaultViewsPath;
private $transPaths;
private $viewsPaths;
/**
* @param TranslatorInterface $translator
*/
public function __construct($translator, TranslationReaderInterface $reader, ExtractorInterface $extractor, string $defaultTransPath = null, string $defaultViewsPath = null)
public function __construct($translator, TranslationReaderInterface $reader, ExtractorInterface $extractor, string $defaultTransPath = null, string $defaultViewsPath = null, array $transPaths = [], array $viewsPaths = [])
{
if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) {
throw new \TypeError(sprintf('Argument 1 passed to %s() must be an instance of %s, %s given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator)));
@ -66,6 +68,8 @@ class TranslationDebugCommand extends Command
$this->extractor = $extractor;
$this->defaultTransPath = $defaultTransPath;
$this->defaultViewsPath = $defaultViewsPath;
$this->transPaths = $transPaths;
$this->viewsPaths = $viewsPaths;
}
/**
@ -131,7 +135,7 @@ EOF
$rootDir = $kernel->getContainer()->getParameter('kernel.root_dir');
// Define Root Paths
$transPaths = [];
$transPaths = $this->transPaths;
if (is_dir($dir = $rootDir.'/Resources/translations')) {
if ($dir !== $this->defaultTransPath) {
$notice = sprintf('Storing translations in the "%s" directory is deprecated since Symfony 4.2, ', $dir);
@ -142,7 +146,7 @@ EOF
if ($this->defaultTransPath) {
$transPaths[] = $this->defaultTransPath;
}
$viewsPaths = [];
$viewsPaths = $this->viewsPaths;
if (is_dir($dir = $rootDir.'/Resources/views')) {
if ($dir !== $this->defaultViewsPath) {
$notice = sprintf('Storing templates in the "%s" directory is deprecated since Symfony 4.2, ', $dir);

View File

@ -44,8 +44,10 @@ class TranslationUpdateCommand extends Command
private $defaultLocale;
private $defaultTransPath;
private $defaultViewsPath;
private $transPaths;
private $viewsPaths;
public function __construct(TranslationWriterInterface $writer, TranslationReaderInterface $reader, ExtractorInterface $extractor, string $defaultLocale, string $defaultTransPath = null, string $defaultViewsPath = null)
public function __construct(TranslationWriterInterface $writer, TranslationReaderInterface $reader, ExtractorInterface $extractor, string $defaultLocale, string $defaultTransPath = null, string $defaultViewsPath = null, array $transPaths = [], array $viewsPaths = [])
{
parent::__construct();
@ -55,6 +57,8 @@ class TranslationUpdateCommand extends Command
$this->defaultLocale = $defaultLocale;
$this->defaultTransPath = $defaultTransPath;
$this->defaultViewsPath = $defaultViewsPath;
$this->transPaths = $transPaths;
$this->viewsPaths = $viewsPaths;
}
/**
@ -122,7 +126,7 @@ EOF
$rootDir = $kernel->getContainer()->getParameter('kernel.root_dir');
// Define Root Paths
$transPaths = [];
$transPaths = $this->transPaths;
if (is_dir($dir = $rootDir.'/Resources/translations')) {
if ($dir !== $this->defaultTransPath) {
$notice = sprintf('Storing translations in the "%s" directory is deprecated since Symfony 4.2, ', $dir);
@ -133,7 +137,7 @@ EOF
if ($this->defaultTransPath) {
$transPaths[] = $this->defaultTransPath;
}
$viewsPaths = [];
$viewsPaths = $this->viewsPaths;
if (is_dir($dir = $rootDir.'/Resources/views')) {
if ($dir !== $this->defaultViewsPath) {
$notice = sprintf('Storing templates in the "%s" directory is deprecated since Symfony 4.2, ', $dir);

View File

@ -1023,20 +1023,21 @@ class FrameworkExtension extends Extension
// Discover translation directories
$dirs = [];
$transPaths = [];
if (class_exists('Symfony\Component\Validator\Validation')) {
$r = new \ReflectionClass('Symfony\Component\Validator\Validation');
$dirs[] = \dirname($r->getFileName()).'/Resources/translations';
$dirs[] = $transPaths[] = \dirname($r->getFileName()).'/Resources/translations';
}
if (class_exists('Symfony\Component\Form\Form')) {
$r = new \ReflectionClass('Symfony\Component\Form\Form');
$dirs[] = \dirname($r->getFileName()).'/Resources/translations';
$dirs[] = $transPaths[] = \dirname($r->getFileName()).'/Resources/translations';
}
if (class_exists('Symfony\Component\Security\Core\Exception\AuthenticationException')) {
$r = new \ReflectionClass('Symfony\Component\Security\Core\Exception\AuthenticationException');
$dirs[] = \dirname(\dirname($r->getFileName())).'/Resources/translations';
$dirs[] = $transPaths[] = \dirname(\dirname($r->getFileName())).'/Resources/translations';
}
$defaultDir = $container->getParameterBag()->resolveValue($config['default_path']);
$rootDir = $container->getParameter('kernel.root_dir');
@ -1053,11 +1054,13 @@ class FrameworkExtension extends Extension
foreach ($config['paths'] as $dir) {
if ($container->fileExists($dir)) {
$dirs[] = $dir;
$dirs[] = $transPaths[] = $dir;
} else {
throw new \UnexpectedValueException(sprintf('%s defined in translator.paths does not exist or is not a directory', $dir));
}
}
$container->getDefinition('console.command.translation_debug')->replaceArgument(5, $transPaths);
$container->getDefinition('console.command.translation_update')->replaceArgument(6, $transPaths);
if ($container->fileExists($defaultDir)) {
$dirs[] = $defaultDir;

View File

@ -105,7 +105,9 @@ class FrameworkBundle extends Bundle
$container->addCompilerPass(new AddAnnotationsCachedReaderPass(), PassConfig::TYPE_AFTER_REMOVING, -255);
$this->addCompilerPassIfExists($container, AddValidatorInitializersPass::class);
$this->addCompilerPassIfExists($container, AddConsoleCommandPass::class, PassConfig::TYPE_BEFORE_REMOVING);
$this->addCompilerPassIfExists($container, TranslatorPass::class);
// must be registered as late as possible to get access to all Twig paths registered in
// twig.template_iterator definition
$this->addCompilerPassIfExists($container, TranslatorPass::class, PassConfig::TYPE_BEFORE_OPTIMIZATION, -32);
$container->addCompilerPass(new LoggingTranslatorPass());
$container->addCompilerPass(new AddExpressionLanguageProvidersPass(false));
$this->addCompilerPassIfExists($container, TranslationExtractorPass::class);

View File

@ -101,6 +101,8 @@
<argument type="service" id="translation.extractor" />
<argument>%translator.default_path%</argument>
<argument /> <!-- %twig.default_path% -->
<argument type="collection" /> <!-- Translator paths -->
<argument type="collection" /> <!-- Twig paths -->
<tag name="console.command" command="debug:translation" />
</service>
@ -111,6 +113,8 @@
<argument>%kernel.default_locale%</argument>
<argument>%translator.default_path%</argument>
<argument /> <!-- %twig.default_path% -->
<argument type="collection" /> <!-- Translator paths -->
<argument type="collection" /> <!-- Twig paths -->
<tag name="console.command" command="translation:update" />
</service>

View File

@ -90,7 +90,7 @@ class TranslationDebugCommandTest extends TestCase
$this->fs->mkdir($this->translationDir.'/translations');
$this->fs->mkdir($this->translationDir.'/templates');
$tester = $this->createCommandTester(['foo' => 'foo'], ['bar' => 'bar']);
$tester = $this->createCommandTester(['foo' => 'foo'], ['bar' => 'bar'], null, [$this->translationDir.'/trans'], [$this->translationDir.'/views']);
$tester->execute(['locale' => 'en']);
$this->assertRegExp('/missing/', $tester->getDisplay());
@ -145,7 +145,7 @@ class TranslationDebugCommandTest extends TestCase
/**
* @return CommandTester
*/
private function createCommandTester($extractedMessages = [], $loadedMessages = [], $kernel = null)
private function createCommandTester($extractedMessages = [], $loadedMessages = [], $kernel = null, array $transPaths = [], array $viewsPaths = [])
{
$translator = $this->getMockBuilder('Symfony\Component\Translation\Translator')
->disableOriginalConstructor()
@ -207,7 +207,7 @@ class TranslationDebugCommandTest extends TestCase
->method('getContainer')
->will($this->returnValue($container));
$command = new TranslationDebugCommand($translator, $loader, $extractor, $this->translationDir.'/translations', $this->translationDir.'/templates');
$command = new TranslationDebugCommand($translator, $loader, $extractor, $this->translationDir.'/translations', $this->translationDir.'/templates', $transPaths, $viewsPaths);
$application = new Application($kernel);
$application->add($command);

View File

@ -39,7 +39,7 @@ class TranslationUpdateCommandTest extends TestCase
$this->fs->mkdir($this->translationDir.'/translations');
$this->fs->mkdir($this->translationDir.'/templates');
$tester = $this->createCommandTester(['messages' => ['foo' => 'foo']]);
$tester = $this->createCommandTester(['messages' => ['foo' => 'foo']], [], null, [$this->translationDir.'/trans'], [$this->translationDir.'/views']);
$tester->execute(['command' => 'translation:update', 'locale' => 'en', '--dump-messages' => true, '--clean' => true]);
$this->assertRegExp('/foo/', $tester->getDisplay());
$this->assertRegExp('/1 message was successfully extracted/', $tester->getDisplay());
@ -121,7 +121,7 @@ class TranslationUpdateCommandTest extends TestCase
/**
* @return CommandTester
*/
private function createCommandTester($extractedMessages = [], $loadedMessages = [], HttpKernel\KernelInterface $kernel = null)
private function createCommandTester($extractedMessages = [], $loadedMessages = [], HttpKernel\KernelInterface $kernel = null, array $transPaths = [], array $viewsPaths = [])
{
$translator = $this->getMockBuilder('Symfony\Component\Translation\Translator')
->disableOriginalConstructor()
@ -197,7 +197,7 @@ class TranslationUpdateCommandTest extends TestCase
->method('getContainer')
->will($this->returnValue($container));
$command = new TranslationUpdateCommand($writer, $loader, $extractor, 'en', $this->translationDir.'/translations', $this->translationDir.'/templates');
$command = new TranslationUpdateCommand($writer, $loader, $extractor, 'en', $this->translationDir.'/translations', $this->translationDir.'/templates', $transPaths, $viewsPaths);
$application = new Application($kernel);
$application->add($command);

View File

@ -0,0 +1,21 @@
<?php
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Remove in Symfony 5.0 when the templates directory deprecation is gone.
*/
class TranslationDebugPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if ($container->hasDefinition('console.command.translation_debug')) {
// skipping the /Resources/views path deprecation
$container->getDefinition('console.command.translation_debug')
->setArgument(4, '%kernel.project_dir%/Resources/views');
}
}
}

View File

@ -13,6 +13,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle;
use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection\AnnotationReaderPass;
use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection\Config\CustomConfig;
use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection\TranslationDebugPass;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
@ -29,5 +30,6 @@ class TestBundle extends Bundle
$extension->setCustomConfig(new CustomConfig());
$container->addCompilerPass(new AnnotationReaderPass(), PassConfig::TYPE_AFTER_REMOVING);
$container->addCompilerPass(new TranslationDebugPass());
}
}

View File

@ -0,0 +1,46 @@
<?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.
*/
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
/**
* @group functional
*/
class TranslationDebugCommandTest extends WebTestCase
{
private $application;
protected function setUp()
{
$kernel = static::createKernel(['test_case' => 'TransDebug', 'root_config' => 'config.yml']);
$this->application = new Application($kernel);
}
public function testDumpAllTrans()
{
$tester = $this->createCommandTester();
$ret = $tester->execute(['locale' => 'en']);
$this->assertSame(0, $ret, 'Returns 0 in case of success');
$this->assertContains('unused validators This value should be blank.', $tester->getDisplay());
$this->assertContains('unused security Invalid CSRF token.', $tester->getDisplay());
}
private function createCommandTester(): CommandTester
{
$command = $this->application->find('debug:translation');
return new CommandTester($command);
}
}

View File

@ -0,0 +1,18 @@
<?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.
*/
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle;
return [
new FrameworkBundle(),
new TestBundle(),
];

View File

@ -0,0 +1,13 @@
imports:
- { resource: ../config/default.yml }
framework:
secret: '%secret%'
default_locale: '%env(LOCALE)%'
translator:
fallbacks:
- '%env(LOCALE)%'
parameters:
env(LOCALE): en
secret: test

View File

@ -74,7 +74,7 @@
"symfony/property-info": "<3.4",
"symfony/serializer": "<4.2",
"symfony/stopwatch": "<3.4",
"symfony/translation": "<4.2",
"symfony/translation": "<4.3",
"symfony/twig-bridge": "<4.1.1",
"symfony/validator": "<4.1",
"symfony/workflow": "<4.1"

View File

@ -68,12 +68,16 @@ class TranslatorPass implements CompilerPassInterface
return;
}
$paths = array_keys($container->getDefinition('twig.template_iterator')->getArgument(2));
if ($container->hasDefinition($this->debugCommandServiceId)) {
$container->getDefinition($this->debugCommandServiceId)->replaceArgument(4, $container->getParameter('twig.default_path'));
$definition = $container->getDefinition($this->debugCommandServiceId);
$definition->replaceArgument(4, $container->getParameter('twig.default_path'));
$definition->replaceArgument(6, $paths);
}
if ($container->hasDefinition($this->updateCommandServiceId)) {
$container->getDefinition($this->updateCommandServiceId)->replaceArgument(5, $container->getParameter('twig.default_path'));
$definition = $container->getDefinition($this->updateCommandServiceId);
$definition->replaceArgument(5, $container->getParameter('twig.default_path'));
$definition->replaceArgument(7, $paths);
}
}
}

View File

@ -54,4 +54,32 @@ class TranslationPassTest extends TestCase
$expected = ['translation.xliff_loader' => new ServiceClosureArgument(new Reference('translation.xliff_loader'))];
$this->assertEquals($expected, $container->getDefinition((string) $translator->getArgument(0))->getArgument(0));
}
public function testValidCommandsViewPathsArgument()
{
$container = new ContainerBuilder();
$container->register('translator.default')
->setArguments([null, null, null, null])
;
$debugCommand = $container->register('console.command.translation_debug')
->setArguments([null, null, null, null, null, [], []])
;
$updateCommand = $container->register('console.command.translation_update')
->setArguments([null, null, null, null, null, null, [], []])
;
$container->register('twig.template_iterator')
->setArguments([null, null, ['other/templates' => null, 'tpl' => 'App']])
;
$container->setParameter('twig.default_path', 'templates');
$pass = new TranslatorPass('translator.default');
$pass->process($container);
$expectedViewPaths = ['other/templates', 'tpl'];
$this->assertSame('templates', $debugCommand->getArgument(4));
$this->assertSame('templates', $updateCommand->getArgument(5));
$this->assertSame($expectedViewPaths, $debugCommand->getArgument(6));
$this->assertSame($expectedViewPaths, $updateCommand->getArgument(7));
}
}