[Contracts] add TranslatableInterface
This commit is contained in:
parent
6349a1b2fc
commit
9224f7ac5b
@ -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 `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 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 the `t()` function to easily create `Translatable` objects
|
||||||
* Added support for extracting messages from the `t()` function
|
* Added support for extracting messages from the `t()` function
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ 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\Component\Translation\Translatable;
|
||||||
|
use Symfony\Contracts\Translation\TranslatableInterface;
|
||||||
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;
|
||||||
@ -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) {
|
if ($message instanceof TranslatableInterface) {
|
||||||
$arguments += $message->getParameters();
|
if ([] !== $arguments && !\is_string($arguments)) {
|
||||||
$domain = $message->getDomain();
|
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)));
|
||||||
$message = $message->getMessage();
|
}
|
||||||
|
|
||||||
|
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 '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,8 +133,12 @@ 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
|
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);
|
return new Translatable($message, $parameters, $domain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,19 +126,14 @@ class TranslationExtensionTest extends TestCase
|
|||||||
// trans object
|
// trans object
|
||||||
['{{ t("Hello")|trans }}', 'Hello'],
|
['{{ t("Hello")|trans }}', 'Hello'],
|
||||||
['{{ t(name)|trans }}', 'Symfony', ['name' => 'Symfony']],
|
['{{ 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%\': \'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%']],
|
['{% 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")|trans(locale="fr") }}', 'Hello'],
|
||||||
['{{ t("Hello", {}, "messages")|trans(locale="fr") }}', 'Hello'],
|
['{{ t("Hello", {}, "messages")|trans(locale="fr") }}', 'Hello'],
|
||||||
|
|
||||||
// trans object with count
|
// 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("{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]],
|
||||||
['{{ 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]],
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
20
src/Symfony/Component/Translation/Resources/functions.php
Normal file
20
src/Symfony/Component/Translation/Resources/functions.php
Normal 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);
|
||||||
|
}
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -27,7 +27,7 @@ class TranslatableTest extends TestCase
|
|||||||
$translator->addLoader('array', new ArrayLoader());
|
$translator->addLoader('array', new ArrayLoader());
|
||||||
$translator->addResource('array', [$translatable->getMessage() => $translation], $locale, $translatable->getDomain());
|
$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->addLoader('array', new ArrayLoader());
|
||||||
$translator->addResource('array', $messages, 'fr', '');
|
$translator->addResource('array', $messages, 'fr', '');
|
||||||
|
|
||||||
$this->assertSame($expected, Translatable::trans($translator, $translatable, 'fr'));
|
$this->assertSame($expected, $translatable->trans($translator, 'fr'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testToString()
|
public function testToString()
|
||||||
|
@ -11,18 +11,19 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Translation;
|
namespace Symfony\Component\Translation;
|
||||||
|
|
||||||
|
use Symfony\Contracts\Translation\TranslatableInterface;
|
||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Nate Wiebe <nate@northern.co>
|
* @author Nate Wiebe <nate@northern.co>
|
||||||
*/
|
*/
|
||||||
final class Translatable
|
class Translatable implements TranslatableInterface
|
||||||
{
|
{
|
||||||
private $message;
|
private $message;
|
||||||
private $parameters;
|
private $parameters;
|
||||||
private $domain;
|
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->message = $message;
|
||||||
$this->parameters = $parameters;
|
$this->parameters = $parameters;
|
||||||
@ -31,7 +32,7 @@ final class Translatable
|
|||||||
|
|
||||||
public function __toString(): string
|
public function __toString(): string
|
||||||
{
|
{
|
||||||
return $this->message;
|
return $this->getMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMessage(): string
|
public function getMessage(): string
|
||||||
@ -44,13 +45,13 @@ final class Translatable
|
|||||||
return $this->parameters;
|
return $this->parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDomain(): string
|
public function getDomain(): ?string
|
||||||
{
|
{
|
||||||
return $this->domain;
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
"php": ">=7.2.5",
|
"php": ">=7.2.5",
|
||||||
"symfony/polyfill-mbstring": "~1.0",
|
"symfony/polyfill-mbstring": "~1.0",
|
||||||
"symfony/polyfill-php80": "^1.15",
|
"symfony/polyfill-php80": "^1.15",
|
||||||
"symfony/translation-contracts": "^2"
|
"symfony/translation-contracts": "^2.3"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"symfony/config": "^4.4|^5.0",
|
"symfony/config": "^4.4|^5.0",
|
||||||
@ -48,7 +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" ],
|
"files": [ "Resources/functions.php" ],
|
||||||
"psr-4": { "Symfony\\Component\\Translation\\": "" },
|
"psr-4": { "Symfony\\Component\\Translation\\": "" },
|
||||||
"exclude-from-classmap": [
|
"exclude-from-classmap": [
|
||||||
"/Tests/"
|
"/Tests/"
|
||||||
|
@ -1,6 +1,17 @@
|
|||||||
CHANGELOG
|
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
|
2.1.3
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "2.2-dev"
|
"dev-master": "2.3-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/contracts",
|
"name": "symfony/contracts",
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "2.2-dev"
|
"dev-master": "2.3-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/contracts",
|
"name": "symfony/contracts",
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "2.2-dev"
|
"dev-master": "2.3-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/contracts",
|
"name": "symfony/contracts",
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "2.2-dev"
|
"dev-master": "2.3-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/contracts",
|
"name": "symfony/contracts",
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "2.2-dev"
|
"dev-master": "2.3-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/contracts",
|
"name": "symfony/contracts",
|
||||||
|
20
src/Symfony/Contracts/Translation/TranslatableInterface.php
Normal file
20
src/Symfony/Contracts/Translation/TranslatableInterface.php
Normal 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;
|
||||||
|
}
|
@ -27,7 +27,7 @@
|
|||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "2.2-dev"
|
"dev-master": "2.3-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/contracts",
|
"name": "symfony/contracts",
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "2.2-dev"
|
"dev-master": "2.3-dev"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user