diff --git a/src/Bridges/ApplicationLatte/Nodes/TranslateNode.php b/src/Bridges/ApplicationLatte/Nodes/TranslateNode.php index 6928ea19c..48b2c707d 100644 --- a/src/Bridges/ApplicationLatte/Nodes/TranslateNode.php +++ b/src/Bridges/ApplicationLatte/Nodes/TranslateNode.php @@ -9,14 +9,16 @@ namespace Nette\Bridges\ApplicationLatte\Nodes; +use Latte\Compiler\NodeHelpers; use Latte\Compiler\Nodes\AreaNode; use Latte\Compiler\Nodes\NopNode; use Latte\Compiler\Nodes\Php; use Latte\Compiler\Nodes\Php\ModifierNode; use Latte\Compiler\Nodes\StatementNode; +use Latte\Compiler\Nodes\TextNode; use Latte\Compiler\PrintContext; use Latte\Compiler\Tag; -use Latte\Helpers; +use Nette\Localization\Translator; /** @@ -29,7 +31,7 @@ class TranslateNode extends StatementNode /** @return \Generator */ - public static function create(Tag $tag): \Generator + public static function create(Tag $tag, ?Translator $translator): \Generator { $tag->outputMode = $tag::OutputKeepIndentation; @@ -42,14 +44,30 @@ public static function create(Tag $tag): \Generator } [$node->content] = yield; + + if ($text = NodeHelpers::toText($node->content)) { + $node->content = new TextNode($text); + if ($translator) { + try { + $translation = $translator->translate($text, ...NodeHelpers::toValue($args, constants: true)); + if (is_string($translation)) { + $node->content = new TextNode($translation); + return $node; + } + } catch (\InvalidArgumentException) { + } + } + } + array_unshift($node->modifier->filters, new Php\FilterNode(new Php\IdentifierNode('translate'), $args->toArguments())); + return $node; } public function print(PrintContext $context): string { - if ($text = Helpers::toTextualContent($this->content)) { + if ($this->content instanceof TextNode) { return $context->format( <<<'XX' $ʟ_fi = new LR\FilterInfo(%dump); @@ -57,7 +75,7 @@ public function print(PrintContext $context): string XX, $context->getEscaper()->export(), $this->modifier, - $text, + $this->content->content, $this->position, ); diff --git a/src/Bridges/ApplicationLatte/Template.php b/src/Bridges/ApplicationLatte/Template.php index 6429969f5..2f599572a 100644 --- a/src/Bridges/ApplicationLatte/Template.php +++ b/src/Bridges/ApplicationLatte/Template.php @@ -105,7 +105,7 @@ public function addFunction(string $name, callable $callback) * Sets translate adapter. * @return static */ - public function setTranslator(?Nette\Localization\Translator $translator) + public function setTranslator(?Nette\Localization\Translator $translator, ?string $language = null) { if (version_compare(Latte\Engine::VERSION, '3', '<')) { $this->latte->addFilter( @@ -117,7 +117,7 @@ function (Latte\Runtime\FilterInfo $fi, ...$args) use ($translator): string { } ); } else { - $this->latte->addExtension(new TranslatorExtension($translator)); + $this->latte->addExtension(new TranslatorExtension($translator, $language)); } return $this; } diff --git a/src/Bridges/ApplicationLatte/TranslatorExtension.php b/src/Bridges/ApplicationLatte/TranslatorExtension.php index e79c9036c..2eeb52261 100644 --- a/src/Bridges/ApplicationLatte/TranslatorExtension.php +++ b/src/Bridges/ApplicationLatte/TranslatorExtension.php @@ -10,8 +10,10 @@ namespace Nette\Bridges\ApplicationLatte; use Latte; +use Latte\Compiler\NodeHelpers; use Latte\Compiler\Nodes\Php; use Latte\Compiler\Tag; +use Latte\Engine; use Latte\Essential\Nodes\PrintNode; use Nette\Localization\Translator; @@ -23,6 +25,7 @@ final class TranslatorExtension extends Latte\Extension { public function __construct( private ?Translator $translator, + private ?string $key = null, ) { } @@ -31,7 +34,7 @@ public function getTags(): array { return [ '_' => [$this, 'parseTranslate'], - 'translate' => [Nodes\TranslateNode::class, 'create'], + 'translate' => fn(Tag $tag): \Generator => Nodes\TranslateNode::create($tag, $this->key ? $this->translator : null), ]; } @@ -46,6 +49,12 @@ public function getFilters(): array } + public function getCacheKey(Engine $engine): mixed + { + return $this->key; + } + + /** * {_ ...} */ @@ -59,8 +68,24 @@ public function parseTranslate(Tag $tag): PrintNode if ($tag->parser->stream->tryConsume(',')) { $args = $tag->parser->parseArguments(); } + $node->modifier = $tag->parser->parseModifier(); $node->modifier->escape = true; + + if ($this->translator && $this->key) { + try { + $translation = $this->translator->translate( + NodeHelpers::toValue($node->expression, constants: true), + ...NodeHelpers::toValue($args, constants: true), + ); + if (is_string($translation)) { + $node->expression = new Php\Scalar\StringNode($translation); + return $node; + } + } catch (\InvalidArgumentException) { + } + } + array_unshift($node->modifier->filters, new Php\FilterNode(new Php\IdentifierNode('translate'), $args->toArguments())); return $node; } diff --git a/tests/Bridges.Latte3/{_var}.phpt b/tests/Bridges.Latte3/{_var}.phpt index 11d483602..fc5d1c4dd 100644 --- a/tests/Bridges.Latte3/{_var}.phpt +++ b/tests/Bridges.Latte3/{_var}.phpt @@ -4,6 +4,7 @@ declare(strict_types=1); +use Nette\Localization\Translator; use Tester\Assert; require __DIR__ . '/../bootstrap.php'; @@ -31,3 +32,35 @@ Assert::contains( 'echo LR\Filters::escapeHtmlText(($this->filters->translate)($var, 10, 20)) /* line 1 */;', $latte->compile('{_$var, 10, 20}'), ); + + +class MyTranslator implements Translator +{ + public function translate($message, ...$parameters): string + { + return strrev($message) . implode(',', $parameters); + } +} + +$latte = new Latte\Engine; +$latte->setLoader(new Latte\Loaders\StringLoader); +$latte->addExtension(new Nette\Bridges\ApplicationLatte\TranslatorExtension(new MyTranslator)); +Assert::contains( + 'echo LR\Filters::escapeHtmlText(($this->filters->translate)(\'a&b\', 1, 2))', + $latte->compile('{_"a&b", 1, 2}'), +); +Assert::same( + 'b&a1,2', + $latte->renderToString('{_"a&b", 1, 2}'), +); + + +$latte->addExtension(new Nette\Bridges\ApplicationLatte\TranslatorExtension(new MyTranslator, 'en')); +Assert::contains( + 'echo LR\Filters::escapeHtmlText(\'b&a1,2\')', + $latte->compile('{_"a&b", 1, 2}'), +); +Assert::same( + 'b&a1,2', + $latte->renderToString('{_"a&b", 1, 2}'), +); diff --git a/tests/Bridges.Latte3/{translate}.phpt b/tests/Bridges.Latte3/{translate}.phpt index 3cd4d8d3f..26397f8d8 100644 --- a/tests/Bridges.Latte3/{translate}.phpt +++ b/tests/Bridges.Latte3/{translate}.phpt @@ -4,6 +4,7 @@ declare(strict_types=1); +use Nette\Localization\Translator; use Tester\Assert; require __DIR__ . '/../bootstrap.php'; @@ -65,3 +66,35 @@ Assert::notContains( "'translate'", $latte->compile('{translate /}'), ); + + +class MyTranslator implements Translator +{ + public function translate($message, ...$parameters): string + { + return strrev($message) . implode(',', $parameters); + } +} + +$latte = new Latte\Engine; +$latte->setLoader(new Latte\Loaders\StringLoader); +$latte->addExtension(new Nette\Bridges\ApplicationLatte\TranslatorExtension(new MyTranslator)); +Assert::contains( + 'echo LR\Filters::convertTo($ʟ_fi, \'html\', $this->filters->filterContent(\'translate\', $ʟ_fi, \'a&b\', 1, 2))', + $latte->compile('{translate 1,2}a&b{/translate}'), +); +Assert::same( + 'b&a1,2', + $latte->renderToString('{translate 1,2}a&b{/translate}'), +); + + +$latte->addExtension(new Nette\Bridges\ApplicationLatte\TranslatorExtension(new MyTranslator, 'en')); +Assert::contains( + 'echo LR\Filters::convertTo($ʟ_fi, \'html\', \'b&a1,2\')', + $latte->compile('{translate 1,2}a&b{/translate}'), +); +Assert::same( + 'b&a1,2', + $latte->renderToString('{translate 1,2}a&b{/translate}'), +);