diff --git a/src/Symfony/Bridge/Twig/CHANGELOG.md b/src/Symfony/Bridge/Twig/CHANGELOG.md index e0cd19afd5..0928d2b69e 100644 --- a/src/Symfony/Bridge/Twig/CHANGELOG.md +++ b/src/Symfony/Bridge/Twig/CHANGELOG.md @@ -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 diff --git a/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php b/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php index 4f6cc27d18..66486fa19d 100644 --- a/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php @@ -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); } } diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php index c45f7fb760..b0e59d7242 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php @@ -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]], ]; } diff --git a/src/Symfony/Component/Translation/Resources/functions.php b/src/Symfony/Component/Translation/Resources/functions.php new file mode 100644 index 0000000000..25da6010b5 --- /dev/null +++ b/src/Symfony/Component/Translation/Resources/functions.php @@ -0,0 +1,20 @@ + + * + * 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 + */ +function t(string $message, array $parameters = [], string $domain = null): Translatable +{ + return new Translatable($message, $parameters, $domain); +} diff --git a/src/Symfony/Component/Translation/Resources/functions/translatable.php b/src/Symfony/Component/Translation/Resources/functions/translatable.php deleted file mode 100644 index f963b76052..0000000000 --- a/src/Symfony/Component/Translation/Resources/functions/translatable.php +++ /dev/null @@ -1,22 +0,0 @@ - - * - * 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 - */ - function t(string $message, array $parameters = [], string $domain = 'messages'): Translatable - { - return new Translatable($message, $parameters, $domain); - } -} diff --git a/src/Symfony/Component/Translation/Tests/TranslatableTest.php b/src/Symfony/Component/Translation/Tests/TranslatableTest.php index 9a93c2c1c3..231df66cd5 100644 --- a/src/Symfony/Component/Translation/Tests/TranslatableTest.php +++ b/src/Symfony/Component/Translation/Tests/TranslatableTest.php @@ -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() diff --git a/src/Symfony/Component/Translation/Translatable.php b/src/Symfony/Component/Translation/Translatable.php index efb6011fa8..eceb4c3423 100644 --- a/src/Symfony/Component/Translation/Translatable.php +++ b/src/Symfony/Component/Translation/Translatable.php @@ -11,18 +11,19 @@ namespace Symfony\Component\Translation; +use Symfony\Contracts\Translation\TranslatableInterface; use Symfony\Contracts\Translation\TranslatorInterface; /** * @author Nate Wiebe */ -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); } } diff --git a/src/Symfony/Component/Translation/composer.json b/src/Symfony/Component/Translation/composer.json index da59763414..e0cf58c6b6 100644 --- a/src/Symfony/Component/Translation/composer.json +++ b/src/Symfony/Component/Translation/composer.json @@ -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/" diff --git a/src/Symfony/Contracts/CHANGELOG.md b/src/Symfony/Contracts/CHANGELOG.md index 912d90e51e..b62029adb5 100644 --- a/src/Symfony/Contracts/CHANGELOG.md +++ b/src/Symfony/Contracts/CHANGELOG.md @@ -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 ----- diff --git a/src/Symfony/Contracts/Cache/composer.json b/src/Symfony/Contracts/Cache/composer.json index 0fc3064c80..c6e868b482 100644 --- a/src/Symfony/Contracts/Cache/composer.json +++ b/src/Symfony/Contracts/Cache/composer.json @@ -28,7 +28,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-master": "2.3-dev" }, "thanks": { "name": "symfony/contracts", diff --git a/src/Symfony/Contracts/Deprecation/composer.json b/src/Symfony/Contracts/Deprecation/composer.json index 052541cce3..379117f6ee 100644 --- a/src/Symfony/Contracts/Deprecation/composer.json +++ b/src/Symfony/Contracts/Deprecation/composer.json @@ -25,7 +25,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-master": "2.3-dev" }, "thanks": { "name": "symfony/contracts", diff --git a/src/Symfony/Contracts/EventDispatcher/composer.json b/src/Symfony/Contracts/EventDispatcher/composer.json index cc53176eb9..731593b405 100644 --- a/src/Symfony/Contracts/EventDispatcher/composer.json +++ b/src/Symfony/Contracts/EventDispatcher/composer.json @@ -28,7 +28,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-master": "2.3-dev" }, "thanks": { "name": "symfony/contracts", diff --git a/src/Symfony/Contracts/HttpClient/composer.json b/src/Symfony/Contracts/HttpClient/composer.json index 7ec4955e58..adf519dda2 100644 --- a/src/Symfony/Contracts/HttpClient/composer.json +++ b/src/Symfony/Contracts/HttpClient/composer.json @@ -27,7 +27,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-master": "2.3-dev" }, "thanks": { "name": "symfony/contracts", diff --git a/src/Symfony/Contracts/Service/composer.json b/src/Symfony/Contracts/Service/composer.json index 47244fbb10..0a4df93295 100644 --- a/src/Symfony/Contracts/Service/composer.json +++ b/src/Symfony/Contracts/Service/composer.json @@ -28,7 +28,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-master": "2.3-dev" }, "thanks": { "name": "symfony/contracts", diff --git a/src/Symfony/Contracts/Translation/TranslatableInterface.php b/src/Symfony/Contracts/Translation/TranslatableInterface.php new file mode 100644 index 0000000000..47fd6fa029 --- /dev/null +++ b/src/Symfony/Contracts/Translation/TranslatableInterface.php @@ -0,0 +1,20 @@ + + * + * 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 + */ +interface TranslatableInterface +{ + public function trans(TranslatorInterface $translator, string $locale = null): string; +} diff --git a/src/Symfony/Contracts/Translation/composer.json b/src/Symfony/Contracts/Translation/composer.json index 2cef00fdd4..4e50eec291 100644 --- a/src/Symfony/Contracts/Translation/composer.json +++ b/src/Symfony/Contracts/Translation/composer.json @@ -27,7 +27,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-master": "2.3-dev" }, "thanks": { "name": "symfony/contracts", diff --git a/src/Symfony/Contracts/composer.json b/src/Symfony/Contracts/composer.json index 1ce94b4699..22c5b72f7b 100644 --- a/src/Symfony/Contracts/composer.json +++ b/src/Symfony/Contracts/composer.json @@ -49,7 +49,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-master": "2.3-dev" } } }