Skip to content

Commit

Permalink
[Translation][Profiler] Added a Translation profiler.
Browse files Browse the repository at this point in the history
  • Loading branch information
aitboudad committed Mar 30, 2015
1 parent 007386c commit c923b2a
Show file tree
Hide file tree
Showing 7 changed files with 481 additions and 5 deletions.
Expand Up @@ -35,6 +35,7 @@
class FrameworkExtension extends Extension
{
private $formConfigEnabled = false;
private $translationConfigEnabled = false;
private $sessionConfigEnabled = false;

/**
Expand Down Expand Up @@ -116,8 +117,8 @@ public function load(array $configs, ContainerBuilder $container)
$this->registerEsiConfiguration($config['esi'], $container, $loader);
$this->registerSsiConfiguration($config['ssi'], $container, $loader);
$this->registerFragmentsConfiguration($config['fragments'], $container, $loader);
$this->registerProfilerConfiguration($config['profiler'], $container, $loader);
$this->registerTranslatorConfiguration($config['translator'], $container);
$this->registerProfilerConfiguration($config['profiler'], $container, $loader);

if (isset($config['router'])) {
$this->registerRouterConfiguration($config['router'], $container, $loader);
Expand Down Expand Up @@ -288,10 +289,15 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $
$loader->load('profiling.xml');
$loader->load('collectors.xml');

if (true === $this->formConfigEnabled) {
if ($this->formConfigEnabled) {
$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_master_requests', $config['only_master_requests']);

Expand Down Expand Up @@ -644,6 +650,7 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder
if (!$this->isConfigEnabled($container, $config)) {
return;
}
$this->translationConfigEnabled = true;

// Use the "real" translator instead of the identity default
$container->setAlias('translator', 'translator.default');
Expand Down Expand Up @@ -865,9 +872,9 @@ private function registerSecurityCsrfConfiguration(array $config, ContainerBuild
/**
* 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 XmlFileLoader $loader An XmlFileLoader instance
* @param XmlFileLoader $loader An XmlFileLoader instance
*/
private function registerSerializerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
{
Expand Down
@@ -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;
}
}

0 comments on commit c923b2a

Please sign in to comment.