Created
July 5, 2015 15:10
-
-
Save damienalexandre/fa0d513d2fc832c132d0 to your computer and use it in GitHub Desktop.
Adding a checkbox on the translation panel + custom save controller
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace AppBundle\Controller; | |
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | |
use Symfony\Bundle\FrameworkBundle\Controller\Controller; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\HttpFoundation\Response; | |
class ProfilerController extends Controller | |
{ | |
/** | |
* Save the selected translation to resources. | |
* | |
* @Route("/{token}/translation/save", name="_profiler_save_translations") | |
* | |
* @return Response A Response instance | |
*/ | |
public function saveAction(Request $request, $token) | |
{ | |
if (!$request->isXmlHttpRequest()) { | |
return $this->redirectToRoute('_profiler', ['token' => $token]); | |
} | |
$profiler = $this->get('profiler'); | |
$profiler->disable(); | |
$selected = $request->request->get('selected'); | |
if (!$selected || count($selected) == 0) { | |
return new Response('No key selected.'); | |
} | |
$profile = $profiler->loadProfile($token); | |
$all = $profile->getCollector('translation'); | |
$toSave = array_intersect_key($all->getMessages(), array_flip($selected)); | |
// @todo Save the $toSave messages! | |
// I'm using a custim Loco service, doing API calls for each message. | |
if (true) { | |
return new Response(sprintf("%s translation keys saved!", count($selected))); | |
} else { | |
return new Response("Can't save the translations."); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Add | |
_profiler_custom: | |
resource: "@AppBundle/Controller/ProfilerController.php" | |
type: annotation | |
prefix: /_profiler |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
services: | |
# Custom data_collector to use our own template | |
app.data_collector.translation: | |
class: 'Symfony\Component\Translation\DataCollector\TranslationDataCollector' | |
arguments: ['@translator.data_collector'] | |
tags: | |
- { name: 'data_collector', template: "profiler/translation.html.twig", id: "translation", priority: 200 } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% 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>Translation Stats</h2> | |
<table> | |
<tbody> | |
<tr> | |
<th>Defined messages</th> | |
<td><pre>{{ collector.countdefines }}</pre></td> | |
</tr> | |
<tr> | |
<th scope="col" style="width: 30%">Fallback messages</th> | |
<td scope="col" style="width: 60%"><pre>{{ collector.countFallbacks }}</pre></td> | |
</tr> | |
<tr> | |
<th>Missing messages</th> | |
<td><pre>{{ collector.countMissings }}</pre></td> | |
</tr> | |
</tbody> | |
</table> | |
<form action="{{ path('_profiler_save_translations', {'token': token}) }}" method="post" | |
id="translations-list" onsubmit="javascript:saveTranslations(this);return false;" > | |
<table> | |
<tr> | |
<th>Select</th> | |
<th>State</th> | |
<th>Locale</th> | |
<th>Domain</th> | |
<th>Id</th> | |
<th>Message Preview</th> | |
</tr> | |
{% for key, message in collector.messages %} | |
<tr> | |
<td> | |
{% if message.state == constant('Symfony\\Component\\Translation\\DataCollectorTranslator::MESSAGE_MISSING') %} | |
<input type="checkbox" name="translationKey" value="{{ key }}"> | |
{% else %} | |
<input type="checkbox" disabled> | |
{% endif %} | |
</td> | |
<td><code>{{ translator.state(message) }}</code></td> | |
<td><code>{{ message.locale }}</code></td> | |
<td><code>{{ message.domain }}</code></td> | |
<td> | |
<code>{{ message.id }}</code> | |
{% if message.count > 1 %}<br><small style="color: gray;">(used {{ message.count }} times)</small>{% endif %} | |
{% if message.transChoiceNumber is not null %}<br><small style="color: gray;">(use pluralization)</small>{% endif %} | |
<div> | |
[<a href="#" onclick="return openParameters(this);" style="text-decoration: none;" | |
title="Toggle parameters display" data-target-id="parameters-{{ loop.index }}" > | |
<img alt="+" src="data:image/gif;base64,R0lGODlhEgASAMQTANft99/v+Ga44bHb8ITG52S44dXs9+z1+uPx+YvK6WC24G+944/M6W28443L6dnu+Ge54v/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABMALAAAAAASABIAQAVS4DQBTiOd6LkwgJgeUSzHSDoNaZ4PU6FLgYBA5/vFID/DbylRGiNIZu74I0h1hNsVxbNuUV4d9SsZM2EzWe1qThVzwWFOAFCQFa1RQq6DJB4iIQA7" style="display: inline; width: 12px; height: 12px;" /> | |
<img alt="-" src="data:image/gif;base64,R0lGODlhEgASAMQSANft94TG57Hb8GS44ez1+mC24IvK6ePx+Wa44dXs92+942e54o3L6W2844/M6dnu+P/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABIALAAAAAASABIAQAVCoCQBTBOd6Kk4gJhGBCTPxysJb44K0qD/ER/wlxjmisZkMqBEBW5NHrMZmVKvv9hMVsO+hE0EoNAstEYGxG9heIhCADs=" style="display: none; width: 12px; height: 12px;" /> | |
<span style="vertical-align:top">Parameters</span> | |
</a>] | |
<div id="parameters-{{ loop.index }}" style="display: none;"> | |
{% for parameters in message.parameters %} | |
{% if parameters|length > 0 %} | |
{{ dump(parameters) }} | |
{% else %} | |
{{ dump(null) }} | |
{% endif %} | |
{% endfor %} | |
</div> | |
</div> | |
</td> | |
<td><code>{{ message.translation }}</code></td> | |
</tr> | |
{% endfor %} | |
</table> | |
<div id="translationResult"> | |
<button type="submit" class="sf-button"> | |
<span class="border-l"> | |
<span class="border-r"> | |
<span class="btn-bg">Add selected translations to catalogs</span> | |
</span> | |
</span> | |
</button> | |
</div> | |
</form> | |
<script type="text/javascript"> | |
function openParameters(link) { | |
"use strict"; | |
var imgs = link.children, | |
target = link.getAttribute('data-target-id'); | |
Sfjs.toggle(target, imgs[0], imgs[1]); | |
}; | |
var serializeQueryString = function(obj, prefix) { | |
var str = []; | |
for(var p in obj) { | |
if (obj.hasOwnProperty(p)) { | |
var k = prefix ? prefix + "[" + p + "]" : p, v = obj[p]; | |
str.push(typeof v == "object" ? serializeQueryString(v, k) : encodeURIComponent(k) + "=" + encodeURIComponent(v)); | |
} | |
} | |
return str.join("&"); | |
}; | |
// We need to hack a bit Sfjs.request because it does not support POST requests | |
// May not work for ActiveXObject('Microsoft.XMLHTTP'); :( | |
(function(open) { | |
XMLHttpRequest.prototype.open = function(method, url, async, user, pass) { | |
open.call(this, method, url, async, user, pass); | |
if (method.toLowerCase() === 'post') { | |
this.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); | |
} | |
}; | |
})(XMLHttpRequest.prototype.open); | |
var saveTranslations = function(form) { | |
"use strict"; | |
var inputs = form.translationKey; | |
var selected = []; | |
if (!inputs.value) { | |
for (var val in inputs) { | |
if (inputs.hasOwnProperty(val) && inputs[val].value) { | |
if (inputs[val].checked) { | |
selected.push(inputs[val].value); | |
} | |
} | |
} | |
} else if (inputs.checked) { | |
selected.push(inputs.value); | |
} | |
Sfjs.request( | |
form.action, | |
function(xhr) { | |
// Success | |
document.getElementById('translationResult').innerHTML = xhr.responseText; | |
}, | |
function(xhr) { | |
// Error | |
document.getElementById('translationResult').innerHTML = xhr.responseText; | |
}, | |
serializeQueryString({selected: selected}), | |
{ method: 'POST' } | |
); | |
return false; | |
}; | |
</script> | |
{% 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 %} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For those who just want to update the corresponding translation files in app/Resources/translations: