Skip to content

Commit

Permalink
added the i18n extension
Browse files Browse the repository at this point in the history
  • Loading branch information
fabpot committed Dec 15, 2010
1 parent 927dc5f commit 326a3ad
Show file tree
Hide file tree
Showing 4 changed files with 341 additions and 0 deletions.
44 changes: 44 additions & 0 deletions lib/Twig/Extensions/Extension/I18n.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

/*
* This file is part of Twig.
*
* (c) 2010 Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class Twig_Extensions_Extension_I18n extends Twig_Extension
{
/**
* Returns the token parser instances to add to the existing list.
*
* @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
*/
public function getTokenParsers()
{
return array(new Twig_Extensions_TokenParser_Trans());
}

/**
* Returns a list of filters to add to the existing list.
*
* @return array An array of filters
*/
public function getFilters()
{
return array(
'trans' => new Twig_Filter_Function('gettext'),
);
}

/**
* Returns the name of the extension.
*
* @return string The extension name
*/
public function getName()
{
return 'i18n';
}
}
124 changes: 124 additions & 0 deletions lib/Twig/Extensions/Node/Trans.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?php

/*
* This file is part of Twig.
*
* (c) 2010 Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

/**
* Represents a trans node.
*
* @package twig
* @author Fabien Potencier <[email protected]>
*/
class Twig_Extensions_Node_Trans extends Twig_Node
{
public function __construct(Twig_NodeInterface $body, Twig_NodeInterface $plural = null, Twig_Node_Expression $count = null, $lineno, $tag = null)
{
parent::__construct(array('count' => $count, 'body' => $body, 'plural' => $plural), 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->getNode('body'));

if (null !== $this->getNode('plural')) {
list($msg1, $vars1) = $this->compileString($this->getNode('plural'));

$vars = array_merge($vars, $vars1);
}

$function = null === $this->getNode('plural') ? 'gettext' : 'ngettext';

if ($vars) {
$compiler
->write('echo strtr('.$function.'(')
->subcompile($msg)
;

if (null !== $this->getNode('plural')) {
$compiler
->raw(', ')
->subcompile($msg1)
->raw(', abs(')
->subcompile($this->getNode('count'))
->raw(')')
;
}

$compiler->raw('), array(');

foreach ($vars as $var) {
if ('count' === $var->getAttribute('name')) {
$compiler
->string('%count%')
->raw(' => abs(')
->subcompile($this->getNode('count'))
->raw('), ')
;
} else {
$compiler
->string('%'.$var->getAttribute('name').'%')
->raw(' => ')
->subcompile($var)
->raw(', ')
;
}
}

$compiler->raw("));\n");
} else {
$compiler
->write('echo '.$function.'(')
->subcompile($msg)
;

if (null !== $this->getNode('plural')) {
$compiler
->raw(', ')
->subcompile($msg1)
->raw(', abs(')
->subcompile($this->getNode('count'))
->raw(')')
;
}

$compiler->raw(');');
}
}

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->getNode('expr');
while ($n instanceof Twig_Node_Expression_Filter) {
$n = $n->getNode('node');
}
$msg .= sprintf('%%%s%%', $n->getAttribute('name'));
$vars[] = new Twig_Node_Expression_Name($n->getAttribute('name'), $n->getLine());
} else {
$msg .= $node->getAttribute('data');
}
}

return array(new Twig_Node(array(new Twig_Node_Expression_Constant(trim($msg), $node->getLine()))), $vars);
}
}
80 changes: 80 additions & 0 deletions lib/Twig/Extensions/TokenParser/Trans.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

