[Translation] Translatable objects
This commit is contained in:
parent
6c094cce73
commit
dc6b3bf62d
@ -5,6 +5,9 @@ CHANGELOG
|
|||||||
-----
|
-----
|
||||||
|
|
||||||
* added the `workflow_transition()` function to easily retrieve a specific transition object
|
* added the `workflow_transition()` function to easily retrieve a specific transition object
|
||||||
|
* added support for translating `Translatable` objects
|
||||||
|
* added the `t()` function to easily create `Translatable` objects
|
||||||
|
* Added support for extracting messages from the `t()` function
|
||||||
|
|
||||||
5.0.0
|
5.0.0
|
||||||
-----
|
-----
|
||||||
|
@ -15,10 +15,12 @@ use Symfony\Bridge\Twig\NodeVisitor\TranslationDefaultDomainNodeVisitor;
|
|||||||
use Symfony\Bridge\Twig\NodeVisitor\TranslationNodeVisitor;
|
use Symfony\Bridge\Twig\NodeVisitor\TranslationNodeVisitor;
|
||||||
use Symfony\Bridge\Twig\TokenParser\TransDefaultDomainTokenParser;
|
use Symfony\Bridge\Twig\TokenParser\TransDefaultDomainTokenParser;
|
||||||
use Symfony\Bridge\Twig\TokenParser\TransTokenParser;
|
use Symfony\Bridge\Twig\TokenParser\TransTokenParser;
|
||||||
|
use Symfony\Component\Translation\Translatable;
|
||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
use Symfony\Contracts\Translation\TranslatorTrait;
|
use Symfony\Contracts\Translation\TranslatorTrait;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Extension\AbstractExtension;
|
||||||
use Twig\TwigFilter;
|
use Twig\TwigFilter;
|
||||||
|
use Twig\TwigFunction;
|
||||||
|
|
||||||
// Help opcache.preload discover always-needed symbols
|
// Help opcache.preload discover always-needed symbols
|
||||||
class_exists(TranslatorInterface::class);
|
class_exists(TranslatorInterface::class);
|
||||||
@ -54,6 +56,16 @@ final class TranslationExtension extends AbstractExtension
|
|||||||
return $this->translator;
|
return $this->translator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getFunctions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new TwigFunction('t', [$this, 'createTranslatable']),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
@ -91,8 +103,17 @@ final class TranslationExtension extends AbstractExtension
|
|||||||
return $this->translationNodeVisitor ?: $this->translationNodeVisitor = new TranslationNodeVisitor();
|
return $this->translationNodeVisitor ?: $this->translationNodeVisitor = new TranslationNodeVisitor();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function trans(?string $message, array $arguments = [], string $domain = null, string $locale = null, int $count = null): string
|
/**
|
||||||
|
* @param ?string|Translatable $message The message id (may also be an object that can be cast to string)
|
||||||
|
*/
|
||||||
|
public function trans($message, array $arguments = [], string $domain = null, string $locale = null, int $count = null): string
|
||||||
{
|
{
|
||||||
|
if ($message instanceof Translatable) {
|
||||||
|
$arguments += $message->getParameters();
|
||||||
|
$domain = $message->getDomain();
|
||||||
|
$message = $message->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
if (null === $message || '' === $message) {
|
if (null === $message || '' === $message) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@ -103,4 +124,9 @@ final class TranslationExtension extends AbstractExtension
|
|||||||
|
|
||||||
return $this->getTranslator()->trans($message, $arguments, $domain, $locale);
|
return $this->getTranslator()->trans($message, $arguments, $domain, $locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function createTranslatable(string $message, array $parameters = [], string $domain = 'messages'): Translatable
|
||||||
|
{
|
||||||
|
return new Translatable($message, $parameters, $domain);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ use Symfony\Bridge\Twig\Node\TransNode;
|
|||||||
use Twig\Environment;
|
use Twig\Environment;
|
||||||
use Twig\Node\Expression\ConstantExpression;
|
use Twig\Node\Expression\ConstantExpression;
|
||||||
use Twig\Node\Expression\FilterExpression;
|
use Twig\Node\Expression\FilterExpression;
|
||||||
|
use Twig\Node\Expression\FunctionExpression;
|
||||||
use Twig\Node\Node;
|
use Twig\Node\Node;
|
||||||
use Twig\NodeVisitor\AbstractNodeVisitor;
|
use Twig\NodeVisitor\AbstractNodeVisitor;
|
||||||
|
|
||||||
@ -66,6 +67,20 @@ final class TranslationNodeVisitor extends AbstractNodeVisitor
|
|||||||
$node->getNode('node')->getAttribute('value'),
|
$node->getNode('node')->getAttribute('value'),
|
||||||
$this->getReadDomainFromArguments($node->getNode('arguments'), 1),
|
$this->getReadDomainFromArguments($node->getNode('arguments'), 1),
|
||||||
];
|
];
|
||||||
|
} elseif (
|
||||||
|
$node instanceof FilterExpression &&
|
||||||
|
'trans' === $node->getNode('filter')->getAttribute('value') &&
|
||||||
|
$node->getNode('node') instanceof FunctionExpression &&
|
||||||
|
't' === $node->getNode('node')->getAttribute('name')
|
||||||
|
) {
|
||||||
|
$nodeArguments = $node->getNode('node')->getNode('arguments');
|
||||||
|
|
||||||
|
if ($nodeArguments->getIterator()->current() instanceof ConstantExpression) {
|
||||||
|
$this->messages[] = [
|
||||||
|
$this->getReadMessageFromArguments($nodeArguments, 0),
|
||||||
|
$this->getReadDomainFromArguments($nodeArguments, 2),
|
||||||
|
];
|
||||||
|
}
|
||||||
} elseif (
|
} elseif (
|
||||||
$node instanceof FilterExpression &&
|
$node instanceof FilterExpression &&
|
||||||
'transchoice' === $node->getNode('filter')->getAttribute('value') &&
|
'transchoice' === $node->getNode('filter')->getAttribute('value') &&
|
||||||
@ -103,6 +118,28 @@ final class TranslationNodeVisitor extends AbstractNodeVisitor
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getReadMessageFromArguments(Node $arguments, int $index): ?string
|
||||||
|
{
|
||||||
|
if ($arguments->hasNode('message')) {
|
||||||
|
$argument = $arguments->getNode('message');
|
||||||
|
} elseif ($arguments->hasNode($index)) {
|
||||||
|
$argument = $arguments->getNode($index);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->getReadMessageFromNode($argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getReadMessageFromNode(Node $node): ?string
|
||||||
|
{
|
||||||
|
if ($node instanceof ConstantExpression) {
|
||||||
|
return $node->getAttribute('value');
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private function getReadDomainFromArguments(Node $arguments, int $index): ?string
|
private function getReadDomainFromArguments(Node $arguments, int $index): ?string
|
||||||
{
|
{
|
||||||
if ($arguments->hasNode('domain')) {
|
if ($arguments->hasNode('domain')) {
|
||||||
|
@ -122,6 +122,23 @@ class TranslationExtensionTest extends TestCase
|
|||||||
// trans filter with null message
|
// trans filter with null message
|
||||||
['{{ null|trans }}', ''],
|
['{{ null|trans }}', ''],
|
||||||
['{{ foo|trans }}', '', ['foo' => null]],
|
['{{ foo|trans }}', '', ['foo' => null]],
|
||||||
|
|
||||||
|
// trans object
|
||||||
|
['{{ t("Hello")|trans }}', 'Hello'],
|
||||||
|
['{{ t(name)|trans }}', 'Symfony', ['name' => 'Symfony']],
|
||||||
|
['{{ t(hello)|trans({ \'%name%\': \'Symfony\' }) }}', 'Hello Symfony', ['hello' => 'Hello %name%']],
|
||||||
|
['{{ t(hello, { \'%name%\': \'Symfony\' })|trans }}', 'Hello Symfony', ['hello' => 'Hello %name%']],
|
||||||
|
['{{ t(hello, { \'%name%\': \'Another Name\' })|trans({ \'%name%\': \'Symfony\' }) }}', 'Hello Symfony', ['hello' => 'Hello %name%']],
|
||||||
|
['{% set vars = { \'%name%\': \'Symfony\' } %}{{ t(hello)|trans(vars) }}', 'Hello Symfony', ['hello' => 'Hello %name%']],
|
||||||
|
['{% set vars = { \'%name%\': \'Symfony\' } %}{{ t(hello, vars)|trans }}', 'Hello Symfony', ['hello' => 'Hello %name%']],
|
||||||
|
['{{ t("Hello")|trans(locale="fr") }}', 'Hello'],
|
||||||
|
['{{ t("Hello", {}, "messages")|trans(locale="fr") }}', 'Hello'],
|
||||||
|
|
||||||
|
// trans object with count
|
||||||
|
['{{ t("{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples")|trans(count=count) }}', 'There is 5 apples', ['count' => 5]],
|
||||||
|
['{{ t(text)|trans(count=5, arguments={\'%name%\': \'Symfony\'}) }}', 'There is 5 apples (Symfony)', ['text' => '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples (%name%)']],
|
||||||
|
['{{ t(text, {\'%name%\': \'Symfony\'})|trans(count=5) }}', 'There is 5 apples (Symfony)', ['text' => '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples (%name%)']],
|
||||||
|
['{{ t("{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples", {}, "messages")|trans(locale="fr", count=count) }}', 'There is 5 apples', ['count' => 5]],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +60,9 @@ class TwigExtractorTest extends TestCase
|
|||||||
['{% trans from "domain" %}new key{% endtrans %}', ['new key' => 'domain']],
|
['{% trans from "domain" %}new key{% endtrans %}', ['new key' => 'domain']],
|
||||||
['{% set foo = "new key" | trans %}', ['new key' => 'messages']],
|
['{% set foo = "new key" | trans %}', ['new key' => 'messages']],
|
||||||
['{{ 1 ? "new key" | trans : "another key" | trans }}', ['new key' => 'messages', 'another key' => 'messages']],
|
['{{ 1 ? "new key" | trans : "another key" | trans }}', ['new key' => 'messages', 'another key' => 'messages']],
|
||||||
|
['{{ t("new key") | trans() }}', ['new key' => 'messages']],
|
||||||
|
['{{ t("new key", {}, "domain") | trans() }}', ['new key' => 'domain']],
|
||||||
|
['{{ 1 ? t("new key") | trans : t("another key") | trans }}', ['new key' => 'messages', 'another key' => 'messages']],
|
||||||
|
|
||||||
// make sure 'trans_default_domain' tag is supported
|
// make sure 'trans_default_domain' tag is supported
|
||||||
['{% trans_default_domain "domain" %}{{ "new key"|trans }}', ['new key' => 'domain']],
|
['{% trans_default_domain "domain" %}{{ "new key"|trans }}', ['new key' => 'domain']],
|
||||||
|
@ -5,6 +5,9 @@ CHANGELOG
|
|||||||
-----
|
-----
|
||||||
|
|
||||||
* added `PseudoLocalizationTranslator`
|
* added `PseudoLocalizationTranslator`
|
||||||
|
* added `Translatable` objects that represent a message that can be translated
|
||||||
|
* added the `t()` function to easily create `Translatable` objects
|
||||||
|
* Added support for extracting messages from `Translatable` objects
|
||||||
|
|
||||||
5.1.0
|
5.1.0
|
||||||
-----
|
-----
|
||||||
|
@ -54,6 +54,66 @@ class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
|
|||||||
'(',
|
'(',
|
||||||
self::MESSAGE_TOKEN,
|
self::MESSAGE_TOKEN,
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'new',
|
||||||
|
'Translatable',
|
||||||
|
'(',
|
||||||
|
self::MESSAGE_TOKEN,
|
||||||
|
',',
|
||||||
|
self::METHOD_ARGUMENTS_TOKEN,
|
||||||
|
',',
|
||||||
|
self::DOMAIN_TOKEN,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'new',
|
||||||
|
'Translatable',
|
||||||
|
'(',
|
||||||
|
self::MESSAGE_TOKEN,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'new',
|
||||||
|
'\\',
|
||||||
|
'Symfony',
|
||||||
|
'\\',
|
||||||
|
'Component',
|
||||||
|
'\\',
|
||||||
|
'Translation',
|
||||||
|
'\\',
|
||||||
|
'Translatable',
|
||||||
|
'(',
|
||||||
|
self::MESSAGE_TOKEN,
|
||||||
|
',',
|
||||||
|
self::METHOD_ARGUMENTS_TOKEN,
|
||||||
|
',',
|
||||||
|
self::DOMAIN_TOKEN,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'new',
|
||||||
|
'\\',
|
||||||
|
'Symfony',
|
||||||
|
'\\',
|
||||||
|
'Component',
|
||||||
|
'\\',
|
||||||
|
'Translation',
|
||||||
|
'\\',
|
||||||
|
'Translatable',
|
||||||
|
'(',
|
||||||
|
self::MESSAGE_TOKEN,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
't',
|
||||||
|
'(',
|
||||||
|
self::MESSAGE_TOKEN,
|
||||||
|
',',
|
||||||
|
self::METHOD_ARGUMENTS_TOKEN,
|
||||||
|
',',
|
||||||
|
self::DOMAIN_TOKEN,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
't',
|
||||||
|
'(',
|
||||||
|
self::MESSAGE_TOKEN,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Symfony\Component\Translation\Translatable;
|
||||||
|
|
||||||
|
if (!function_exists('t')) {
|
||||||
|
/**
|
||||||
|
* @author Nate Wiebe <nate@northern.co>
|
||||||
|
*/
|
||||||
|
function t(string $message, array $parameters = [], string $domain = 'messages'): Translatable
|
||||||
|
{
|
||||||
|
return new Translatable($message, $parameters, $domain);
|
||||||
|
}
|
||||||
|
}
|
@ -41,6 +41,39 @@ EOF;
|
|||||||
// Assert
|
// Assert
|
||||||
$expectedCatalogue = [
|
$expectedCatalogue = [
|
||||||
'messages' => [
|
'messages' => [
|
||||||
|
'translatable single-quoted key' => 'prefixtranslatable single-quoted key',
|
||||||
|
'translatable double-quoted key' => 'prefixtranslatable double-quoted key',
|
||||||
|
'translatable heredoc key' => 'prefixtranslatable heredoc key',
|
||||||
|
'translatable nowdoc key' => 'prefixtranslatable nowdoc key',
|
||||||
|
"translatable double-quoted key with whitespace and escaped \$\n\" sequences" => "prefixtranslatable double-quoted key with whitespace and escaped \$\n\" sequences",
|
||||||
|
'translatable single-quoted key with whitespace and nonescaped \$\n\' sequences' => 'prefixtranslatable single-quoted key with whitespace and nonescaped \$\n\' sequences',
|
||||||
|
'translatable single-quoted key with "quote mark at the end"' => 'prefixtranslatable single-quoted key with "quote mark at the end"',
|
||||||
|
'translatable '.$expectedHeredoc => 'prefixtranslatable '.$expectedHeredoc,
|
||||||
|
'translatable '.$expectedNowdoc => 'prefixtranslatable '.$expectedNowdoc,
|
||||||
|
'translatable concatenated message with heredoc and nowdoc' => 'prefixtranslatable concatenated message with heredoc and nowdoc',
|
||||||
|
'translatable default domain' => 'prefixtranslatable default domain',
|
||||||
|
'translatable-fqn single-quoted key' => 'prefixtranslatable-fqn single-quoted key',
|
||||||
|
'translatable-fqn double-quoted key' => 'prefixtranslatable-fqn double-quoted key',
|
||||||
|
'translatable-fqn heredoc key' => 'prefixtranslatable-fqn heredoc key',
|
||||||
|
'translatable-fqn nowdoc key' => 'prefixtranslatable-fqn nowdoc key',
|
||||||
|
"translatable-fqn double-quoted key with whitespace and escaped \$\n\" sequences" => "prefixtranslatable-fqn double-quoted key with whitespace and escaped \$\n\" sequences",
|
||||||
|
'translatable-fqn single-quoted key with whitespace and nonescaped \$\n\' sequences' => 'prefixtranslatable-fqn single-quoted key with whitespace and nonescaped \$\n\' sequences',
|
||||||
|
'translatable-fqn single-quoted key with "quote mark at the end"' => 'prefixtranslatable-fqn single-quoted key with "quote mark at the end"',
|
||||||
|
'translatable-fqn '.$expectedHeredoc => 'prefixtranslatable-fqn '.$expectedHeredoc,
|
||||||
|
'translatable-fqn '.$expectedNowdoc => 'prefixtranslatable-fqn '.$expectedNowdoc,
|
||||||
|
'translatable-fqn concatenated message with heredoc and nowdoc' => 'prefixtranslatable-fqn concatenated message with heredoc and nowdoc',
|
||||||
|
'translatable-fqn default domain' => 'prefixtranslatable-fqn default domain',
|
||||||
|
'translatable-short single-quoted key' => 'prefixtranslatable-short single-quoted key',
|
||||||
|
'translatable-short double-quoted key' => 'prefixtranslatable-short double-quoted key',
|
||||||
|
'translatable-short heredoc key' => 'prefixtranslatable-short heredoc key',
|
||||||
|
'translatable-short nowdoc key' => 'prefixtranslatable-short nowdoc key',
|
||||||
|
"translatable-short double-quoted key with whitespace and escaped \$\n\" sequences" => "prefixtranslatable-short double-quoted key with whitespace and escaped \$\n\" sequences",
|
||||||
|
'translatable-short single-quoted key with whitespace and nonescaped \$\n\' sequences' => 'prefixtranslatable-short single-quoted key with whitespace and nonescaped \$\n\' sequences',
|
||||||
|
'translatable-short single-quoted key with "quote mark at the end"' => 'prefixtranslatable-short single-quoted key with "quote mark at the end"',
|
||||||
|
'translatable-short '.$expectedHeredoc => 'prefixtranslatable-short '.$expectedHeredoc,
|
||||||
|
'translatable-short '.$expectedNowdoc => 'prefixtranslatable-short '.$expectedNowdoc,
|
||||||
|
'translatable-short concatenated message with heredoc and nowdoc' => 'prefixtranslatable-short concatenated message with heredoc and nowdoc',
|
||||||
|
'translatable-short default domain' => 'prefixtranslatable-short default domain',
|
||||||
'single-quoted key' => 'prefixsingle-quoted key',
|
'single-quoted key' => 'prefixsingle-quoted key',
|
||||||
'double-quoted key' => 'prefixdouble-quoted key',
|
'double-quoted key' => 'prefixdouble-quoted key',
|
||||||
'heredoc key' => 'prefixheredoc key',
|
'heredoc key' => 'prefixheredoc key',
|
||||||
@ -54,6 +87,21 @@ EOF;
|
|||||||
'default domain' => 'prefixdefault domain',
|
'default domain' => 'prefixdefault domain',
|
||||||
],
|
],
|
||||||
'not_messages' => [
|
'not_messages' => [
|
||||||
|
'translatable other-domain-test-no-params-short-array' => 'prefixtranslatable other-domain-test-no-params-short-array',
|
||||||
|
'translatable other-domain-test-no-params-long-array' => 'prefixtranslatable other-domain-test-no-params-long-array',
|
||||||
|
'translatable other-domain-test-params-short-array' => 'prefixtranslatable other-domain-test-params-short-array',
|
||||||
|
'translatable other-domain-test-params-long-array' => 'prefixtranslatable other-domain-test-params-long-array',
|
||||||
|
'translatable typecast' => 'prefixtranslatable typecast',
|
||||||
|
'translatable-fqn other-domain-test-no-params-short-array' => 'prefixtranslatable-fqn other-domain-test-no-params-short-array',
|
||||||
|
'translatable-fqn other-domain-test-no-params-long-array' => 'prefixtranslatable-fqn other-domain-test-no-params-long-array',
|
||||||
|
'translatable-fqn other-domain-test-params-short-array' => 'prefixtranslatable-fqn other-domain-test-params-short-array',
|
||||||
|
'translatable-fqn other-domain-test-params-long-array' => 'prefixtranslatable-fqn other-domain-test-params-long-array',
|
||||||
|
'translatable-fqn typecast' => 'prefixtranslatable-fqn typecast',
|
||||||
|
'translatable-short other-domain-test-no-params-short-array' => 'prefixtranslatable-short other-domain-test-no-params-short-array',
|
||||||
|
'translatable-short other-domain-test-no-params-long-array' => 'prefixtranslatable-short other-domain-test-no-params-long-array',
|
||||||
|
'translatable-short other-domain-test-params-short-array' => 'prefixtranslatable-short other-domain-test-params-short-array',
|
||||||
|
'translatable-short other-domain-test-params-long-array' => 'prefixtranslatable-short other-domain-test-params-long-array',
|
||||||
|
'translatable-short typecast' => 'prefixtranslatable-short typecast',
|
||||||
'other-domain-test-no-params-short-array' => 'prefixother-domain-test-no-params-short-array',
|
'other-domain-test-no-params-short-array' => 'prefixother-domain-test-no-params-short-array',
|
||||||
'other-domain-test-no-params-long-array' => 'prefixother-domain-test-no-params-long-array',
|
'other-domain-test-no-params-long-array' => 'prefixother-domain-test-no-params-long-array',
|
||||||
'other-domain-test-params-short-array' => 'prefixother-domain-test-params-short-array',
|
'other-domain-test-params-short-array' => 'prefixother-domain-test-params-short-array',
|
||||||
@ -65,6 +113,18 @@ EOF;
|
|||||||
|
|
||||||
$this->assertEquals($expectedCatalogue, $actualCatalogue);
|
$this->assertEquals($expectedCatalogue, $actualCatalogue);
|
||||||
|
|
||||||
|
$filename = str_replace(\DIRECTORY_SEPARATOR, '/', __DIR__).'/../fixtures/extractor/translatable.html.php';
|
||||||
|
$this->assertEquals(['sources' => [$filename.':2']], $catalogue->getMetadata('translatable single-quoted key'));
|
||||||
|
$this->assertEquals(['sources' => [$filename.':37']], $catalogue->getMetadata('translatable other-domain-test-no-params-short-array', 'not_messages'));
|
||||||
|
|
||||||
|
$filename = str_replace(\DIRECTORY_SEPARATOR, '/', __DIR__).'/../fixtures/extractor/translatable-fqn.html.php';
|
||||||
|
$this->assertEquals(['sources' => [$filename.':2']], $catalogue->getMetadata('translatable-fqn single-quoted key'));
|
||||||
|
$this->assertEquals(['sources' => [$filename.':37']], $catalogue->getMetadata('translatable-fqn other-domain-test-no-params-short-array', 'not_messages'));
|
||||||
|
|
||||||
|
$filename = str_replace(\DIRECTORY_SEPARATOR, '/', __DIR__).'/../fixtures/extractor/translatable-short.html.php';
|
||||||
|
$this->assertEquals(['sources' => [$filename.':2']], $catalogue->getMetadata('translatable-short single-quoted key'));
|
||||||
|
$this->assertEquals(['sources' => [$filename.':37']], $catalogue->getMetadata('translatable-short other-domain-test-no-params-short-array', 'not_messages'));
|
||||||
|
|
||||||
$filename = str_replace(\DIRECTORY_SEPARATOR, '/', __DIR__).'/../fixtures/extractor/translation.html.php';
|
$filename = str_replace(\DIRECTORY_SEPARATOR, '/', __DIR__).'/../fixtures/extractor/translation.html.php';
|
||||||
$this->assertEquals(['sources' => [$filename.':2']], $catalogue->getMetadata('single-quoted key'));
|
$this->assertEquals(['sources' => [$filename.':2']], $catalogue->getMetadata('single-quoted key'));
|
||||||
$this->assertEquals(['sources' => [$filename.':37']], $catalogue->getMetadata('other-domain-test-no-params-short-array', 'not_messages'));
|
$this->assertEquals(['sources' => [$filename.':37']], $catalogue->getMetadata('other-domain-test-no-params-short-array', 'not_messages'));
|
||||||
@ -73,20 +133,21 @@ EOF;
|
|||||||
public function resourcesProvider()
|
public function resourcesProvider()
|
||||||
{
|
{
|
||||||
$directory = __DIR__.'/../fixtures/extractor/';
|
$directory = __DIR__.'/../fixtures/extractor/';
|
||||||
|
$phpFiles = [];
|
||||||
$splFiles = [];
|
$splFiles = [];
|
||||||
foreach (new \DirectoryIterator($directory) as $fileInfo) {
|
foreach (new \DirectoryIterator($directory) as $fileInfo) {
|
||||||
if ($fileInfo->isDot()) {
|
if ($fileInfo->isDot()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ('translation.html.php' === $fileInfo->getBasename()) {
|
if (\in_array($fileInfo->getBasename(), ['translatable.html.php', 'translatable-fqn.html.php', 'translatable-short.html.php', 'translation.html.php'], true)) {
|
||||||
$phpFile = $fileInfo->getPathname();
|
$phpFiles[] = $fileInfo->getPathname();
|
||||||
}
|
}
|
||||||
$splFiles[] = $fileInfo->getFileInfo();
|
$splFiles[] = $fileInfo->getFileInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
[$directory],
|
[$directory],
|
||||||
[$phpFile],
|
[$phpFiles],
|
||||||
[glob($directory.'*')],
|
[glob($directory.'*')],
|
||||||
[$splFiles],
|
[$splFiles],
|
||||||
[new \ArrayObject(glob($directory.'*'))],
|
[new \ArrayObject(glob($directory.'*'))],
|
||||||
|
75
src/Symfony/Component/Translation/Tests/TranslatableTest.php
Normal file
75
src/Symfony/Component/Translation/Tests/TranslatableTest.php
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Translation\Tests;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\Translation\Loader\ArrayLoader;
|
||||||
|
use Symfony\Component\Translation\Translatable;
|
||||||
|
use Symfony\Component\Translation\Translator;
|
||||||
|
|
||||||
|
class TranslatableTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @dataProvider getTransTests
|
||||||
|
*/
|
||||||
|
public function testTrans($expected, $translatable, $translation, $locale)
|
||||||
|
{
|
||||||
|
$translator = new Translator('en');
|
||||||
|
$translator->addLoader('array', new ArrayLoader());
|
||||||
|
$translator->addResource('array', [$translatable->getMessage() => $translation], $locale, $translatable->getDomain());
|
||||||
|
|
||||||
|
$this->assertEquals($expected, Translatable::trans($translator, $translatable, $locale));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getFlattenedTransTests
|
||||||
|
*/
|
||||||
|
public function testFlattenedTrans($expected, $messages, $translatable)
|
||||||
|
{
|
||||||
|
$translator = new Translator('en');
|
||||||
|
$translator->addLoader('array', new ArrayLoader());
|
||||||
|
$translator->addResource('array', $messages, 'fr', '');
|
||||||
|
|
||||||
|
$this->assertEquals($expected, Translatable::trans($translator, $translatable, 'fr'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTransTests()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['Symfony est super !', new Translatable('Symfony is great!', [], ''), 'Symfony est super !', 'fr'],
|
||||||
|
['Symfony est awesome !', new Translatable('Symfony is %what%!', ['%what%' => 'awesome'], ''), 'Symfony est %what% !', 'fr'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFlattenedTransTests()
|
||||||
|
{
|
||||||
|
$messages = [
|
||||||
|
'symfony' => [
|
||||||
|
'is' => [
|
||||||
|
'great' => 'Symfony est super!',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'foo' => [
|
||||||
|
'bar' => [
|
||||||
|
'baz' => 'Foo Bar Baz',
|
||||||
|
],
|
||||||
|
'baz' => 'Foo Baz',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
return [
|
||||||
|
['Symfony est super!', $messages, new Translatable('symfony.is.great', [], '')],
|
||||||
|
['Foo Bar Baz', $messages, new Translatable('foo.bar.baz', [], '')],
|
||||||
|
['Foo Baz', $messages, new Translatable('foo.baz', [], '')],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
47
src/Symfony/Component/Translation/Tests/fixtures/extractor/translatable-fqn.html.php
vendored
Normal file
47
src/Symfony/Component/Translation/Tests/fixtures/extractor/translatable-fqn.html.php
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
This template is used for translation message extraction tests
|
||||||
|
<?php new \Symfony\Component\Translation\Translatable('translatable-fqn single-quoted key'); ?>
|
||||||
|
<?php new \Symfony\Component\Translation\Translatable('translatable-fqn double-quoted key'); ?>
|
||||||
|
<?php new \Symfony\Component\Translation\Translatable(<<<EOF
|
||||||
|
translatable-fqn heredoc key
|
||||||
|
EOF
|
||||||
|
); ?>
|
||||||
|
<?php new \Symfony\Component\Translation\Translatable(<<<'EOF'
|
||||||
|
translatable-fqn nowdoc key
|
||||||
|
EOF
|
||||||
|
); ?>
|
||||||
|
<?php new \Symfony\Component\Translation\Translatable(
|
||||||
|
"translatable-fqn double-quoted key with whitespace and escaped \$\n\" sequences"
|
||||||
|
); ?>
|
||||||
|
<?php new \Symfony\Component\Translation\Translatable(
|
||||||
|
'translatable-fqn single-quoted key with whitespace and nonescaped \$\n\' sequences'
|
||||||
|
); ?>
|
||||||
|
<?php new \Symfony\Component\Translation\Translatable(<<<EOF
|
||||||
|
translatable-fqn heredoc key with whitespace and escaped \$\n sequences
|
||||||
|
EOF
|
||||||
|
); ?>
|
||||||
|
<?php new \Symfony\Component\Translation\Translatable(<<<'EOF'
|
||||||
|
translatable-fqn nowdoc key with whitespace and nonescaped \$\n sequences
|
||||||
|
EOF
|
||||||
|
); ?>
|
||||||
|
|
||||||
|
<?php new \Symfony\Component\Translation\Translatable('translatable-fqn single-quoted key with "quote mark at the end"'); ?>
|
||||||
|
|
||||||
|
<?php new \Symfony\Component\Translation\Translatable('translatable-fqn concatenated'.' message'.<<<EOF
|
||||||
|
with heredoc
|
||||||
|
EOF
|
||||||
|
.<<<'EOF'
|
||||||
|
and nowdoc
|
||||||
|
EOF
|
||||||
|
); ?>
|
||||||
|
|
||||||
|
<?php new \Symfony\Component\Translation\Translatable('translatable-fqn other-domain-test-no-params-short-array', [], 'not_messages'); ?>
|
||||||
|
|
||||||
|
<?php new \Symfony\Component\Translation\Translatable('translatable-fqn other-domain-test-no-params-long-array', [], 'not_messages'); ?>
|
||||||
|
|
||||||
|
<?php new \Symfony\Component\Translation\Translatable('translatable-fqn other-domain-test-params-short-array', ['foo' => 'bar'], 'not_messages'); ?>
|
||||||
|
|
||||||
|
<?php new \Symfony\Component\Translation\Translatable('translatable-fqn other-domain-test-params-long-array', ['foo' => 'bar'], 'not_messages'); ?>
|
||||||
|
|
||||||
|
<?php new \Symfony\Component\Translation\Translatable('translatable-fqn typecast', ['a' => (int) '123'], 'not_messages'); ?>
|
||||||
|
|
||||||
|
<?php new \Symfony\Component\Translation\Translatable('translatable-fqn default domain', [], null); ?>
|
47
src/Symfony/Component/Translation/Tests/fixtures/extractor/translatable-short.html.php
vendored
Normal file
47
src/Symfony/Component/Translation/Tests/fixtures/extractor/translatable-short.html.php
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
This template is used for translation message extraction tests
|
||||||
|
<?php t('translatable-short single-quoted key'); ?>
|
||||||
|
<?php t('translatable-short double-quoted key'); ?>
|
||||||
|
<?php t(<<<EOF
|
||||||
|
translatable-short heredoc key
|
||||||
|
EOF
|
||||||
|
); ?>
|
||||||
|
<?php t(<<<'EOF'
|
||||||
|
translatable-short nowdoc key
|
||||||
|
EOF
|
||||||
|
); ?>
|
||||||
|
<?php t(
|
||||||
|
"translatable-short double-quoted key with whitespace and escaped \$\n\" sequences"
|
||||||
|
); ?>
|
||||||
|
<?php t(
|
||||||
|
'translatable-short single-quoted key with whitespace and nonescaped \$\n\' sequences'
|
||||||
|
); ?>
|
||||||
|
<?php t(<<<EOF
|
||||||
|
translatable-short heredoc key with whitespace and escaped \$\n sequences
|
||||||
|
EOF
|
||||||
|
); ?>
|
||||||
|
<?php t(<<<'EOF'
|
||||||
|
translatable-short nowdoc key with whitespace and nonescaped \$\n sequences
|
||||||
|
EOF
|
||||||
|
); ?>
|
||||||
|
|
||||||
|
<?php t('translatable-short single-quoted key with "quote mark at the end"'); ?>
|
||||||
|
|
||||||
|
<?php t('translatable-short concatenated'.' message'.<<<EOF
|
||||||
|
with heredoc
|
||||||
|
EOF
|
||||||
|
.<<<'EOF'
|
||||||
|
and nowdoc
|
||||||
|
EOF
|
||||||
|
); ?>
|
||||||
|
|
||||||
|
<?php t('translatable-short other-domain-test-no-params-short-array', [], 'not_messages'); ?>
|
||||||
|
|
||||||
|
<?php t('translatable-short other-domain-test-no-params-long-array', [], 'not_messages'); ?>
|
||||||
|
|
||||||
|
<?php t('translatable-short other-domain-test-params-short-array', ['foo' => 'bar'], 'not_messages'); ?>
|
||||||
|
|
||||||
|
<?php t('translatable-short other-domain-test-params-long-array', ['foo' => 'bar'], 'not_messages'); ?>
|
||||||
|
|
||||||
|
<?php t('translatable-short typecast', ['a' => (int) '123'], 'not_messages'); ?>
|
||||||
|
|
||||||
|
<?php t('translatable-short default domain', [], null); ?>
|
47
src/Symfony/Component/Translation/Tests/fixtures/extractor/translatable.html.php
vendored
Normal file
47
src/Symfony/Component/Translation/Tests/fixtures/extractor/translatable.html.php
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
This template is used for translation message extraction tests
|
||||||
|
<?php new Translatable('translatable single-quoted key'); ?>
|
||||||
|
<?php new Translatable('translatable double-quoted key'); ?>
|
||||||
|
<?php new Translatable(<<<EOF
|
||||||
|
translatable heredoc key
|
||||||
|
EOF
|
||||||
|
); ?>
|
||||||
|
<?php new Translatable(<<<'EOF'
|
||||||
|
translatable nowdoc key
|
||||||
|
EOF
|
||||||
|
); ?>
|
||||||
|
<?php new Translatable(
|
||||||
|
"translatable double-quoted key with whitespace and escaped \$\n\" sequences"
|
||||||
|
); ?>
|
||||||
|
<?php new Translatable(
|
||||||
|
'translatable single-quoted key with whitespace and nonescaped \$\n\' sequences'
|
||||||
|
); ?>
|
||||||
|
<?php new Translatable(<<<EOF
|
||||||
|
translatable heredoc key with whitespace and escaped \$\n sequences
|
||||||
|
EOF
|
||||||
|
); ?>
|
||||||
|
<?php new Translatable(<<<'EOF'
|
||||||
|
translatable nowdoc key with whitespace and nonescaped \$\n sequences
|
||||||
|
EOF
|
||||||
|
); ?>
|
||||||
|
|
||||||
|
<?php new Translatable('translatable single-quoted key with "quote mark at the end"'); ?>
|
||||||
|
|
||||||
|
<?php new Translatable('translatable concatenated'.' message'.<<<EOF
|
||||||
|
with heredoc
|
||||||
|
EOF
|
||||||
|
.<<<'EOF'
|
||||||
|
and nowdoc
|
||||||
|
EOF
|
||||||
|
); ?>
|
||||||
|
|
||||||
|
<?php new Translatable('translatable other-domain-test-no-params-short-array', [], 'not_messages'); ?>
|
||||||
|
|
||||||
|
<?php new Translatable('translatable other-domain-test-no-params-long-array', [], 'not_messages'); ?>
|
||||||
|
|
||||||
|
<?php new Translatable('translatable other-domain-test-params-short-array', ['foo' => 'bar'], 'not_messages'); ?>
|
||||||
|
|
||||||
|
<?php new Translatable('translatable other-domain-test-params-long-array', ['foo' => 'bar'], 'not_messages'); ?>
|
||||||
|
|
||||||
|
<?php new Translatable('translatable typecast', ['a' => (int) '123'], 'not_messages'); ?>
|
||||||
|
|
||||||
|
<?php new Translatable('translatable default domain', [], null); ?>
|
54
src/Symfony/Component/Translation/Translatable.php
Normal file
54
src/Symfony/Component/Translation/Translatable.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Translation;
|
||||||
|
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
|
// Load the global t() function
|
||||||
|
require_once __DIR__.'/Resources/functions/translatable.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nate Wiebe <nate@northern.co>
|
||||||
|
*/
|
||||||
|
final class Translatable
|
||||||
|
{
|
||||||
|
private $message;
|
||||||
|
private $parameters;
|
||||||
|
private $domain;
|
||||||
|
|
||||||
|
public function __construct(string $message, array $parameters = [], string $domain = 'messages')
|
||||||
|
{
|
||||||
|
$this->message = $message;
|
||||||
|
$this->parameters = $parameters;
|
||||||
|
$this->domain = $domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMessage(): string
|
||||||
|
{
|
||||||
|
return $this->message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getParameters(): array
|
||||||
|
{
|
||||||
|
return $this->parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDomain(): string
|
||||||
|
{
|
||||||
|
return $this->domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function trans(TranslatorInterface $translator, self $translatable, ?string $locale = null): string
|
||||||
|
{
|
||||||
|
return $translator->trans($translatable->getMessage(), $translatable->getParameters(), $translatable->getDomain(), $locale);
|
||||||
|
}
|
||||||
|
}
|
@ -48,6 +48,7 @@
|
|||||||
"psr/log-implementation": "To use logging capability in translator"
|
"psr/log-implementation": "To use logging capability in translator"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
"files": [ "Resources/functions/translatable.php" ],
|
||||||
"psr-4": { "Symfony\\Component\\Translation\\": "" },
|
"psr-4": { "Symfony\\Component\\Translation\\": "" },
|
||||||
"exclude-from-classmap": [
|
"exclude-from-classmap": [
|
||||||
"/Tests/"
|
"/Tests/"
|
||||||
|
Reference in New Issue
Block a user