[TwigBridge] rewrote the Twig translation extractor
* The extractor is now reusable as this is a proper Twig node visitor * The new extractor covers more cases
This commit is contained in:
parent
b6c8f639f0
commit
ee12b67e4e
@ -14,6 +14,7 @@ namespace Symfony\Bridge\Twig\Extension;
|
||||
use Symfony\Bridge\Twig\TokenParser\TransTokenParser;
|
||||
use Symfony\Bridge\Twig\TokenParser\TransChoiceTokenParser;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Bridge\Twig\NodeVisitor\TranslationNodeVisitor;
|
||||
|
||||
/**
|
||||
* Provides integration of the Translation component with Twig.
|
||||
@ -23,10 +24,12 @@ use Symfony\Component\Translation\TranslatorInterface;
|
||||
class TranslationExtension extends \Twig_Extension
|
||||
{
|
||||
private $translator;
|
||||
private $translationNodeVisitor;
|
||||
|
||||
public function __construct(TranslatorInterface $translator)
|
||||
{
|
||||
$this->translator = $translator;
|
||||
$this->translationNodeVisitor = new TranslationNodeVisitor();
|
||||
}
|
||||
|
||||
public function getTranslator()
|
||||
@ -63,6 +66,19 @@ class TranslationExtension extends \Twig_Extension
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
return array($this->translationNodeVisitor);
|
||||
}
|
||||
|
||||
public function getTranslationNodeVisitor()
|
||||
{
|
||||
return $this->translationNodeVisitor;
|
||||
}
|
||||
|
||||
public function trans($message, array $arguments = array(), $domain = "messages", $locale = null)
|
||||
{
|
||||
return $this->translator->trans($message, $arguments, $domain, $locale);
|
||||
|
@ -0,0 +1,88 @@
|
||||
<?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\Bridge\Twig\NodeVisitor;
|
||||
|
||||
use Symfony\Bridge\Twig\Node\TransNode;
|
||||
|
||||
/**
|
||||
* TranslationNodeVisitor extracts translation messages.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class TranslationNodeVisitor implements \Twig_NodeVisitorInterface
|
||||
{
|
||||
private $enabled = false;
|
||||
private $messages = array();
|
||||
|
||||
public function enable()
|
||||
{
|
||||
$this->enabled = true;
|
||||
$this->messages = array();
|
||||
}
|
||||
|
||||
public function disable()
|
||||
{
|
||||
$this->enabled = false;
|
||||
$this->messages = array();
|
||||
}
|
||||
|
||||
public function getMessages()
|
||||
{
|
||||
return $this->messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function enterNode(\Twig_NodeInterface $node, \Twig_Environment $env)
|
||||
{
|
||||
if (!$this->enabled) {
|
||||
return $node;
|
||||
}
|
||||
|
||||
if (
|
||||
$node instanceof \Twig_Node_Expression_Filter &&
|
||||
'trans' === $node->getNode('filter')->getAttribute('value') &&
|
||||
$node->getNode('node') instanceof \Twig_Node_Expression_Constant
|
||||
) {
|
||||
// extract constant nodes with a trans filter
|
||||
$this->messages[] = array(
|
||||
$node->getNode('node')->getAttribute('value'),
|
||||
$node->getNode('arguments')->hasNode(1) ? $node->getNode('arguments')->getNode(1)->getAttribute('value') : null,
|
||||
);
|
||||
} elseif ($node instanceof TransNode) {
|
||||
// extract trans nodes
|
||||
$this->messages[] = array(
|
||||
$node->getNode('body')->getAttribute('data'),
|
||||
$node->getNode('domain')->getAttribute('value'),
|
||||
);
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function leaveNode(\Twig_NodeInterface $node, \Twig_Environment $env)
|
||||
{
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPriority()
|
||||
{
|
||||
return 255;
|
||||
}
|
||||
}
|
@ -14,12 +14,12 @@ namespace Symfony\Bridge\Twig\Translation;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Translation\Extractor\ExtractorInterface;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
use Symfony\Bridge\Twig\Node\TransNode;
|
||||
|
||||
/**
|
||||
* TwigExtractor extracts translation messages from a twig template.
|
||||
*
|
||||
* @author Michel Salib <michelsalib@hotmail.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class TwigExtractor implements ExtractorInterface
|
||||
{
|
||||
@ -61,13 +61,6 @@ class TwigExtractor implements ExtractorInterface
|
||||
}
|
||||
}
|
||||
|
||||
protected function extractTemplate($template, MessageCatalogue $catalogue)
|
||||
{
|
||||
$tree = $this->twig->parse($this->twig->tokenize($template));
|
||||
|
||||
$this->crawlNode($tree, $catalogue);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@ -75,79 +68,18 @@ class TwigExtractor implements ExtractorInterface
|
||||
{
|
||||
$this->prefix = $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts trans message from a twig tree.
|
||||
*
|
||||
* @param \Twig_Node $node The twig tree root
|
||||
* @param MessageCatalogue $catalogue The catalogue
|
||||
*/
|
||||
private function crawlNode(\Twig_Node $node, MessageCatalogue $catalogue)
|
||||
|
||||
protected function extractTemplate($template, MessageCatalogue $catalogue)
|
||||
{
|
||||
if ($node instanceof TransNode && !$node->getNode('body') instanceof \Twig_Node_Expression_GetAttr) {
|
||||
// trans block
|
||||
$message = $node->getNode('body')->getAttribute('data');
|
||||
$domain = $node->getNode('domain')->getAttribute('value');
|
||||
$catalogue->set($message, $this->prefix.$message, $domain);
|
||||
} elseif ($node instanceof \Twig_Node_Print) {
|
||||
// trans filter (be carefull of how you chain your filters)
|
||||
$message = $this->extractMessage($node->getNode('expr'));
|
||||
$domain = $this->extractDomain($node->getNode('expr'));
|
||||
if ($message !== null && $domain !== null) {
|
||||
$catalogue->set($message, $this->prefix.$message, $domain);
|
||||
}
|
||||
} else {
|
||||
// continue crawling
|
||||
foreach ($node as $child) {
|
||||
if ($child != null) {
|
||||
$this->crawlNode($child, $catalogue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$visitor = $this->twig->getExtension('translator')->getTranslationNodeVisitor();
|
||||
$visitor->enable();
|
||||
|
||||
/**
|
||||
* Extracts a message from a \Twig_Node_Print.
|
||||
* Return null if not a constant message.
|
||||
*
|
||||
* @param \Twig_Node $node
|
||||
* @return The message (or null)
|
||||
*/
|
||||
private function extractMessage(\Twig_Node $node)
|
||||
{
|
||||
if ($node->hasNode('node')) {
|
||||
return $this->extractMessage($node->getNode('node'));
|
||||
}
|
||||
if ($node instanceof \Twig_Node_Expression_Constant) {
|
||||
return $node->getAttribute('value');
|
||||
$this->twig->parse($this->twig->tokenize($template));
|
||||
|
||||
foreach ($visitor->getMessages() as $message) {
|
||||
$catalogue->set($message[0], $this->prefix.$message[0], $message[1] ? $message[1] : $this->defaultDomain);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a domain from a \Twig_Node_Print.
|
||||
* Return null if no trans filter.
|
||||
*
|
||||
* @param \Twig_Node $node
|
||||
* @return The domain (or null)
|
||||
*/
|
||||
private function extractDomain(\Twig_Node $node)
|
||||
{
|
||||
// must be a filter node
|
||||
if (!$node instanceof \Twig_Node_Expression_Filter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// is a trans filter
|
||||
if ($node->getNode('filter')->getAttribute('value') === 'trans') {
|
||||
if ($node->getNode('arguments')->hasNode(1)) {
|
||||
return $node->getNode('arguments')->getNode(1)->getAttribute('value');
|
||||
}
|
||||
|
||||
return $this->defaultDomain;
|
||||
}
|
||||
|
||||
return $this->extractDomain($node->getNode('node'));
|
||||
$visitor->disable();
|
||||
}
|
||||
}
|
||||
|
@ -50,9 +50,12 @@ class TwigExtractorTest extends TestCase
|
||||
{
|
||||
return array(
|
||||
array('{{ "new key" | trans() }}', array('new key' => 'messages')),
|
||||
array('{{ "new key" | trans() | upper }}', array('new key' => 'messages')),
|
||||
array('{{ "new key" | trans({}, "domain") }}', array('new key' => 'domain')),
|
||||
array('{% trans %}new key{% endtrans %}', array('new key' => 'messages')),
|
||||
array('{% trans from "domain" %}new key{% endtrans %}', array('new key' => 'domain')),
|
||||
array('{% set foo = "new key" | trans %}', array('new key' => 'messages')),
|
||||
array('{{ 1 ? "new key" | trans : "another key" | trans }}', array('new key' => 'messages', 'another key' => 'messages')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user