/*
* This file is part of Twig.
*
* (c) 2010 Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class Twig_Extensions_TokenParser_Trans 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();
$count = null;
$plural = null;

if (!$stream->test(Twig_Token::BLOCK_END_TYPE)) {
$body = $this->parser->getExpressionParser()->parseExpression();
} else {
$stream->expect(Twig_Token::BLOCK_END_TYPE);
$body = $this->parser->subparse(array($this, 'decideForFork'));
if ('plural' === $stream->next()->getValue()) {
$count = $this->parser->getExpressionParser()->parseExpression();
$stream->expect(Twig_Token::BLOCK_END_TYPE);
$plural = $this->parser->subparse(array($this, 'decideForEnd'), true);
}
}

$stream->expect(Twig_Token::BLOCK_END_TYPE);

$this->checkTransString($body, $lineno);

return new Twig_Extensions_Node_Trans($body, $plural, $count, $lineno, $this->getTag());
}

public function decideForFork($token)
{
return $token->test(array('plural', 'endtrans'));
}

public function decideForEnd($token)
{
return $token->test('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->getNode('expr') instanceof Twig_Node_Expression_Name)
) {
continue;
}

throw new Twig_Error_Syntax(sprintf('The text to be translated with "trans" can only contain references to simple variables'), $lineno);
}
}
}
93 changes: 93 additions & 0 deletions test/Twig/Tests/Node/TransTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

/*
* This file is part of Twig.
*
* (c) Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

require_once TWIG_LIB_DIR.'/../test/Twig/Tests/Node/TestCase.php';

class Twig_Tests_Node_TransTest extends Twig_Tests_Node_TestCase
{
/**
* @covers Twig_Node_Trans::__construct
*/
public function testConstructor()
{
$count = new Twig_Node_Expression_Constant(12, 0);
$body = new Twig_Node(array(
new Twig_Node_Text('Hello', 0),
), array(), 0);
$plural = new Twig_Node(array(
new Twig_Node_Text('Hey ', 0),
new Twig_Node_Print(new Twig_Node_Expression_Name('name', 0), 0),
new Twig_Node_Text(', I have ', 0),
new Twig_Node_Print(new Twig_Node_Expression_Name('count', 0), 0),
new Twig_Node_Text(' apples', 0),
), array(), 0);
$node = new Twig_Extensions_Node_Trans($body, $plural, $count, 0);

$this->assertEquals($body, $node->getNode('body'));
$this->assertEquals($count, $node->getNode('count'));
$this->assertEquals($plural, $node->getNode('plural'));
}

public function getTests()
{
$tests = array();

$body = new Twig_Node_Expression_Name('foo', 0);
$node = new Twig_Extensions_Node_Trans($body, null, null, 0);
$tests[] = array($node, 'echo gettext((isset($context[\'foo\']) ? $context[\'foo\'] : null));');

$body = new Twig_Node_Expression_Constant('Hello', 0);
$node = new Twig_Extensions_Node_Trans($body, null, null, 0);
$tests[] = array($node, 'echo gettext("Hello");');

$body = new Twig_Node(array(
new Twig_Node_Text('Hello', 0),
), array(), 0);
$node = new Twig_Extensions_Node_Trans($body, null, null, 0);
$tests[] = array($node, 'echo gettext("Hello");');

$body = new Twig_Node(array(
new Twig_Node_Text('J\'ai ', 0),
new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0),
new Twig_Node_Text(' pommes', 0),
), array(), 0);
$node = new Twig_Extensions_Node_Trans($body, null, null, 0);
$tests[] = array($node, 'echo strtr(gettext("J\'ai %foo% pommes"), array("%foo%" => (isset($context[\'foo\']) ? $context[\'foo\'] : null), ));');

$count = new Twig_Node_Expression_Constant(12, 0);
$body = new Twig_Node(array(
new Twig_Node_Text('Hey ', 0),
new Twig_Node_Print(new Twig_Node_Expression_Name('name', 0), 0),
new Twig_Node_Text(', I have one apple', 0),
), array(), 0);
$plural = new Twig_Node(array(
new Twig_Node_Text('Hey ', 0),
new Twig_Node_Print(new Twig_Node_Expression_Name('name', 0), 0),
new Twig_Node_Text(', I have ', 0),
new Twig_Node_Print(new Twig_Node_Expression_Name('count', 0), 0),
new Twig_Node_Text(' apples', 0),
), array(), 0);
$node = new Twig_Extensions_Node_Trans($body, $plural, $count, 0);
$tests[] = array($node, 'echo strtr(ngettext("Hey %name%, I have one apple", "Hey %name%, I have %count% apples", abs(12)), array("%name%" => (isset($context[\'name\']) ? $context[\'name\'] : null), "%name%" => (isset($context[\'name\']) ? $context[\'name\'] : null), "%count%" => abs(12), ));');

// with escaper extension set to on
$body = new Twig_Node(array(
new Twig_Node_Text('J\'ai ', 0),
new Twig_Node_Print(new Twig_Node_Expression_Filter(new Twig_Node_Expression_Name('foo', 0), new Twig_Node_Expression_Constant('escape', 0), new Twig_Node(), 0), 0),
new Twig_Node_Text(' pommes', 0),
), array(), 0);

$node = new Twig_Extensions_Node_Trans($body, null, null, 0);
$tests[] = array($node, 'echo strtr(gettext("J\'ai %foo% pommes"), array("%foo%" => (isset($context[\'foo\']) ? $context[\'foo\'] : null), ));');

return $tests;
}
}

0 comments on commit 326a3ad

Please sign in to comment.