[Contracts] add TranslatableInterface

This commit is contained in:
Nicolas Grekas 2020-09-28 10:10:59 +02:00
parent 6349a1b2fc
commit 9224f7ac5b
17 changed files with 92 additions and 55 deletions

View File

@ -6,7 +6,7 @@ CHANGELOG
* added the `impersonation_exit_url()` and `impersonation_exit_path()` functions. They return a URL that allows to switch back to the original user.
* added the `workflow_transition()` function to easily retrieve a specific transition object
* added support for translating `Translatable` objects
* added support for translating `TranslatableInterface` objects
* added the `t()` function to easily create `Translatable` objects
* Added support for extracting messages from the `t()` function

View File

@ -16,6 +16,7 @@ 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\TranslatableInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Contracts\Translation\TranslatorTrait;
use Twig\Extension\AbstractExtension;
@ -104,17 +105,24 @@ final class TranslationExtension extends AbstractExtension
}
/**
* @param ?string|Translatable $message The message id (may also be an object that can be cast to string)
* @param string|\Stringable|TranslatableInterface|null $message
* @param array|string $arguments Can be the locale as a string when $message is a TranslatableInterface
*/
public function trans($message, array $arguments = [], string $domain = null, string $locale = null, int $count = null): string
public function trans($message, $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 ($message instanceof TranslatableInterface) {
if ([] !== $arguments && !\is_string($arguments)) {
throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be a locale passed as a string when the message is a "%s", "%s" given.', __METHOD__, TranslatableInterface::class, get_debug_type($arguments)));
}
return $message->trans($this->getTranslator(), $locale ?? (\is_string($arguments) ? $arguments : null));
}
if (null === $message || '' === $message) {
if (!\is_array($arguments)) {
throw new \TypeError(sprintf('Unless the message is a "%s", argument 2 passed to "%s()" must be an array of parameters, "%s" given.', TranslatableInterface::class, __METHOD__, get_debug_type($arguments)));
}
if ('' === $message = (string) $message) {
return '';
}
@ -125,8 +133,12 @@ final class TranslationExtension extends AbstractExtension
return $this->getTranslator()->trans($message, $arguments, $domain, $locale);
}
public function createTranslatable(string $message, array $parameters = [], string $domain = 'messages'): Translatable
public function createTranslatable(string $message, array $parameters = [], string $domain = null): Translatable
{
if (!class_exists(Translatable::class)) {
throw new \LogicException(sprintf('You cannot use the "%s" as the Translation Component is not installed. Try running "composer require symfony/translation".', __CLASS__));
}
return new Translatable($message, $parameters, $domain);
}
}

View File

@ -126,19 +126,14 @@ class TranslationExtensionTest extends TestCase
// 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("fr") }}', 'Hello'],
['{{ 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]],
['{{ t("{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples", {\'%count%\': count})|trans }}', 'There is 5 apples', ['count' => 5]],
];
}

View File

@ -0,0 +1,20 @@
<?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;
/**
* @author Nate Wiebe <nate@northern.co>
*/
function t(string $message, array $parameters = [], string $domain = null): Translatable
{
return new Translatable($message, $parameters, $domain);
}

View File

@ -1,22 +0,0 @@
<?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

@ -27,7 +27,7 @@ class TranslatableTest extends TestCase
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', [$translatable->getMessage() => $translation], $locale, $translatable->getDomain());
$this->assertSame($expected, Translatable::trans($translator, $translatable, $locale));
$this->assertSame($expected, $translatable->trans($translator, $locale));
}
/**
@ -39,7 +39,7 @@ class TranslatableTest extends TestCase
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', $messages, 'fr', '');
$this->assertSame($expected, Translatable::trans($translator, $translatable, 'fr'));
$this->assertSame($expected, $translatable->trans($translator, 'fr'));
}
public function testToString()

View File

@ -11,18 +11,19 @@
namespace Symfony\Component\Translation;
use Symfony\Contracts\Translation\TranslatableInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @author Nate Wiebe <nate@northern.co>
*/
final class Translatable
class Translatable implements TranslatableInterface
{
private $message;
private $parameters;
private $domain;
public function __construct(string $message, array $parameters = [], string $domain = 'messages')
public function __construct(string $message, array $parameters = [], string $domain = null)
{
$this->message = $message;
$this->parameters = $parameters;
@ -31,7 +32,7 @@ final class Translatable
public function __toString(): string
{
return $this->message;
return $this->getMessage();
}
public function getMessage(): string
@ -44,13 +45,13 @@ final class Translatable
return $this->parameters;
}
public function getDomain(): string
public function getDomain(): ?string
{
return $this->domain;
}
public static function trans(TranslatorInterface $translator, self $translatable, ?string $locale = null): string
public function trans(TranslatorInterface $translator, string $locale = null): string
{
return $translator->trans($translatable->getMessage(), $translatable->getParameters(), $translatable->getDomain(), $locale);
return $translator->trans($this->getMessage(), $this->getParameters(), $this->getDomain(), $locale);
}
}

View File

@ -19,7 +19,7 @@
"php": ">=7.2.5",
"symfony/polyfill-mbstring": "~1.0",
"symfony/polyfill-php80": "^1.15",
"symfony/translation-contracts": "^2"
"symfony/translation-contracts": "^2.3"
},
"require-dev": {
"symfony/config": "^4.4|^5.0",
@ -48,7 +48,7 @@
"psr/log-implementation": "To use logging capability in translator"
},
"autoload": {
"files": [ "Resources/functions/translatable.php" ],
"files": [ "Resources/functions.php" ],
"psr-4": { "Symfony\\Component\\Translation\\": "" },
"exclude-from-classmap": [
"/Tests/"

View File

@ -1,6 +1,17 @@
CHANGELOG
=========
2.3.0
-----
* added `Translation\TranslatableInterface` to enable value-objects to be translated
* made `Translation\TranslatorTrait::getLocale()` fallback to intl's `Locale::getDefault()` when available
2.2.0
-----
* added `Service\Attribute\Required` attribute for PHP 8
2.1.3
-----

View File

@ -28,7 +28,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
"dev-master": "2.3-dev"
},
"thanks": {
"name": "symfony/contracts",

View File

@ -25,7 +25,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
"dev-master": "2.3-dev"
},
"thanks": {
"name": "symfony/contracts",

View File

@ -28,7 +28,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
"dev-master": "2.3-dev"
},
"thanks": {
"name": "symfony/contracts",

View File

@ -27,7 +27,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
"dev-master": "2.3-dev"
},
"thanks": {
"name": "symfony/contracts",

View File

@ -28,7 +28,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
"dev-master": "2.3-dev"
},
"thanks": {
"name": "symfony/contracts",

View File

@ -0,0 +1,20 @@
<?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\Contracts\Translation;
/**
* @author Nicolas Grekas <p@tchwork.com>
*/
interface TranslatableInterface
{
public function trans(TranslatorInterface $translator, string $locale = null): string;
}

View File

@ -27,7 +27,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
"dev-master": "2.3-dev"
},
"thanks": {
"name": "symfony/contracts",

View File

@ -49,7 +49,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
"dev-master": "2.3-dev"
}
}
}