feature #39352 [TwigBridge] export concatenated translations (Stephen)

This PR was submitted for the 4.4 branch but it was squashed and merged into the 5.3-dev branch instead.

Discussion
----------

[TwigBridge] export concatenated translations

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | Fix #39351
| License       | MIT
| Doc PR        |

This PR will fix that concatenated strings are ignored by the translation:update command. Suppose you have a twig file like this:
```
{{ ('aa' ~ 'bb') | trans }}
```
This is not exported when using the command. This PR will fix that.

Commits
-------

122eaba746 [TwigBridge] export concatenated translations
This commit is contained in:
Fabien Potencier 2020-12-10 08:04:07 +01:00
commit b82bc85786
2 changed files with 48 additions and 0 deletions

View File

@ -13,6 +13,7 @@ namespace Symfony\Bridge\Twig\NodeVisitor;
use Symfony\Bridge\Twig\Node\TransNode;
use Twig\Environment;
use Twig\Node\Expression\Binary\ConcatBinary;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\FilterExpression;
use Twig\Node\Expression\FunctionExpression;
@ -87,6 +88,16 @@ final class TranslationNodeVisitor extends AbstractNodeVisitor
$node->getNode('body')->getAttribute('data'),
$node->hasNode('domain') ? $this->getReadDomainFromNode($node->getNode('domain')) : null,
];
} elseif (
$node instanceof FilterExpression &&
'trans' === $node->getNode('filter')->getAttribute('value') &&
$node->getNode('node') instanceof ConcatBinary &&
$message = $this->getConcatValueFromNode($node->getNode('node'), null)
) {
$this->messages[] = [
$message,
$this->getReadDomainFromArguments($node->getNode('arguments'), 1),
];
}
return $node;
@ -151,4 +162,28 @@ final class TranslationNodeVisitor extends AbstractNodeVisitor
return self::UNDEFINED_DOMAIN;
}
private function getConcatValueFromNode(Node $node, ?string $value): ?string
{
if ($node instanceof ConcatBinary) {
foreach ($node as $nextNode) {
if ($nextNode instanceof ConcatBinary) {
$nextValue = $this->getConcatValueFromNode($nextNode, $value);
if (null === $nextValue) {
return null;
}
$value .= $nextValue;
} elseif ($nextNode instanceof ConstantExpression) {
$value .= $nextNode->getAttribute('value');
} else {
// this is a node we cannot process (variable, or translation in translation)
return null;
}
}
} elseif ($node instanceof ConstantExpression) {
$value .= $node->getAttribute('value');
}
return $value;
}
}

View File

@ -43,6 +43,10 @@ class TwigExtractorTest extends TestCase
$m->setAccessible(true);
$m->invoke($extractor, $template, $catalogue);
if (0 === \count($messages)) {
$this->assertSame($catalogue->all(), $messages);
}
foreach ($messages as $key => $domain) {
$this->assertTrue($catalogue->has($key, $domain));
$this->assertEquals('prefix'.$key, $catalogue->get($key, $domain));
@ -70,6 +74,15 @@ class TwigExtractorTest extends TestCase
// make sure this works with twig's named arguments
['{{ "new key" | trans(domain="domain") }}', ['new key' => 'domain']],
// concat translations
['{{ ("new" ~ " key") | trans() }}', ['new key' => 'messages']],
['{{ ("another " ~ "new " ~ "key") | trans() }}', ['another new key' => 'messages']],
['{{ ("new" ~ " key") | trans(domain="domain") }}', ['new key' => 'domain']],
['{{ ("another " ~ "new " ~ "key") | trans(domain="domain") }}', ['another new key' => 'domain']],
// if it has a variable or other expression, we can not extract it
['{% set foo = "new" %} {{ ("new " ~ foo ~ "key") | trans() }}', []],
['{{ ("foo " ~ "new"|trans ~ "key") | trans() }}', ['new' => 'messages']],
];
}