Issue #2049241 by Mark Carver, penyaskito, Gábor Hojtsy: Change notice: Add support for language options to the Twig {% trans %} tag extension.
parent
a6b26016ad
commit
9fb3b72f18
|
@ -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 @@ class TwigNodeTrans extends \Twig_Node {
|
|||
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 @@ class TwigNodeTrans extends \Twig_Node {
|
|||
$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 @@ class TwigNodeTrans extends \Twig_Node {
|
|||
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'");
|
||||
}
|
||||
|
||||
|
|
|
@ -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 @@ class TwigTransTokenParser extends \Twig_TokenParser {
|
|||
|
||||
$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;
|
||||
}
|
||||
|
|
|
@ -34,18 +34,14 @@ class TwigTransTest extends WebTestBase {
|
|||
protected $admin_user;
|
||||
|
||||
/**
|
||||
* Custom language code.
|
||||
* Custom languages.
|
||||
*
|
||||
* @var string
|
||||
* @var array
|
||||
*/
|
||||
protected $langcode = 'xx';
|
||||
|
||||
/**
|
||||
* Custom language name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'Lolspeak';
|
||||
protected $languages = array(
|
||||
'xx' => 'Lolspeak',
|
||||
'zz' => 'Lolspeak2',
|
||||
);
|
||||
|
||||
/**
|
||||
* Defines information about this test.
|
||||
|
@ -80,33 +76,18 @@ class TwigTransTest extends WebTestBase {
|
|||
));
|
||||
$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 languages.
|
||||
$this->installLanguages();
|
||||
|
||||
// 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);
|
||||
// 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 @@ class TwigTransTest extends WebTestBase {
|
|||
'{% 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 @@ class TwigTransTest extends WebTestBase {
|
|||
'{{ 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 @@ class TwigTransTest extends WebTestBase {
|
|||
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 @@ class TwigTransTest extends WebTestBase {
|
|||
}
|
||||
|
||||
/**
|
||||
* 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 @@ msgstr ""
|
|||
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 @@ 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."
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue