Issue #1927584 by Mark Carver, ezeedub, drupalninja99, Cottser, geoffreyr, John Bickar, jenlampton, penyaskito: Add support for the Twig {% trans %} tag extension.
parent
e35e083e84
commit
2460eb7c77
|
@ -32,6 +32,14 @@ class TwigExtension extends \Twig_Extension {
|
|||
public function getFilters() {
|
||||
return array(
|
||||
't' => new \Twig_Filter_Function('t'),
|
||||
'trans' => new \Twig_Filter_Function('t'),
|
||||
// The "raw" filter is not detectable when parsing "trans" tags. To detect
|
||||
// which prefix must be used for translation (@, !, %), we must clone the
|
||||
// "raw" filter and give it identifiable names. These filters should only
|
||||
// be used in "trans" tags.
|
||||
// @see TwigNodeTrans::compileString()
|
||||
'passthrough' => new \Twig_Filter_Function('twig_raw_filter'),
|
||||
'placeholder' => new \Twig_Filter_Function('twig_raw_filter'),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -47,6 +55,7 @@ class TwigExtension extends \Twig_Extension {
|
|||
return array(
|
||||
new TwigFunctionTokenParser('hide'),
|
||||
new TwigFunctionTokenParser('show'),
|
||||
new TwigTransTokenParser(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Template\TwigNodeTrans.
|
||||
*
|
||||
* This Twig extension was originally based on Twig i18n extension. It has been
|
||||
* severely modified to work properly with the complexities of the Drupal
|
||||
* translation system.
|
||||
*
|
||||
* @see http://twig.sensiolabs.org/doc/extensions/i18n.html
|
||||
* @see https://github.com/fabpot/Twig-extensions
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Template;
|
||||
|
||||
/**
|
||||
* A class that defines the Twig 'trans' tag for Drupal.
|
||||
*/
|
||||
class TwigNodeTrans extends \Twig_Node {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function compile(\Twig_Compiler $compiler) {
|
||||
$compiler->addDebugInfo($this);
|
||||
|
||||
list($singular, $tokens) = $this->compileString($this->getNode('body'));
|
||||
$plural = NULL;
|
||||
|
||||
if (NULL !== $this->getNode('plural')) {
|
||||
list($plural, $pluralTokens) = $this->compileString($this->getNode('plural'));
|
||||
$tokens = array_merge($tokens, $pluralTokens);
|
||||
}
|
||||
|
||||
// Start writing with the function to be called.
|
||||
$compiler->write('echo ' . (empty($plural) ? 't' : 'format_plural') . '(');
|
||||
|
||||
// Move the count to the beginning of the parameters list.
|
||||
if (!empty($plural)) {
|
||||
$compiler->raw('abs(')->subcompile($this->getNode('count'))->raw('), ');
|
||||
}
|
||||
|
||||
// Write the singular text parameter.
|
||||
$compiler->subcompile($singular);
|
||||
|
||||
// Write the plural text parameter, if necessary.
|
||||
if (!empty($plural)) {
|
||||
$compiler->raw(', ')->subcompile($plural);
|
||||
}
|
||||
|
||||
// Write any tokens found as an associative array parameter.
|
||||
if (!empty($tokens)) {
|
||||
$compiler->raw(', array(');
|
||||
foreach ($tokens as $token) {
|
||||
$compiler->string($token->getAttribute('placeholder'))->raw(' => ')->subcompile($token)->raw(', ');
|
||||
}
|
||||
$compiler->raw(')');
|
||||
}
|
||||
|
||||
// Write function closure.
|
||||
$compiler->raw(')');
|
||||
|
||||
// Append translation debug markup, if necessary.
|
||||
if (settings()->get('twig_debug', FALSE)) {
|
||||
$compiler->raw(" . '\n<!-- TRANSLATION: ");
|
||||
$compiler->subcompile($singular);
|
||||
if (!empty($plural)) {
|
||||
$compiler->raw(', PLURAL: ')->subcompile($plural);
|
||||
}
|
||||
$compiler->raw(" -->\n'");
|
||||
}
|
||||
|
||||
// End writing.
|
||||
$compiler->raw(";\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the text and tokens for the "trans" tag.
|
||||
*
|
||||
* @param \Twig_NodeInterface $body
|
||||
* The node to compile.
|
||||
*
|
||||
* @return array
|
||||
* Returns an array containing the two following parameters:
|
||||
* - string $text
|
||||
* The extracted text.
|
||||
* - array $tokens
|
||||
* The extracted tokens as new \Twig_Node_Expression_Name instances.
|
||||
*/
|
||||
protected function compileString(\Twig_NodeInterface $body) {
|
||||
if ($body instanceof \Twig_Node_Expression_Name || $body instanceof \Twig_Node_Expression_Constant || $body instanceof \Twig_Node_Expression_TempName) {
|
||||
return array($body, array());
|
||||
}
|
||||
|
||||
$tokens = array();
|
||||
if (count($body)) {
|
||||
$text = '';
|
||||
|
||||
foreach ($body as $node) {
|
||||
if (get_class($node) === 'Twig_Node' && $node->getNode(0) instanceof \Twig_Node_SetTemp) {
|
||||
$node = $node->getNode(1);
|
||||
}
|
||||
|
||||
if ($node instanceof \Twig_Node_Print) {
|
||||
$n = $node->getNode('expr');
|
||||
while ($n instanceof \Twig_Node_Expression_Filter) {
|
||||
$n = $n->getNode('node');
|
||||
}
|
||||
$args = $n->getNode('arguments')->getNode(0);
|
||||
|
||||
// Detect if a token implements one of the filters reserved for
|
||||
// modifying the prefix of a token. The default prefix used for
|
||||
// translations is "@". This escapes the printed token and makes them
|
||||
// safe for templates.
|
||||
// @see TwigExtension::getFilters()
|
||||
$argPrefix = '@';
|
||||
while ($args instanceof \Twig_Node_Expression_Filter) {
|
||||
switch ($args->getNode('filter')->getAttribute('value')) {
|
||||
case 'passthrough':
|
||||
$argPrefix = '!';
|
||||
break;
|
||||
case 'placeholder':
|
||||
$argPrefix = '%';
|
||||
break;
|
||||
}
|
||||
$args = $args->getNode('node');
|
||||
}
|
||||
if ($args instanceof \Twig_Node_Expression_GetAttr) {
|
||||
$argName = $args->getNode('attribute')->getAttribute('value');
|
||||
$expr = $n;
|
||||
}
|
||||
else {
|
||||
$argName = $n->getAttribute('name');
|
||||
if (!is_null($args)) {
|
||||
$argName = $args->getAttribute('name');
|
||||
}
|
||||
$expr = new \Twig_Node_Expression_Name($argName, $n->getLine());
|
||||
}
|
||||
$placeholder = sprintf('%s%s', $argPrefix, $argName);
|
||||
$text .= $placeholder;
|
||||
$expr->setAttribute('placeholder', $placeholder);
|
||||
$tokens[] = $expr;
|
||||
}
|
||||
else {
|
||||
$text .= $node->getAttribute('data');
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$text = $body->getAttribute('data');
|
||||
}
|
||||
|
||||
return array(new \Twig_Node(array(new \Twig_Node_Expression_Constant(trim($text), $body->getLine()))), $tokens);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Template\TwigTransTokenParser.
|
||||
*
|
||||
* @see http://twig.sensiolabs.org/doc/extensions/i18n.html
|
||||
* @see https://github.com/fabpot/Twig-extensions
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Template;
|
||||
|
||||
/**
|
||||
* A class that defines the Twig 'trans' token parser for Drupal.
|
||||
*
|
||||
* The token parser converts a token stream created from template source
|
||||
* code into an Abstract Syntax Tree (AST). The AST will later be compiled
|
||||
* into PHP code usable for runtime execution of the template.
|
||||
*
|
||||
* @see \Twig_TokenParser
|
||||
*/
|
||||
class TwigTransTokenParser extends \Twig_TokenParser {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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);
|
||||
|
||||
$node = new TwigNodeTrans($body, $plural, $count, $lineno, $this->getTag());
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect a 'plural' switch or the end of a 'trans' tag.
|
||||
*/
|
||||
public function decideForFork($token) {
|
||||
return $token->test(array('plural', 'endtrans'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the end of a 'trans' tag.
|
||||
*/
|
||||
public function decideForEnd($token) {
|
||||
return $token->test('endtrans');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTag() {
|
||||
return 'trans';
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that any nodes that are parsed are only of allowed types.
|
||||
*
|
||||
* @param \Twig_NodeInterface $body
|
||||
* The expression to check.
|
||||
* @param integer $lineno
|
||||
* The source line.
|
||||
*
|
||||
* @throws \Twig_Error_Syntax
|
||||
*/
|
||||
protected function checkTransString(\Twig_NodeInterface $body, $lineno) {
|
||||
foreach ($body as $node) {
|
||||
if (
|
||||
$node instanceof \Twig_Node_Text
|
||||
||
|
||||
($node instanceof \Twig_Node_Print && $node->getNode('expr') instanceof \Twig_Node_Expression_Name)
|
||||
||
|
||||
($node instanceof \Twig_Node_Print && $node->getNode('expr') instanceof \Twig_Node_Expression_GetAttr)
|
||||
||
|
||||
($node instanceof \Twig_Node_Print && $node->getNode('expr') instanceof \Twig_Node_Expression_Filter)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
throw new \Twig_Error_Syntax(sprintf('The text to be translated with "trans" can only contain references to simple variables'), $lineno);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,274 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\TwigTransTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests Twig "trans" tags.
|
||||
*/
|
||||
class TwigTransTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array(
|
||||
'theme_test',
|
||||
'twig_theme_test',
|
||||
'locale',
|
||||
'language'
|
||||
);
|
||||
|
||||
/**
|
||||
* An administrative user for testing.
|
||||
*
|
||||
* @var \Drupal\user\Plugin\Core\Entity\User
|
||||
*/
|
||||
protected $admin_user;
|
||||
|
||||
/**
|
||||
* Custom language code.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $langcode = 'xx';
|
||||
|
||||
/**
|
||||
* Custom language name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'Lolspeak';
|
||||
|
||||
/**
|
||||
* Defines information about this test.
|
||||
*
|
||||
* @return array
|
||||
* An associative array of information.
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Twig Translation',
|
||||
'description' => 'Test Twig "trans" tags.',
|
||||
'group' => 'Theme',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Setup test_theme.
|
||||
theme_enable(array('test_theme'));
|
||||
\Drupal::config('system.theme')->set('default', 'test_theme')->save();
|
||||
|
||||
// Create and log in as admin.
|
||||
$this->admin_user = $this->drupalCreateUser(array(
|
||||
'administer languages',
|
||||
'access administration pages',
|
||||
'administer site configuration',
|
||||
'translate interface'
|
||||
));
|
||||
$this->drupalLogin($this->admin_user);
|
||||
|
||||
// Add test language for translation testing.
|
||||
$edit = array(
|
||||
'predefined_langcode' => 'custom',
|
||||
'langcode' => $this->langcode,
|
||||
'name' => $this->name,
|
||||
'direction' => '0',
|
||||
);
|
||||
|
||||
// Install the lolspeak language.
|
||||
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
|
||||
$this->assertRaw('"edit-languages-' . $this->langcode . '-weight"', 'Language code found.');
|
||||
|
||||
// Import a custom .po file for the lolspeak language.
|
||||
$this->importPoFile($this->examplePoFile(), array(
|
||||
'langcode' => $this->langcode,
|
||||
'customized' => TRUE,
|
||||
));
|
||||
|
||||
// Assign lolspeak to be the default language.
|
||||
$edit = array('site_default_language' => $this->langcode);
|
||||
$this->drupalPost('admin/config/regional/settings', $edit, t('Save configuration'));
|
||||
|
||||
// Reset the static cache of the language list.
|
||||
drupal_static_reset('language_list');
|
||||
|
||||
// Check that lolspeak is the default language for the site.
|
||||
$this->assertEqual(language_default()->id, $this->langcode, $this->name . ' is the default language');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Twig "trans" tags.
|
||||
*/
|
||||
public function testTwigTransTags() {
|
||||
$this->drupalGet('twig-theme-test/trans', array('language' => language_load('xx')));
|
||||
|
||||
$this->assertText(
|
||||
'OH HAI SUNZ',
|
||||
'{% trans "Hello sun." %} was successfully translated.'
|
||||
);
|
||||
|
||||
$this->assertText(
|
||||
'O HERRO ERRRF.',
|
||||
'{{ "Hello Earth."|trans }} was successfully translated.'
|
||||
);
|
||||
|
||||
$this->assertText(
|
||||
'OH HAI TEH MUUN',
|
||||
'{% trans %}Hello moon.{% endtrans %} was successfully translated.'
|
||||
);
|
||||
|
||||
$this->assertText(
|
||||
'O HAI STARRRRR',
|
||||
'{% trans %} with {% plural count = 1 %} was successfully translated.'
|
||||
);
|
||||
|
||||
$this->assertText(
|
||||
'O HAI 2 STARZZZZ',
|
||||
'{% trans %} with {% plural count = 2 %} was successfully translated.'
|
||||
);
|
||||
|
||||
$this->assertRaw(
|
||||
'ESCAPEE: &"<>',
|
||||
'{{ token }} was successfully translated and prefixed with "@".'
|
||||
);
|
||||
|
||||
$this->assertRaw(
|
||||
'PAS-THRU: &"<>',
|
||||
'{{ token|passthrough }} was successfully translated and prefixed with "!".'
|
||||
);
|
||||
|
||||
$this->assertRaw(
|
||||
'PLAYSHOLDR: <em class="placeholder">&"<></em>',
|
||||
'{{ token|placeholder }} was successfully translated and prefixed with "%".'
|
||||
);
|
||||
|
||||
$this->assertRaw(
|
||||
'DIS complex token HAZ LENGTH OV: 3. IT CONTAYNZ: <em class="placeholder">12345</em> AN &"<>. LETS PAS TEH BAD TEXT THRU: &"<>.',
|
||||
'{{ complex.tokens }} were successfully translated with appropriate prefixes.'
|
||||
);
|
||||
|
||||
// Ensure debug output does not print.
|
||||
$this->checkForDebugMarkup(FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Twig "trans" debug markup.
|
||||
*/
|
||||
public function testTwigTransDebug() {
|
||||
// Enable twig debug and write to the test settings.php file.
|
||||
$this->settingsSet('twig_debug', TRUE);
|
||||
$settings['settings']['twig_debug'] = (object) array(
|
||||
'value' => TRUE,
|
||||
'required' => TRUE,
|
||||
);
|
||||
$this->writeSettings($settings);
|
||||
|
||||
// Get page for assertion testing.
|
||||
$this->drupalGet('twig-theme-test/trans', array('language' => language_load('xx')));
|
||||
|
||||
// Ensure debug output is printed.
|
||||
$this->checkForDebugMarkup(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function: test twig debug translation markup.
|
||||
*
|
||||
* @param bool $visible
|
||||
* Toggle determining which assertion to use for test.
|
||||
*/
|
||||
protected function checkForDebugMarkup($visible) {
|
||||
$tests = array(
|
||||
'{% trans "Hello sun." %}' => '<!-- TRANSLATION: "Hello sun." -->',
|
||||
'{{ "Hello moon."|trans }}' => '<!-- TRANSLATION: "Hello moon." -->',
|
||||
'{% trans %} with {% plural %}' => '<!-- TRANSLATION: "Hello star.", PLURAL: "Hello @count stars." -->',
|
||||
'{{ token }}' => '<!-- TRANSLATION: "Escaped: @string" -->',
|
||||
'{{ token|passthrough }}' => '<!-- TRANSLATION: "Pass-through: !string" -->',
|
||||
'{{ token|placeholder }}' => '<!-- TRANSLATION: "Placeholder: %string" -->',
|
||||
'{{ complex.tokens }}' => '<!-- TRANSLATION: "This @name has a length of: @count. It contains: %numbers and @bad_text. Lets pass the bad text through: !bad_text." -->',
|
||||
);
|
||||
foreach ($tests as $test => $markup) {
|
||||
if ($visible) {
|
||||
$this->assertRaw($markup, "Twig debug translation markup exists in source for: $test");
|
||||
}
|
||||
else {
|
||||
$this->assertNoRaw($markup, "Twig debug translation markup does not exist in source for: $test");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function: import a standalone .po file in a given language.
|
||||
*
|
||||
* Borrowed from \Drupal\locale\Tests\LocaleImportFunctionalTest.
|
||||
*
|
||||
* @param string $contents
|
||||
* Contents of the .po file to import.
|
||||
* @param array $options
|
||||
* Additional options to pass to the translation import form.
|
||||
*/
|
||||
protected function importPoFile($contents, array $options = array()) {
|
||||
$name = tempnam('temporary://', "po_") . '.po';
|
||||
file_put_contents($name, $contents);
|
||||
$options['files[file]'] = $name;
|
||||
$this->drupalPost('admin/config/regional/translate/import', $options, t('Import'));
|
||||
drupal_unlink($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* An example .po file.
|
||||
*
|
||||
* @return string
|
||||
* The .po contents used for this test.
|
||||
*/
|
||||
protected function examplePoFile() {
|
||||
return <<< EOF
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Drupal 8\\n"
|
||||
"MIME-Version: 1.0\\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\\n"
|
||||
"Content-Transfer-Encoding: 8bit\\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\\n"
|
||||
|
||||
msgid "Hello sun."
|
||||
msgstr "OH HAI SUNZ"
|
||||
|
||||
msgid "Hello Earth."
|
||||
msgstr "O HERRO ERRRF."
|
||||
|
||||
msgid "Hello moon."
|
||||
msgstr "OH HAI TEH MUUN"
|
||||
|
||||
msgid "Hello star."
|
||||
msgid_plural "Hello @count stars."
|
||||
msgstr[0] "O HAI STARRRRR"
|
||||
msgstr[1] "O HAI @count STARZZZZ"
|
||||
|
||||
msgid "Escaped: @string"
|
||||
msgstr "ESCAPEE: @string"
|
||||
|
||||
msgid "Pass-through: !string"
|
||||
msgstr "PAS-THRU: !string"
|
||||
|
||||
msgid "Placeholder: %string"
|
||||
msgstr "PLAYSHOLDR: %string"
|
||||
|
||||
msgid "This @name has a length of: @count. It contains: %numbers and @bad_text. Lets pass the bad text through: !bad_text."
|
||||
msgstr "DIS @name HAZ LENGTH OV: @count. IT CONTAYNZ: %numbers AN @bad_text. LETS PAS TEH BAD TEXT THRU: !bad_text."
|
||||
EOF;
|
||||
}
|
||||
|
||||
}
|
|
@ -29,4 +29,13 @@ class TwigThemeTestController implements ControllerInterface {
|
|||
return theme('twig_theme_test_php_variables');
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback for testing translation blocks in a Twig template.
|
||||
*/
|
||||
public function transBlockRender() {
|
||||
return array(
|
||||
'#theme' => 'twig_theme_test_trans',
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
{# Test trans tag with string argument. #}
|
||||
<div>
|
||||
{% trans 'Hello sun.' %}
|
||||
</div>
|
||||
|
||||
{# Test trans filter. #}
|
||||
<div>
|
||||
{{ 'Hello Earth.'|trans }}
|
||||
</div>
|
||||
|
||||
{# Test trans tag with text content. #}
|
||||
<div>
|
||||
{% trans %}
|
||||
Hello moon.
|
||||
{% endtrans %}
|
||||
</div>
|
||||
|
||||
{# Test trans/plural tag where count = 1. #}
|
||||
<div>
|
||||
{% set count = 1 %}
|
||||
{% trans %}
|
||||
Hello star.
|
||||
{% plural count %}
|
||||
Hello {{ count }} stars.
|
||||
{% endtrans %}
|
||||
</div>
|
||||
|
||||
{# Test trans/plural tag where count = 2. #}
|
||||
<div>
|
||||
{% set count = 2 %}
|
||||
{% trans %}
|
||||
Hello star.
|
||||
{% plural count %}
|
||||
Hello {{ count }} stars.
|
||||
{% endtrans %}
|
||||
</div>
|
||||
|
||||
{# Test trans tag with different filters applied to tokens. #}
|
||||
{% set string = '&"<>' %}
|
||||
<div>
|
||||
{% trans %}
|
||||
Escaped: {{ string }}
|
||||
{% endtrans %}
|
||||
</div>
|
||||
<div>
|
||||
{% trans %}
|
||||
Pass-through: {{ string|passthrough }}
|
||||
{% endtrans %}
|
||||
</div>
|
||||
<div>
|
||||
{% trans %}
|
||||
Placeholder: {{ string|placeholder }}
|
||||
{% endtrans %}
|
||||
</div>
|
||||
|
||||
{# Test trans tag with complex tokens. #}
|
||||
{% set token = {'name': 'complex token', 'numbers': '12345', 'bad_text': '&"<>' } %}
|
||||
{% set count = token|length %}
|
||||
<div>
|
||||
{% trans %}
|
||||
This {{ token.name }} has a length of: {{ count }}. It contains: {{ token.numbers|placeholder }} and {{ token.bad_text }}. Lets pass the bad text through: {{ token.bad_text|passthrough }}.
|
||||
{% endtrans %}
|
||||
</div>
|
|
@ -7,6 +7,10 @@ function twig_theme_test_theme($existing, $type, $theme, $path) {
|
|||
$items['twig_theme_test_php_variables'] = array(
|
||||
'template' => 'twig_theme_test.php_variables',
|
||||
);
|
||||
$items['twig_theme_test_trans'] = array(
|
||||
'variables' => array(),
|
||||
'template' => 'twig_theme_test.trans',
|
||||
);
|
||||
return $items;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,3 +4,9 @@ twig_theme_test_php_variables:
|
|||
_content: '\Drupal\twig_theme_test\TwigThemeTestController::phpVariablesRender'
|
||||
requirements:
|
||||
_permission: 'access content'
|
||||
twig_theme_test_trans:
|
||||
pattern: '/twig-theme-test/trans'
|
||||
defaults:
|
||||
_content: '\Drupal\twig_theme_test\TwigThemeTestController::transBlockRender'
|
||||
requirements:
|
||||
_permission: 'access content'
|
||||
|
|
Loading…
Reference in New Issue