feature #14003 [2.7][Translation][Profiler] Added a Translation profiler. (aitboudad)
This PR was merged into the 2.7 branch.
Discussion
----------
[2.7][Translation][Profiler] Added a Translation profiler.
| Q | A
| ------------- | ---
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Fixed tickets | ~
| Tests pass? | yes
| License | MIT
- [x] tests
![selection_005](https://cloud.githubusercontent.com/assets/1753742/6762405/7e355396-cf57-11e4-9836-cdaebb541d13.png)
![selection_006](https://cloud.githubusercontent.com/assets/1753742/6762407/91dc9422-cf57-11e4-8f6f-f89c9d067b03.png)
Commits
-------
c923b2a
[Translation][Profiler] Added a Translation profiler.
This commit is contained in:
commit
2533d8fe7b
@ -35,6 +35,7 @@ use Symfony\Component\Validator\Validation;
|
|||||||
class FrameworkExtension extends Extension
|
class FrameworkExtension extends Extension
|
||||||
{
|
{
|
||||||
private $formConfigEnabled = false;
|
private $formConfigEnabled = false;
|
||||||
|
private $translationConfigEnabled = false;
|
||||||
private $sessionConfigEnabled = false;
|
private $sessionConfigEnabled = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -116,8 +117,8 @@ class FrameworkExtension extends Extension
|
|||||||
$this->registerEsiConfiguration($config['esi'], $container, $loader);
|
$this->registerEsiConfiguration($config['esi'], $container, $loader);
|
||||||
$this->registerSsiConfiguration($config['ssi'], $container, $loader);
|
$this->registerSsiConfiguration($config['ssi'], $container, $loader);
|
||||||
$this->registerFragmentsConfiguration($config['fragments'], $container, $loader);
|
$this->registerFragmentsConfiguration($config['fragments'], $container, $loader);
|
||||||
$this->registerProfilerConfiguration($config['profiler'], $container, $loader);
|
|
||||||
$this->registerTranslatorConfiguration($config['translator'], $container);
|
$this->registerTranslatorConfiguration($config['translator'], $container);
|
||||||
|
$this->registerProfilerConfiguration($config['profiler'], $container, $loader);
|
||||||
|
|
||||||
if (isset($config['router'])) {
|
if (isset($config['router'])) {
|
||||||
$this->registerRouterConfiguration($config['router'], $container, $loader);
|
$this->registerRouterConfiguration($config['router'], $container, $loader);
|
||||||
@ -288,10 +289,15 @@ class FrameworkExtension extends Extension
|
|||||||
$loader->load('profiling.xml');
|
$loader->load('profiling.xml');
|
||||||
$loader->load('collectors.xml');
|
$loader->load('collectors.xml');
|
||||||
|
|
||||||
if (true === $this->formConfigEnabled) {
|
if ($this->formConfigEnabled) {
|
||||||
$loader->load('form_debug.xml');
|
$loader->load('form_debug.xml');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->translationConfigEnabled) {
|
||||||
|
$loader->load('translation_debug.xml');
|
||||||
|
$container->getDefinition('translator.data_collector')->setDecoratedService('translator');
|
||||||
|
}
|
||||||
|
|
||||||
$container->setParameter('profiler_listener.only_exceptions', $config['only_exceptions']);
|
$container->setParameter('profiler_listener.only_exceptions', $config['only_exceptions']);
|
||||||
$container->setParameter('profiler_listener.only_master_requests', $config['only_master_requests']);
|
$container->setParameter('profiler_listener.only_master_requests', $config['only_master_requests']);
|
||||||
|
|
||||||
@ -644,6 +650,7 @@ class FrameworkExtension extends Extension
|
|||||||
if (!$this->isConfigEnabled($container, $config)) {
|
if (!$this->isConfigEnabled($container, $config)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
$this->translationConfigEnabled = true;
|
||||||
|
|
||||||
// Use the "real" translator instead of the identity default
|
// Use the "real" translator instead of the identity default
|
||||||
$container->setAlias('translator', 'translator.default');
|
$container->setAlias('translator', 'translator.default');
|
||||||
@ -865,9 +872,9 @@ class FrameworkExtension extends Extension
|
|||||||
/**
|
/**
|
||||||
* Loads the serializer configuration.
|
* Loads the serializer configuration.
|
||||||
*
|
*
|
||||||
* @param array $config A serializer configuration array
|
* @param array $config A serializer configuration array
|
||||||
* @param ContainerBuilder $container A ContainerBuilder instance
|
* @param ContainerBuilder $container A ContainerBuilder instance
|
||||||
* @param XmlFileLoader $loader An XmlFileLoader instance
|
* @param XmlFileLoader $loader An XmlFileLoader instance
|
||||||
*/
|
*/
|
||||||
private function registerSerializerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
|
private function registerSerializerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" ?>
|
||||||
|
|
||||||
|
<container xmlns="http://symfony.com/schema/dic/services"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||||
|
|
||||||
|
<services>
|
||||||
|
<!-- DataCollectorTranslator -->
|
||||||
|
<service id="translator.data_collector" class="Symfony\Component\Translation\DataCollectorTranslator" public="false">
|
||||||
|
<argument type="service" id="translator.data_collector.inner" />
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<!-- DataCollector -->
|
||||||
|
<service id="data_collector.translation" class="Symfony\Component\Translation\DataCollector\TranslationDataCollector">
|
||||||
|
<tag name="data_collector" template="@WebProfiler/Collector/translation.html.twig" id="translation" priority="255" />
|
||||||
|
<argument type="service" id="translator.data_collector" />
|
||||||
|
</service>
|
||||||
|
</services>
|
||||||
|
</container>
|
@ -0,0 +1,93 @@
|
|||||||
|
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||||
|
|
||||||
|
{% import _self as translator %}
|
||||||
|
|
||||||
|
{% block toolbar %}
|
||||||
|
{% if collector.messages|length %}
|
||||||
|
{% set icon %}
|
||||||
|
<svg width="28" height="28" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 417 300" enable-background="new 0 0 417 300" xml:space="preserve"><g id="Layer_1_1_"><g id="outline_1_"><path fill="#B5B5B6" d="M275.9,145c0,18.2-14.799,33-33,33H120.701l-36.3,42l-0.3-42H40c-18.2,0-33-14.8-33-33V44c0-18.2,14.8-33,33-33h202.9c18.199,0,33,14.8,33,33V145L275.9,145z"/></g><g enable-background="new"><path fill="#FFFFFF" d="M194.501,146.962h-23.898l-9.5-24.715h-43.492l-8.98,24.715H85.326l42.379-108.805h23.23L194.501,146.962zM154.052,103.915L139.06,63.54l-14.695,40.375H154.052z"/></g></g><g id="Layer_2_1_"><g id="japanese"><g id="outline"><path fill="#414141" d="M141.451,214c0,18.2,14.8,33,33,33h122.2l36.301,42l0.301-42h44.1c18.201,0,33-14.8,33-33V113c0-18.2-14.799-33-33-33H174.453c-18.201,0-33,14.8-33,33L141.451,214L141.451,214z"/></g><g enable-background="new"><path fill="#FFFFFF" d="M312.158,143.327c-0.455,1.672-0.912,3.344-1.215,5.016c22.039,6.08,31.766,21.431,31.766,38.455c0,24.318-18.238,40.733-57.301,45.598c-1.217-3.952-5.016-11.248-7.904-15.352c27.359-3.04,45.295-12.159,45.295-29.791c0-5.016-1.672-16.871-18.088-22.19c-6.688,15.199-16.871,29.335-28.727,39.519c0.607,1.976,1.367,3.647,2.127,5.167l-15.654,10.032c-0.76-1.521-1.52-3.192-2.129-5.017c-7.6,4.256-15.959,6.992-24.471,6.992c-13.375,0-22.189-8.512-22.189-22.647c0-20.975,16.111-37.542,37.693-46.357c-0.305-6.536-0.305-13.223-0.305-20.215c-11.398,0.304-23.711,0.608-29.789,0.456l-0.912-17.783c6.99,0.152,19.758,0.152,31.006,0.152c0.305-6.536,0.457-14.135,0.76-20.519l23.863,1.824c-0.305,1.52-1.52,2.736-4.104,3.04c-0.457,4.408-0.76,10.184-1.217,15.047c16.568-0.76,37.391-2.736,54.262-6.384l1.672,18.391c-16.719,3.04-38.605,4.56-56.846,5.168c-0.15,5.319-0.303,10.487-0.303,15.503c6.383-1.52,15.654-2.432,22.799-1.976c0.607-2.28,1.063-4.56,1.215-6.84L312.158,143.327z M255.77,198.044c-1.672-8.056-2.736-17.479-3.496-27.814c-12.008,5.927-20.215,15.199-20.215,25.382c0,8.664,6.535,8.36,8.512,8.209C245.281,203.668,250.449,201.539,255.77,198.044zM286.473,162.021c-2.129-0.304-10.033,0.305-16.871,2.128c0.455,7.6,0.91,14.591,1.975,20.671C277.504,178.589,282.672,170.686,286.473,162.021z"/></g></g></g></svg>
|
||||||
|
{% if collector.countMissings %}
|
||||||
|
{% set status_color = "red" %}
|
||||||
|
{% elseif collector.countFallbacks %}
|
||||||
|
{% set status_color = "yellow" %}
|
||||||
|
{% endif %}
|
||||||
|
{% set error_count = collector.countMissings + collector.countFallbacks %}
|
||||||
|
<span class="sf-toolbar-status{% if status_color is defined %} sf-toolbar-status-{{ status_color }}{% endif %}">{{ error_count ?: collector.countdefines }}</span>
|
||||||
|
{% endset %}
|
||||||
|
{% set text %}
|
||||||
|
{% if collector.countMissings %}
|
||||||
|
<div class="sf-toolbar-info-piece">
|
||||||
|
<b>Missing messages</b>
|
||||||
|
<span class="sf-toolbar-status sf-toolbar-status-red">{{ collector.countMissings }}</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if collector.countFallbacks %}
|
||||||
|
<div class="sf-toolbar-info-piece">
|
||||||
|
<b>Fallback messages</b>
|
||||||
|
<span class="sf-toolbar-status sf-toolbar-status-yellow">{{ collector.countFallbacks }}</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if collector.countdefines %}
|
||||||
|
<div class="sf-toolbar-info-piece">
|
||||||
|
<b>Defined messages</b>
|
||||||
|
<span class="sf-toolbar-status sf-toolbar-status-green">{{ collector.countdefines }}</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endset %}
|
||||||
|
{% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': profiler_url } %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block menu %}
|
||||||
|
<span class="label">
|
||||||
|
<span class="icon"><svg width="35" height="28" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 417 300" enable-background="new 0 0 417 300" xml:space="preserve"><g id="Layer_1_1_"><g id="outline_1_"><path fill="#B5B5B6" d="M275.9,145c0,18.2-14.799,33-33,33H120.701l-36.3,42l-0.3-42H40c-18.2,0-33-14.8-33-33V44c0-18.2,14.8-33,33-33h202.9c18.199,0,33,14.8,33,33V145L275.9,145z"/></g><g enable-background="new"><path fill="#FFFFFF" d="M194.501,146.962h-23.898l-9.5-24.715h-43.492l-8.98,24.715H85.326l42.379-108.805h23.23L194.501,146.962zM154.052,103.915L139.06,63.54l-14.695,40.375H154.052z"/></g></g><g id="Layer_2_1_"><g id="japanese"><g id="outline"><path fill="#414141" d="M141.451,214c0,18.2,14.8,33,33,33h122.2l36.301,42l0.301-42h44.1c18.201,0,33-14.8,33-33V113c0-18.2-14.799-33-33-33H174.453c-18.201,0-33,14.8-33,33L141.451,214L141.451,214z"/></g><g enable-background="new"><path fill="#FFFFFF" d="M312.158,143.327c-0.455,1.672-0.912,3.344-1.215,5.016c22.039,6.08,31.766,21.431,31.766,38.455c0,24.318-18.238,40.733-57.301,45.598c-1.217-3.952-5.016-11.248-7.904-15.352c27.359-3.04,45.295-12.159,45.295-29.791c0-5.016-1.672-16.871-18.088-22.19c-6.688,15.199-16.871,29.335-28.727,39.519c0.607,1.976,1.367,3.647,2.127,5.167l-15.654,10.032c-0.76-1.521-1.52-3.192-2.129-5.017c-7.6,4.256-15.959,6.992-24.471,6.992c-13.375,0-22.189-8.512-22.189-22.647c0-20.975,16.111-37.542,37.693-46.357c-0.305-6.536-0.305-13.223-0.305-20.215c-11.398,0.304-23.711,0.608-29.789,0.456l-0.912-17.783c6.99,0.152,19.758,0.152,31.006,0.152c0.305-6.536,0.457-14.135,0.76-20.519l23.863,1.824c-0.305,1.52-1.52,2.736-4.104,3.04c-0.457,4.408-0.76,10.184-1.217,15.047c16.568-0.76,37.391-2.736,54.262-6.384l1.672,18.391c-16.719,3.04-38.605,4.56-56.846,5.168c-0.15,5.319-0.303,10.487-0.303,15.503c6.383-1.52,15.654-2.432,22.799-1.976c0.607-2.28,1.063-4.56,1.215-6.84L312.158,143.327z M255.77,198.044c-1.672-8.056-2.736-17.479-3.496-27.814c-12.008,5.927-20.215,15.199-20.215,25.382c0,8.664,6.535,8.36,8.512,8.209C245.281,203.668,250.449,201.539,255.77,198.044zM286.473,162.021c-2.129-0.304-10.033,0.305-16.871,2.128c0.455,7.6,0.91,14.591,1.975,20.671C277.504,178.589,282.672,170.686,286.473,162.021z"/></g></g></g></svg></span>
|
||||||
|
<strong>Translation</strong>
|
||||||
|
</span>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block panel %}
|
||||||
|
{% if collector.messages is empty %}
|
||||||
|
<h2>Translations</h2>
|
||||||
|
<p>
|
||||||
|
<em>No translations have been called.</em>
|
||||||
|
</p>
|
||||||
|
{% else %}
|
||||||
|
{{ block('panelContent') }}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block panelContent %}
|
||||||
|
<h2>Called Translations</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Defined messages: {{ collector.countdefines }}</strong></li>
|
||||||
|
<li><strong>Fallback messages: {{ collector.countFallbacks }}</strong></li>
|
||||||
|
<li><strong>Missing messages: {{ collector.countMissings }}</strong></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>State</th>
|
||||||
|
<th>Locale</th>
|
||||||
|
<th>Domain</th>
|
||||||
|
<th>Id</th>
|
||||||
|
<th>Message Preview</th>
|
||||||
|
</tr>
|
||||||
|
{% for message in collector.messages %}
|
||||||
|
<tr>
|
||||||
|
<td><code>{{ translator.state(message) }}</code></td>
|
||||||
|
<td><code>{{ message.locale }}</code></td>
|
||||||
|
<td><code>{{ message.domain }}</code></td>
|
||||||
|
<td><code>{{ message.id }}</code></td>
|
||||||
|
<td><code>{{ message.translation }}</code></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% macro state(translation) %}
|
||||||
|
{% if translation.state == constant('Symfony\\Component\\Translation\\DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK') %}
|
||||||
|
same as fallback
|
||||||
|
{% elseif translation.state == constant('Symfony\\Component\\Translation\\DataCollectorTranslator::MESSAGE_MISSING') %}
|
||||||
|
missing
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
@ -0,0 +1,132 @@
|
|||||||
|
<?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\Component\Translation\DataCollector;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||||
|
use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
|
||||||
|
use Symfony\Component\Translation\DataCollectorTranslator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
|
||||||
|
*/
|
||||||
|
class TranslationDataCollector extends DataCollector implements LateDataCollectorInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var DataCollectorTranslator
|
||||||
|
*/
|
||||||
|
private $translator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param DataCollectorTranslator $translator
|
||||||
|
*/
|
||||||
|
public function __construct(DataCollectorTranslator $translator)
|
||||||
|
{
|
||||||
|
$this->translator = $translator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function lateCollect()
|
||||||
|
{
|
||||||
|
$this->data = $this->computeCount();
|
||||||
|
$this->data['messages'] = $this->sanitizeCollectedMessages($this->translator->getCollectedMessages());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function collect(Request $request, Response $response, \Exception $exception = null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getMessages()
|
||||||
|
{
|
||||||
|
return isset($this->data['messages']) ? $this->data['messages'] : array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getCountMissings()
|
||||||
|
{
|
||||||
|
return isset($this->data[DataCollectorTranslator::MESSAGE_MISSING]) ? $this->data[DataCollectorTranslator::MESSAGE_MISSING] : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getCountFallbacks()
|
||||||
|
{
|
||||||
|
return isset($this->data[DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK]) ? $this->data[DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK] : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getCountDefines()
|
||||||
|
{
|
||||||
|
return isset($this->data[DataCollectorTranslator::MESSAGE_DEFINED]) ? $this->data[DataCollectorTranslator::MESSAGE_DEFINED] : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return 'translation';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sanitizeCollectedMessages($messages)
|
||||||
|
{
|
||||||
|
foreach ($messages as $key => $message) {
|
||||||
|
$messages[$key]['translation'] = $this->sanitizeString($messages[$key]['translation']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function computeCount()
|
||||||
|
{
|
||||||
|
$count = array(
|
||||||
|
DataCollectorTranslator::MESSAGE_DEFINED => 0,
|
||||||
|
DataCollectorTranslator::MESSAGE_MISSING => 0,
|
||||||
|
DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK => 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($this->translator->getCollectedMessages() as $message) {
|
||||||
|
++$count[$message['state']];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sanitizeString($string, $length = 80)
|
||||||
|
{
|
||||||
|
$string = trim(preg_replace('/\s+/', ' ', $string));
|
||||||
|
|
||||||
|
if (function_exists('mb_strlen') && false !== $encoding = mb_detect_encoding($string)) {
|
||||||
|
if (mb_strlen($string, $encoding) > $length) {
|
||||||
|
return mb_substr($string, 0, $length - 3, $encoding).'...';
|
||||||
|
}
|
||||||
|
} elseif (strlen($string) > $length) {
|
||||||
|
return substr($string, 0, $length - 3).'...';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $string;
|
||||||
|
}
|
||||||
|
}
|
153
src/Symfony/Component/Translation/DataCollectorTranslator.php
Normal file
153
src/Symfony/Component/Translation/DataCollectorTranslator.php
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
<?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\Component\Translation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
|
||||||
|
*/
|
||||||
|
class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInterface
|
||||||
|
{
|
||||||
|
const MESSAGE_DEFINED = 0;
|
||||||
|
const MESSAGE_MISSING = 1;
|
||||||
|
const MESSAGE_EQUALS_FALLBACK = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var TranslatorInterface
|
||||||
|
*/
|
||||||
|
private $translator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $messages = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Translator $translator
|
||||||
|
*/
|
||||||
|
public function __construct(TranslatorInterface $translator)
|
||||||
|
{
|
||||||
|
if (!($translator instanceof TranslatorInterface && $translator instanceof TranslatorBagInterface)) {
|
||||||
|
throw new \InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface and TranslatorBagInterface.', get_class($translator)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->translator = $translator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function trans($id, array $parameters = array(), $domain = null, $locale = null)
|
||||||
|
{
|
||||||
|
$trans = $this->translator->trans($id, $parameters, $domain, $locale);
|
||||||
|
$this->collectMessage($locale, $domain, $id, $trans);
|
||||||
|
|
||||||
|
return $trans;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null)
|
||||||
|
{
|
||||||
|
$trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale);
|
||||||
|
$this->collectMessage($locale, $domain, $id, $trans);
|
||||||
|
|
||||||
|
return $trans;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
public function setLocale($locale)
|
||||||
|
{
|
||||||
|
$this->translator->setLocale($locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
public function getLocale()
|
||||||
|
{
|
||||||
|
return $this->translator->getLocale();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getCatalogue($locale = null)
|
||||||
|
{
|
||||||
|
return $this->translator->getCatalogue($locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Passes through all unknown calls onto the translator object.
|
||||||
|
*/
|
||||||
|
public function __call($method, $args)
|
||||||
|
{
|
||||||
|
return call_user_func_array(array($this->translator, $method), $args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getCollectedMessages()
|
||||||
|
{
|
||||||
|
return $this->messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $locale
|
||||||
|
* @param string|null $domain
|
||||||
|
* @param string $id
|
||||||
|
* @param string $trans
|
||||||
|
*/
|
||||||
|
private function collectMessage($locale, $domain, $id, $translation)
|
||||||
|
{
|
||||||
|
if (null === $locale) {
|
||||||
|
$locale = $this->getLocale();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $domain) {
|
||||||
|
$domain = 'messages';
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = (string) $id;
|
||||||
|
$catalogue = $this->translator->getCatalogue($locale);
|
||||||
|
if ($catalogue->defines($id, $domain)) {
|
||||||
|
$state = self::MESSAGE_DEFINED;
|
||||||
|
} elseif ($catalogue->has($id, $domain)) {
|
||||||
|
$state = self::MESSAGE_EQUALS_FALLBACK;
|
||||||
|
|
||||||
|
$fallbackCatalogue = $catalogue->getFallBackCatalogue();
|
||||||
|
while ($fallbackCatalogue) {
|
||||||
|
if ($fallbackCatalogue->defines($id, $domain)) {
|
||||||
|
$locale = $fallbackCatalogue->getLocale();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$state = self::MESSAGE_MISSING;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->messages[] = array(
|
||||||
|
'locale' => $locale,
|
||||||
|
'domain' => $domain,
|
||||||
|
'id' => $id,
|
||||||
|
'translation' => $translation,
|
||||||
|
'state' => $state,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -35,7 +35,7 @@ class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface
|
|||||||
public function __construct($translator, LoggerInterface $logger)
|
public function __construct($translator, LoggerInterface $logger)
|
||||||
{
|
{
|
||||||
if (!($translator instanceof TranslatorInterface && $translator instanceof TranslatorBagInterface)) {
|
if (!($translator instanceof TranslatorInterface && $translator instanceof TranslatorBagInterface)) {
|
||||||
throw new \InvalidArgumentException(sprintf('The Translator "%s" must implements TranslatorInterface and TranslatorBagInterface.', get_class($translator)));
|
throw new \InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface and TranslatorBagInterface.', get_class($translator)));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->translator = $translator;
|
$this->translator = $translator;
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
<?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\Component\Translation\Tests;
|
||||||
|
|
||||||
|
use Symfony\Component\Translation\Translator;
|
||||||
|
use Symfony\Component\Translation\DataCollectorTranslator;
|
||||||
|
use Symfony\Component\Translation\Loader\ArrayLoader;
|
||||||
|
|
||||||
|
class DataCollectorTranslatorTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
if (!class_exists('Symfony\Component\HttpKernel\DataCollector\DataCollector')) {
|
||||||
|
$this->markTestSkipped('The "DataCollector" is not available');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function testCollectMessages()
|
||||||
|
{
|
||||||
|
$collector = $this->createCollector();
|
||||||
|
$collector->setFallbackLocales(array('fr'));
|
||||||
|
|
||||||
|
$collector->trans('foo');
|
||||||
|
$collector->trans('bar');
|
||||||
|
$collector->transChoice('choice', 0);
|
||||||
|
|
||||||
|
$expectedMessages = array();
|
||||||
|
$expectedMessages[] = array(
|
||||||
|
'id' => 'foo',
|
||||||
|
'translation' => 'foo (en)',
|
||||||
|
'locale' => 'en',
|
||||||
|
'domain' => 'messages',
|
||||||
|
'state' => DataCollectorTranslator::MESSAGE_DEFINED,
|
||||||
|
);
|
||||||
|
$expectedMessages[] = array(
|
||||||
|
'id' => 'bar',
|
||||||
|
'translation' => 'bar (fr)',
|
||||||
|
'locale' => 'fr',
|
||||||
|
'domain' => 'messages',
|
||||||
|
'state' => DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK,
|
||||||
|
);
|
||||||
|
$expectedMessages[] = array(
|
||||||
|
'id' => 'choice',
|
||||||
|
'translation' => 'choice',
|
||||||
|
'locale' => 'en',
|
||||||
|
'domain' => 'messages',
|
||||||
|
'state' => DataCollectorTranslator::MESSAGE_MISSING,
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals($expectedMessages, $collector->getCollectedMessages());
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createCollector()
|
||||||
|
{
|
||||||
|
$translator = new Translator('en');
|
||||||
|
$translator->addLoader('array', new ArrayLoader());
|
||||||
|
$translator->addResource('array', array('foo' => 'foo (en)'), 'en');
|
||||||
|
$translator->addResource('array', array('bar' => 'bar (fr)'), 'fr');
|
||||||
|
|
||||||
|
$collector = new DataCollectorTranslator($translator);
|
||||||
|
|
||||||
|
return $collector;
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user