From 9fb3b72f1860f9d87f5b3eda7c52bc11fdf2c344 Mon Sep 17 00:00:00 2001 From: webchick <webchick@24967.no-reply.drupal.org> Date: Tue, 13 Aug 2013 01:53:11 -0700 Subject: [PATCH] =?UTF-8?q?Issue=20#2049241=20by=20Mark=20Carver,=20penyas?= =?UTF-8?q?kito,=20G=C3=A1bor=20Hojtsy:=20Change=20notice:=20Add=20support?= =?UTF-8?q?=20for=20language=20options=20to=20the=20Twig=20{%=20trans=20%}?= =?UTF-8?q?=20tag=20extension.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Drupal/Core/Template/TwigNodeTrans.php | 32 +++- .../Core/Template/TwigTransTokenParser.php | 12 +- .../system/Tests/Theme/TwigTransTest.php | 156 ++++++++++++------ .../templates/twig_theme_test.trans.html.twig | 33 ++++ 4 files changed, 172 insertions(+), 61 deletions(-) diff --git a/core/lib/Drupal/Core/Template/TwigNodeTrans.php b/core/lib/Drupal/Core/Template/TwigNodeTrans.php index ee32e98ad9cc..dddc273d9344 100644 --- a/core/lib/Drupal/Core/Template/TwigNodeTrans.php +++ b/core/lib/Drupal/Core/Template/TwigNodeTrans.php @@ -14,6 +14,8 @@ namespace Drupal\Core\Template; +use Drupal\Component\Utility\Unicode; + /** * A class that defines the Twig 'trans' tag for Drupal. */ @@ -22,11 +24,12 @@ class TwigNodeTrans extends \Twig_Node { /** * {@inheritdoc} */ - public function __construct(\Twig_NodeInterface $body, \Twig_NodeInterface $plural = NULL, \Twig_Node_Expression $count = NULL, $lineno, $tag = NULL) { + public function __construct(\Twig_NodeInterface $body, \Twig_NodeInterface $plural = NULL, \Twig_Node_Expression $count = NULL, \Twig_Node_Expression $options = NULL, $lineno, $tag = NULL) { parent::__construct(array( 'count' => $count, 'body' => $body, - 'plural' => $plural + 'plural' => $plural, + 'options' => $options, ), array(), $lineno, $tag); } @@ -36,6 +39,8 @@ public function __construct(\Twig_NodeInterface $body, \Twig_NodeInterface $plur public function compile(\Twig_Compiler $compiler) { $compiler->addDebugInfo($this); + $options = $this->getNode('options'); + list($singular, $tokens) = $this->compileString($this->getNode('body')); $plural = NULL; @@ -60,13 +65,17 @@ public function compile(\Twig_Compiler $compiler) { $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 any tokens found as an associative array parameter, otherwise just + // leave as an empty array. + $compiler->raw(', array('); + foreach ($tokens as $token) { + $compiler->string($token->getAttribute('placeholder'))->raw(' => ')->subcompile($token)->raw(', '); + } + $compiler->raw(')'); + + // Write any options passed. + if (!empty($options)) { + $compiler->raw(', ')->subcompile($options); } // Write function closure. @@ -79,6 +88,11 @@ public function compile(\Twig_Compiler $compiler) { if (!empty($plural)) { $compiler->raw(', PLURAL: ')->subcompile($plural); } + if (!empty($options)) { + foreach ($options->getKeyValuePairs() as $pair) { + $compiler->raw(', ' . Unicode::strtoupper($pair['key']->getAttribute('value')) . ': ')->subcompile($pair['value']); + } + } $compiler->raw(" -->\n'"); } diff --git a/core/lib/Drupal/Core/Template/TwigTransTokenParser.php b/core/lib/Drupal/Core/Template/TwigTransTokenParser.php index 44f6d2124f61..c67f4630f978 100644 --- a/core/lib/Drupal/Core/Template/TwigTransTokenParser.php +++ b/core/lib/Drupal/Core/Template/TwigTransTokenParser.php @@ -27,13 +27,19 @@ class TwigTransTokenParser extends \Twig_TokenParser { public function parse(\Twig_Token $token) { $lineno = $token->getLine(); $stream = $this->parser->getStream(); + $body = NULL; + $options = NULL; $count = NULL; $plural = NULL; - if (!$stream->test(\Twig_Token::BLOCK_END_TYPE)) { + if (!$stream->test(\Twig_Token::BLOCK_END_TYPE) && $stream->test(\Twig_Token::STRING_TYPE)) { $body = $this->parser->getExpressionParser()->parseExpression(); } - else { + if (!$stream->test(\Twig_Token::BLOCK_END_TYPE) && $stream->test(\Twig_Token::NAME_TYPE, 'with')) { + $stream->next(); + $options = $this->parser->getExpressionParser()->parseExpression(); + } + if (!$body) { $stream->expect(\Twig_Token::BLOCK_END_TYPE); $body = $this->parser->subparse(array($this, 'decideForFork')); if ('plural' === $stream->next()->getValue()) { @@ -47,7 +53,7 @@ public function parse(\Twig_Token $token) { $this->checkTransString($body, $lineno); - $node = new TwigNodeTrans($body, $plural, $count, $lineno, $this->getTag()); + $node = new TwigNodeTrans($body, $plural, $count, $options, $lineno, $this->getTag()); return $node; } diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/TwigTransTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigTransTest.php index a1ae6d87ff7b..a772a0612f2a 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Theme/TwigTransTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigTransTest.php @@ -34,18 +34,14 @@ class TwigTransTest extends WebTestBase { protected $admin_user; /** - * Custom language code. + * Custom languages. * - * @var string - */ - protected $langcode = 'xx'; - - /** - * Custom language name. - * - * @var string + * @var array */ - protected $name = 'Lolspeak'; + protected $languages = array( + 'xx' => 'Lolspeak', + 'zz' => 'Lolspeak2', + ); /** * Defines information about this test. @@ -80,33 +76,18 @@ protected function setUp() { )); $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, - )); + // Install languages. + $this->installLanguages(); - // Assign lolspeak to be the default language. - $edit = array('site_default_language' => $this->langcode); + // Assign Lolspeak (xx) to be the default language. + $edit = array('site_default_language' => 'xx'); $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'); + $this->assertEqual(language_default()->id, 'xx', 'Lolspeak is the default language'); } /** @@ -120,6 +101,11 @@ public function testTwigTransTags() { '{% trans "Hello sun." %} was successfully translated.' ); + $this->assertText( + 'O HAI SUNZZZZZZZ', + '{% trans "Hello sun." with {"context": "Lolspeak"} %} was successfully translated.' + ); + $this->assertText( 'O HERRO ERRRF.', '{{ "Hello Earth."|trans }} was successfully translated.' @@ -160,6 +146,26 @@ public function testTwigTransTags() { '{{ complex.tokens }} were successfully translated with appropriate prefixes.' ); + $this->assertText( + 'I have context.', + '{% trans %} with a context only msgid was excluded from translation.' + ); + + $this->assertText( + 'I HAZ KONTEX.', + '{% trans with {"context": "Lolspeak"} %} was successfully translated with context.' + ); + + $this->assertText( + 'O HAI NU TXT.', + '{% trans with {"langcode": "zz"} %} was successfully translated in specified language.' + ); + + $this->assertText( + 'O HAI NU TXTZZZZ.', + '{% trans with {"context": "Lolspeak", "langcode": "zz"} %} was successfully translated with context in specified language.' + ); + // Ensure debug output does not print. $this->checkForDebugMarkup(FALSE); } @@ -192,12 +198,16 @@ public function testTwigTransDebug() { protected function checkForDebugMarkup($visible) { $tests = array( '{% trans "Hello sun." %}' => '<!-- TRANSLATION: "Hello sun." -->', + '{% trans "Hello sun." with {"context": "Lolspeak"} %}' => '<!-- TRANSLATION: "Hello sun.", CONTEXT: "Lolspeak" -->', '{{ "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." -->', + '{% trans with {"context": "Lolspeak"} %}I have context.{% endtrans %}' => '<!-- TRANSLATION: "I have context.", CONTEXT: "Lolspeak" -->', + '{% trans with {"langcode": "zz"} %}Hello new text.{% endtrans %}' => '<!-- TRANSLATION: "Hello new text.", LANGCODE: "zz" -->', + '{% trans with {"context": "Lolspeak", "langcode": "zz"} %}Hello new text.{% endtrans %}' => '<!-- TRANSLATION: "Hello new text.", CONTEXT: "Lolspeak", LANGCODE: "zz" -->', ); foreach ($tests as $test => $markup) { if ($visible) { @@ -210,31 +220,51 @@ protected function checkForDebugMarkup($visible) { } /** - * 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. + * Helper function: install languages. */ - 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); + protected function installLanguages() { + foreach ($this->languages as $langcode => $name) { + // Generate custom .po contents for the language. + $contents = $this->poFileContents($langcode); + if ($contents) { + // Add test language for translation testing. + $edit = array( + 'predefined_langcode' => 'custom', + 'langcode' => $langcode, + 'name' => $name, + 'direction' => '0', + ); + + // Install the language in Drupal. + $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language')); + $this->assertRaw('"edit-languages-' . $langcode . '-weight"', 'Language code found.'); + + // Import the custom .po contents for the language. + $filename = tempnam('temporary://', "po_") . '.po'; + file_put_contents($filename, $contents); + $options = array( + 'files[file]' => $filename, + 'langcode' => $langcode, + 'customized' => TRUE, + ); + $this->drupalPost('admin/config/regional/translate/import', $options, t('Import')); + drupal_unlink($filename); + } + } } /** - * An example .po file. + * Generate a custom .po file for a specific test language. * - * @return string - * The .po contents used for this test. + * @param string $langcode + * The langcode of the specified language. + * + * @return string|FALSE + * The .po contents for the specified language or FALSE if none exists. */ - protected function examplePoFile() { - return <<< EOF + protected function poFileContents($langcode) { + if ($langcode === 'xx') { + return <<< EOF msgid "" msgstr "" "Project-Id-Version: Drupal 8\\n" @@ -246,6 +276,10 @@ protected function examplePoFile() { msgid "Hello sun." msgstr "OH HAI SUNZ" +msgctxt "Lolspeak" +msgid "Hello sun." +msgstr "O HAI SUNZZZZZZZ" + msgid "Hello Earth." msgstr "O HERRO ERRRF." @@ -268,7 +302,31 @@ protected function examplePoFile() { 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." + +msgctxt "Lolspeak" +msgid "I have context." +msgstr "I HAZ KONTEX." +EOF; + } + else if ($langcode === 'zz') { + 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 new text." +msgstr "O HAI NU TXT." + +msgctxt "Lolspeak" +msgid "Hello new text." +msgstr "O HAI NU TXTZZZZ." EOF; + } + return FALSE; } } diff --git a/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.trans.html.twig b/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.trans.html.twig index f487e5a4fcfc..2f4dda0336d6 100644 --- a/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.trans.html.twig +++ b/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.trans.html.twig @@ -3,6 +3,11 @@ {% trans 'Hello sun.' %} </div> +{# Test trans tag with string argument and context #} +<div> + {% trans 'Hello sun.' with {'context': 'Lolspeak'} %} +</div> + {# Test trans filter. #} <div> {{ 'Hello Earth.'|trans }} @@ -61,3 +66,31 @@ 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> + +{# Test trans tag but with a context only msgid. #} +<div> + {% trans %} + I have context. + {% endtrans %} +</div> + +{# Test trans tag with context. #} +<div> + {% trans with {'context': 'Lolspeak'} %} + I have context. + {% endtrans %} +</div> + +{# Test trans tag with a specified language. #} +<div> + {% trans with {'langcode': 'zz'} %} + Hello new text. + {% endtrans %} +</div> + +{# Test trans tag with context and a specified language. #} +<div> + {% trans with {'context': 'Lolspeak', 'langcode': 'zz'} %} + Hello new text. + {% endtrans %} +</div> -- GitLab