From d6f55c31d14db014a1596cdcfcce598985598e42 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 27 Sep 2010 09:45:58 +0200 Subject: [PATCH] [TwigBundle] added helpers for translations --- .../Bundle/TwigBundle/Extension/Helpers.php | 10 ++ .../Bundle/TwigBundle/Node/TransNode.php | 94 +++++++++++++++++++ .../TokenParser/TransChoiceTokenParser.php | 68 ++++++++++++++ .../TokenParser/TransTokenParser.php | 86 +++++++++++++++++ 4 files changed, 258 insertions(+) create mode 100644 src/Symfony/Bundle/TwigBundle/Node/TransNode.php create mode 100644 src/Symfony/Bundle/TwigBundle/TokenParser/TransChoiceTokenParser.php create mode 100644 src/Symfony/Bundle/TwigBundle/TokenParser/TransTokenParser.php diff --git a/src/Symfony/Bundle/TwigBundle/Extension/Helpers.php b/src/Symfony/Bundle/TwigBundle/Extension/Helpers.php index a7e973eee6..d030955b64 100644 --- a/src/Symfony/Bundle/TwigBundle/Extension/Helpers.php +++ b/src/Symfony/Bundle/TwigBundle/Extension/Helpers.php @@ -4,6 +4,8 @@ namespace Symfony\Bundle\TwigBundle\Extension; use Symfony\Component\Templating\Engine; use Symfony\Bundle\TwigBundle\TokenParser\HelperTokenParser; +use Symfony\Bundle\TwigBundle\TokenParser\TransTokenParser; +use Symfony\Bundle\TwigBundle\TokenParser\TransChoiceTokenParser; /* * This file is part of the Symfony package. @@ -51,6 +53,14 @@ class Helpers extends \Twig_Extension // {% flash 'notice' %} new HelperTokenParser('flash', '', 'session', 'flash'), + + // {% trans "Symfony is great!" %} + new TransTokenParser(), + + // {% transchoice count %} + // {0} There is no apples|{1} There is one apple|]1,Inf] There is {{ count }} apples + // {% endtranschoice %} + new TransChoiceTokenParser(), ); } diff --git a/src/Symfony/Bundle/TwigBundle/Node/TransNode.php b/src/Symfony/Bundle/TwigBundle/Node/TransNode.php new file mode 100644 index 0000000000..daf5f3e992 --- /dev/null +++ b/src/Symfony/Bundle/TwigBundle/Node/TransNode.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * + * + * @author Fabien Potencier + */ +class TransNode extends \Twig_Node +{ + public function __construct(\Twig_NodeInterface $body, \Twig_NodeInterface $domain, \Twig_Node_Expression $count = null, $lineno, $tag = null) + { + parent::__construct(array('count' => $count, 'body' => $body, 'domain' => $domain), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param \Twig_Compiler A Twig_Compiler instance + */ + public function compile($compiler) + { + $compiler->addDebugInfo($this); + + list($msg, $vars) = $this->compileString($this->body); + + $method = null === $this->count ? 'trans' : 'transChoice'; + + $compiler + ->write('echo $context[\'_view\'][\'translator\']->'.$method.'(') + ->subcompile($msg) + ; + + $compiler->raw(', '); + + if (null !== $this->count) { + $compiler + ->subcompile($this->count) + ->raw(', ') + ; + } + + $compiler->raw('array('); + + foreach ($vars as $var) { + $compiler + ->string('{{ '.$var['name'].' }}') + ->raw(' => ') + ->subcompile($var) + ->raw(', ') + ; + } + + $compiler + ->raw("), ") + ->subcompile($this->domain) + ->raw(");\n") + ; + } + + protected function compileString(\Twig_NodeInterface $body) + { + if ($body instanceof \Twig_Node_Expression_Name || $body instanceof \Twig_Node_Expression_Constant) { + return array($body, array()); + } + + $msg = ''; + $vars = array(); + foreach ($body as $node) { + if ($node instanceof \Twig_Node_Print) { + $n = $node->expr; + while ($n instanceof \Twig_Node_Expression_Filter) { + $n = $n->node; + } + $msg .= sprintf('{{ %s }}', $n['name']); + $vars[] = new \Twig_Node_Expression_Name($n['name'], $n->getLine()); + } else { + $msg .= $node['data']; + } + } + + return array(new \Twig_Node(array(new \Twig_Node_Expression_Constant(trim($msg), $node->getLine()))), $vars); + } +} diff --git a/src/Symfony/Bundle/TwigBundle/TokenParser/TransChoiceTokenParser.php b/src/Symfony/Bundle/TwigBundle/TokenParser/TransChoiceTokenParser.php new file mode 100644 index 0000000000..c740432f1a --- /dev/null +++ b/src/Symfony/Bundle/TwigBundle/TokenParser/TransChoiceTokenParser.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * + * + * @author Fabien Potencier + */ +class TransChoiceTokenParser extends TransTokenParser +{ + /** + * Parses a token and returns a node. + * + * @param \Twig_Token $token A Twig_Token instance + * + * @return \Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(\Twig_Token $token) + { + $lineno = $token->getLine(); + $stream = $this->parser->getStream(); + + $count = $this->parser->getExpressionParser()->parseExpression(); + + $domain = new \Twig_Node_Expression_Constant('messages', $lineno); + if (!$stream->test(\Twig_Token::BLOCK_END_TYPE) && $stream->test('from')) { + $stream->next(); + $domain = $this->parser->getExpressionParser()->parseExpression(); + } + + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + + $body = $this->parser->subparse(array($this, 'decideTransChoiceFork'), true); + + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + + $this->checkTransString($body, $lineno); + + return new TransNode($body, $domain, $count, $lineno, $this->getTag()); + } + + public function decideTransChoiceFork($token) + { + return $token->test(array('endtranschoice')); + } + + /** + * Gets the tag name associated with this token parser. + * + * @param string The tag name + */ + public function getTag() + { + return 'transchoice'; + } +} diff --git a/src/Symfony/Bundle/TwigBundle/TokenParser/TransTokenParser.php b/src/Symfony/Bundle/TwigBundle/TokenParser/TransTokenParser.php new file mode 100644 index 0000000000..c2f8f7cce5 --- /dev/null +++ b/src/Symfony/Bundle/TwigBundle/TokenParser/TransTokenParser.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * + * + * @author Fabien Potencier + */ +class TransTokenParser extends \Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param \Twig_Token $token A Twig_Token instance + * + * @return \Twig_NodeInterface A Twig_NodeInterface instance + */ + public function parse(\Twig_Token $token) + { + $lineno = $token->getLine(); + $stream = $this->parser->getStream(); + + $domain = new \Twig_Node_Expression_Constant('messages', $lineno); + if (!$stream->test(\Twig_Token::BLOCK_END_TYPE)) { + if (!$stream->test('from')) { + $body = $this->parser->getExpressionParser()->parseExpression(); + } + + if ($stream->test('from')) { + $stream->next(); + $domain = $this->parser->getExpressionParser()->parseExpression(); + } + } else { + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + $body = $this->parser->subparse(array($this, 'decideTransFork')); + } + + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + + $this->checkTransString($body, $lineno); + + return new TransNode($body, $domain, null, $lineno, $this->getTag()); + } + + public function decideTransFork($token) + { + return $token->test(array('endtrans')); + } + + /** + * Gets the tag name associated with this token parser. + * + * @param string The tag name + */ + public function getTag() + { + return 'trans'; + } + + protected function checkTransString(\Twig_NodeInterface $body, $lineno) + { + foreach ($body as $i => $node) { + if ( + $node instanceof \Twig_Node_Text + || + ($node instanceof \Twig_Node_Print && $node->expr instanceof \Twig_Node_Expression_Name) + ) { + continue; + } + + throw new \Twig_SyntaxError(sprintf('The text to be translated with "trans" can only contain references to simple variables'), $lineno); + } + } +}