Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 13418c8

Browse filesBrowse files
committed
feature #10887 [Translation] added LoggingTranslator. (aitboudad)
This PR was squashed before being merged into the 2.6-dev branch (closes #10887). Discussion ---------- [Translation] added LoggingTranslator. | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #3015, #2435 | License | MIT | Doc PR | symfony/symfony-docs/pull/4050 Commits ------- b7770bc [Translation] added LoggingTranslator.
2 parents a7f867f + b7770bc commit 13418c8
Copy full SHA for 13418c8

File tree

Expand file treeCollapse file tree

14 files changed

+385
-7
lines changed
Filter options
Expand file treeCollapse file tree

14 files changed

+385
-7
lines changed
+39Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
13+
14+
use Symfony\Component\DependencyInjection\ContainerBuilder;
15+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
16+
17+
/**
18+
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
19+
*/
20+
class LoggingTranslatorPass implements CompilerPassInterface
21+
{
22+
public function process(ContainerBuilder $container)
23+
{
24+
if (!$container->hasAlias('logger')) {
25+
return;
26+
}
27+
28+
if ($container->getParameter('translator.logging')) {
29+
$translatorAlias = $container->getAlias('translator');
30+
$definition = $container->getDefinition((string) $translatorAlias);
31+
$class = $container->getParameterBag()->resolveValue($definition->getClass());
32+
33+
$refClass = new \ReflectionClass($class);
34+
if ($refClass->implementsInterface('Symfony\Component\Translation\TranslatorInterface') && $refClass->implementsInterface('Symfony\Component\Translation\TranslatorBagInterface')) {
35+
$container->getDefinition('translator.logging')->setDecoratedService('translator');
36+
}
37+
}
38+
}
39+
}

‎src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@
2222
*/
2323
class Configuration implements ConfigurationInterface
2424
{
25+
private $debug;
26+
27+
/**
28+
* @param bool $debug Whether debugging is enabled or not
29+
*/
30+
public function __construct($debug)
31+
{
32+
$this->debug = (bool) $debug;
33+
}
34+
2535
/**
2636
* Generates the configuration tree builder.
2737
*
@@ -441,6 +451,7 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode)
441451
->canBeEnabled()
442452
->children()
443453
->scalarNode('fallback')->defaultValue('en')->end()
454+
->booleanNode('logging')->defaultValue($this->debug)->end()
444455
->end()
445456
->end()
446457
->end()

‎src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,14 @@ public function load(array $configs, ContainerBuilder $container)
169169
));
170170
}
171171

172+
/**
173+
* {@inheritdoc}
174+
*/
175+
public function getConfiguration(array $config, ContainerBuilder $container)
176+
{
177+
return new Configuration($container->getParameter('kernel.debug'));
178+
}
179+
172180
/**
173181
* Loads Form configuration.
174182
*
@@ -627,6 +635,8 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder
627635
}
628636
$translator->addMethodCall('setFallbackLocales', array($config['fallback']));
629637

638+
$container->setParameter('translator.logging', $config['logging']);
639+
630640
// Discover translation directories
631641
$dirs = array();
632642
if (class_exists('Symfony\Component\Validator\Validator')) {

‎src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RoutingResolverPass;
2121
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ProfilerPass;
2222
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslatorPass;
23+
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\LoggingTranslatorPass;
2324
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddCacheWarmerPass;
2425
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddCacheClearerPass;
2526
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ContainerBuilderDebugDumpPass;
@@ -77,6 +78,7 @@ public function build(ContainerBuilder $container)
7778
$container->addCompilerPass(new AddConsoleCommandPass());
7879
$container->addCompilerPass(new FormPass());
7980
$container->addCompilerPass(new TranslatorPass());
81+
$container->addCompilerPass(new LoggingTranslatorPass());
8082
$container->addCompilerPass(new AddCacheWarmerPass());
8183
$container->addCompilerPass(new AddCacheClearerPass());
8284
$container->addCompilerPass(new TranslationExtractorPass());

‎src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
<parameters>
88
<parameter key="translator.class">Symfony\Bundle\FrameworkBundle\Translation\Translator</parameter>
9+
<parameter key="translator.logging.class">Symfony\Component\Translation\LoggingTranslator</parameter>
910
<parameter key="translator.identity.class">Symfony\Component\Translation\IdentityTranslator</parameter>
1011
<parameter key="translator.selector.class">Symfony\Component\Translation\MessageSelector</parameter>
1112
<parameter key="translation.loader.php.class">Symfony\Component\Translation\Loader\PhpFileLoader</parameter>
@@ -46,6 +47,12 @@
4647
</argument>
4748
</service>
4849

50+
<service id="translator.logging" class="%translator.logging.class%" public="false">
51+
<argument type="service" id="translator.logging.inner" />
52+
<argument type="service" id="logger" />
53+
<tag name="monolog.logger" channel="translation" />
54+
</service>
55+
4956
<service id="translator" class="%translator.identity.class%">
5057
<argument type="service" id="translator.selector" />
5158
</service>
+68Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler;
13+
14+
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\LoggingTranslatorPass;
15+
use Symfony\Component\DependencyInjection\ContainerBuilder;
16+
use Symfony\Component\DependencyInjection\Definition;
17+
18+
class LoggingTranslatorPassTest extends \PHPUnit_Framework_TestCase
19+
{
20+
public function testProcess()
21+
{
22+
$definition = $this->getMock('Symfony\Component\DependencyInjection\Definition');
23+
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder');
24+
$parameterBag = $this->getMock('Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface');
25+
26+
$container->expects($this->once())
27+
->method('hasAlias')
28+
->will($this->returnValue(true));
29+
30+
$container->expects($this->once())
31+
->method('getParameter')
32+
->will($this->returnValue(true));
33+
34+
$container->expects($this->once())
35+
->method('getAlias')
36+
->will($this->returnValue('translation.default'));
37+
38+
$container->expects($this->exactly(2))
39+
->method('getDefinition')
40+
->will($this->returnValue($definition));
41+
42+
$definition->expects($this->once())
43+
->method('getClass')
44+
->will($this->returnValue("%translator.class%"));
45+
46+
$parameterBag->expects($this->once())
47+
->method('resolveValue')
48+
->will($this->returnValue("Symfony\Bundle\FrameworkBundle\Translation\Translator"));
49+
50+
$container->expects($this->once())
51+
->method('getParameterBag')
52+
->will($this->returnValue($parameterBag));
53+
54+
$pass = new LoggingTranslatorPass();
55+
$pass->process($container);
56+
}
57+
58+
public function testThatCompilerPassIsIgnoredIfThereIsNotLoggerDefinition()
59+
{
60+
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder');
61+
$container->expects($this->once())
62+
->method('hasAlias')
63+
->will($this->returnValue(false));
64+
65+
$pass = new LoggingTranslatorPass();
66+
$pass->process($container);
67+
}
68+
}

‎src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php
+5-4Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
1919
public function testDefaultConfig()
2020
{
2121
$processor = new Processor();
22-
$config = $processor->processConfiguration(new Configuration(), array(array('secret' => 's3cr3t')));
22+
$config = $processor->processConfiguration(new Configuration(true), array(array('secret' => 's3cr3t')));
2323

2424
$this->assertEquals(
2525
array_merge(array('secret' => 's3cr3t', 'trusted_hosts' => array()), self::getBundleDefaultConfig()),
@@ -33,7 +33,7 @@ public function testDefaultConfig()
3333
public function testValidTrustedProxies($trustedProxies, $processedProxies)
3434
{
3535
$processor = new Processor();
36-
$configuration = new Configuration();
36+
$configuration = new Configuration(true);
3737
$config = $processor->processConfiguration($configuration, array(array(
3838
'secret' => 's3cr3t',
3939
'trusted_proxies' => $trustedProxies,
@@ -62,7 +62,7 @@ public function getTestValidTrustedProxiesData()
6262
public function testInvalidTypeTrustedProxies()
6363
{
6464
$processor = new Processor();
65-
$configuration = new Configuration();
65+
$configuration = new Configuration(true);
6666
$processor->processConfiguration($configuration, array(
6767
array(
6868
'secret' => 's3cr3t',
@@ -77,7 +77,7 @@ public function testInvalidTypeTrustedProxies()
7777
public function testInvalidValueTrustedProxies()
7878
{
7979
$processor = new Processor();
80-
$configuration = new Configuration();
80+
$configuration = new Configuration(true);
8181
$processor->processConfiguration($configuration, array(
8282
array(
8383
'secret' => 's3cr3t',
@@ -123,6 +123,7 @@ protected static function getBundleDefaultConfig()
123123
'translator' => array(
124124
'enabled' => false,
125125
'fallback' => 'en',
126+
'logging' => true,
126127
),
127128
'validation' => array(
128129
'enabled' => false,

‎src/Symfony/Component/Translation/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/Translation/CHANGELOG.md
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ CHANGELOG
55
-----
66

77
* added possibility to cache catalogues
8+
* added TranslatorBagInterface
9+
* added LoggingTranslator
810

911
2.5.0
1012
-----
+124Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Translation;
13+
14+
use Psr\Log\LoggerInterface;
15+
16+
/**
17+
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
18+
*/
19+
class LoggingTranslator implements TranslatorInterface
20+
{
21+
/**
22+
* @var TranslatorInterface
23+
*/
24+
private $translator;
25+
26+
/**
27+
* @var LoggerInterface
28+
*/
29+
private $logger;
30+
31+
/**
32+
* @param Translator $translator
33+
* @param LoggerInterface $logger
34+
*/
35+
public function __construct($translator, LoggerInterface $logger)
36+
{
37+
if (!($translator instanceof TranslatorInterface && $translator instanceof TranslatorBagInterface)) {
38+
throw new \InvalidArgumentException(sprintf('The Translator "%s" must implements TranslatorInterface and TranslatorBagInterface.', get_class($translator)));
39+
}
40+
41+
$this->translator = $translator;
42+
$this->logger = $logger;
43+
}
44+
45+
/**
46+
* {@inheritdoc}
47+
*/
48+
public function trans($id, array $parameters = array(), $domain = null, $locale = null)
49+
{
50+
$trans = $this->translator->trans($id, $parameters , $domain , $locale);
51+
$this->log($id, $domain, $locale);
52+
53+
return $trans;
54+
}
55+
56+
/**
57+
* {@inheritdoc}
58+
*/
59+
public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null)
60+
{
61+
$trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale);
62+
$this->log($id, $domain, $locale);
63+
64+
return $trans;
65+
}
66+
67+
/**
68+
* {@inheritdoc}
69+
*
70+
* @api
71+
*/
72+
public function setLocale($locale)
73+
{
74+
$this->translator->setLocale($locale);
75+
}
76+
77+
/**
78+
* {@inheritdoc}
79+
*
80+
* @api
81+
*/
82+
public function getLocale()
83+
{
84+
return $this->translator->getLocale();
85+
}
86+
87+
/**
88+
* Passes through all unknown calls onto the translator object.
89+
*/
90+
public function __call($method, $args)
91+
{
92+
return call_user_func_array(array($this->translator, $method), $args);
93+
}
94+
95+
/**
96+
* Logs for missing translations.
97+
*
98+
* @param string $id
99+
* @param string|null $domain
100+
* @param string|null $locale
101+
*/
102+
private function log($id, $domain, $locale)
103+
{
104+
if (null === $locale) {
105+
$locale = $this->getLocale();
106+
}
107+
108+
if (null === $domain) {
109+
$domain = 'messages';
110+
}
111+
112+
$id = (string) $id;
113+
$catalogue = $this->translator->getCatalogue($locale);
114+
if ($catalogue->defines($id, $domain)) {
115+
return;
116+
}
117+
118+
if ($catalogue->has($id, $domain)) {
119+
$this->logger->debug('Translation use fallback catalogue.', array('id' => $id, 'domain' => $domain, 'locale' => $catalogue->getLocale()));
120+
} else {
121+
$this->logger->warning('Translation not found.', array('id' => $id, 'domain' => $domain, 'locale' => $catalogue->getLocale()));
122+
}
123+
}
124+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.