[Translation] Translatable objects

This commit is contained in:
Nate Wiebe 2020-07-25 22:18:32 -04:00 committed by Fabien Potencier
parent 6c094cce73
commit dc6b3bf62d
15 changed files with 507 additions and 4 deletions

View File

@ -5,6 +5,9 @@ CHANGELOG
-----
* 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
-----

View File

@ -15,10 +15,12 @@ use Symfony\Bridge\Twig\NodeVisitor\TranslationDefaultDomainNodeVisitor;
use Symfony\Bridge\Twig\NodeVisitor\TranslationNodeVisitor;
use Symfony\Bridge\Twig\TokenParser\TransDefaultDomainTokenParser;
use Symfony\Bridge\Twig\TokenParser\TransTokenParser;
use Symfony\Component\Translation\Translatable;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Contracts\Translation\TranslatorTrait;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;
// Help opcache.preload discover always-needed symbols
class_exists(TranslatorInterface::class);
@ -54,6 +56,16 @@ final class TranslationExtension extends AbstractExtension
return $this->translator;
}
/**
* {@inheritdoc}
*/
public function getFunctions(): array
{
return [
new TwigFunction('t', [$this, 'createTranslatable']),
];
}
/**
* {@inheritdoc}
*/
@ -91,8 +103,17 @@ final class TranslationExtension extends AbstractExtension
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) {
return '';
}
@ -103,4 +124,9 @@ final class TranslationExtension extends AbstractExtension
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);
}
}

View File

@ -15,6 +15,7 @@ use Symfony\Bridge\Twig\Node\TransNode;
use Twig\Environment;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\FilterExpression;
use Twig\Node\Expression\FunctionExpression;
use Twig\Node\Node;
use Twig\NodeVisitor\AbstractNodeVisitor;
@ -66,6 +67,20 @@ final class TranslationNodeVisitor extends AbstractNodeVisitor
$node->getNode('node')->getAttribute('value'),
$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 (
$node instanceof FilterExpression &&
'transchoice' === $node->getNode('filter')->getAttribute('value') &&
@ -103,6 +118,28 @@ final class TranslationNodeVisitor extends AbstractNodeVisitor
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
{
if ($arguments->hasNode('domain')) {

View File

@ -122,6 +122,23 @@ class TranslationExtensionTest extends TestCase
// trans filter with null message
['{{ null|trans }}', ''],
['{{ 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]],
];
}

View File

@ -60,6 +60,9 @@ class TwigExtractorTest extends TestCase
['{% trans from "domain" %}new key{% endtrans %}', ['new key' => 'domain']],
['{% set foo = "new key" | trans %}', ['new 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
['{% trans_default_domain "domain" %}{{ "new key"|trans }}', ['new key' => 'domain']],

View File

@ -5,6 +5,9 @@ CHANGELOG
-----
* 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
-----

View File

@ -54,6 +54,66 @@ class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
'(',
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,
],
];
/**

View File

@ -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);
}
}

View File

@ -41,6 +41,39 @@ EOF;
// Assert
$expectedCatalogue = [
'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',
'double-quoted key' => 'prefixdouble-quoted key',
'heredoc key' => 'prefixheredoc key',
@ -54,6 +87,21 @@ EOF;
'default domain' => 'prefixdefault domain',
],
'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-long-array' => 'prefixother-domain-test-no-params-long-array',
'other-domain-test-params-short-array' => 'prefixother-domain-test-params-short-array',
@ -65,6 +113,18 @@ EOF;
$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';
$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'));
@ -73,20 +133,21 @@ EOF;
public function resourcesProvider()
{
$directory = __DIR__.'/../fixtures/extractor/';
$phpFiles = [];
$splFiles = [];
foreach (new \DirectoryIterator($directory) as $fileInfo) {
if ($fileInfo->isDot()) {
continue;
}
if ('translation.html.php' === $fileInfo->getBasename()) {
$phpFile = $fileInfo->getPathname();
if (\in_array($fileInfo->getBasename(), ['translatable.html.php', 'translatable-fqn.html.php', 'translatable-short.html.php', 'translation.html.php'], true)) {
$phpFiles[] = $fileInfo->getPathname();
}
$splFiles[] = $fileInfo->getFileInfo();
}
return [
[$directory],
[$phpFile],
[$phpFiles],
[glob($directory.'*')],
[$splFiles],
[new \ArrayObject(glob($directory.'*'))],

View 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', [], '')],
];
}
}

View 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); ?>

View 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); ?>

View 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); ?>

View 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);
}
}

View File

@ -48,6 +48,7 @@
"psr/log-implementation": "To use logging capability in translator"
},
"autoload": {
"files": [ "Resources/functions/translatable.php" ],
"psr-4": { "Symfony\\Component\\Translation\\": "" },
"exclude-from-classmap": [
"/Tests/"