Skip to content

Commit d5ce0e3

Browse files
committed
feature symfony#38330 [Contracts] add TranslatableInterface (nicolas-grekas)
This PR was merged into the 5.2-dev branch. Discussion ---------- [Contracts] add TranslatableInterface | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | symfony#38319 | License | MIT | Doc PR | - An alternative to symfony#38328: - `TranslatableInterface` is added to symfony/contracts; - `Translatable`, still in the component, implements it (and is not final as it makes no sense for a value-object) - the `t()` function is kept in the component - it doesn't fit in the contracts IMHO; - `Translatable::trans()` is not static anymore; - the domain is nullable instead of defaulting to "messages"; - the `t()` function moved in the `Symfony\Component\Translation` namespace (for reference, the `s()` function from String is also namespaced.); - the Twig extension is made strictly polymorphic: if you pass a Translatable, you cannot pass arguments/domain/count. Commits ------- 9224f7a [Contracts] add TranslatableInterface
2 parents e159dff + 9224f7a commit d5ce0e3

File tree

17 files changed

+92
-55
lines changed

17 files changed

+92
-55
lines changed

src/Symfony/Bridge/Twig/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ CHANGELOG
66

77
* added the `impersonation_exit_url()` and `impersonation_exit_path()` functions. They return a URL that allows to switch back to the original user.
88
* added the `workflow_transition()` function to easily retrieve a specific transition object
9-
* added support for translating `Translatable` objects
9+
* added support for translating `TranslatableInterface` objects
1010
* added the `t()` function to easily create `Translatable` objects
1111
* Added support for extracting messages from the `t()` function
1212

src/Symfony/Bridge/Twig/Extension/TranslationExtension.php

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Bridge\Twig\TokenParser\TransDefaultDomainTokenParser;
1717
use Symfony\Bridge\Twig\TokenParser\TransTokenParser;
1818
use Symfony\Component\Translation\Translatable;
19+
use Symfony\Contracts\Translation\TranslatableInterface;
1920
use Symfony\Contracts\Translation\TranslatorInterface;
2021
use Symfony\Contracts\Translation\TranslatorTrait;
2122
use Twig\Extension\AbstractExtension;
@@ -104,17 +105,24 @@ public function getTranslationNodeVisitor(): TranslationNodeVisitor
104105
}
105106

106107
/**
107-
* @param ?string|Translatable $message The message id (may also be an object that can be cast to string)
108+
* @param string|\Stringable|TranslatableInterface|null $message
109+
* @param array|string $arguments Can be the locale as a string when $message is a TranslatableInterface
108110
*/
109-
public function trans($message, array $arguments = [], string $domain = null, string $locale = null, int $count = null): string
111+
public function trans($message, $arguments = [], string $domain = null, string $locale = null, int $count = null): string
110112
{
111-
if ($message instanceof Translatable) {
112-
$arguments += $message->getParameters();
113-
$domain = $message->getDomain();
114-
$message = $message->getMessage();
113+
if ($message instanceof TranslatableInterface) {
114+
if ([] !== $arguments && !\is_string($arguments)) {
115+
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)));
116+
}
117+
118+
return $message->trans($this->getTranslator(), $locale ?? (\is_string($arguments) ? $arguments : null));
119+
}
120+
121+
if (!\is_array($arguments)) {
122+
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)));
115123
}
116124

117-
if (null === $message || '' === $message) {
125+
if ('' === $message = (string) $message) {
118126
return '';
119127
}
120128

@@ -125,8 +133,12 @@ public function trans($message, array $arguments = [], string $domain = null, st
125133
return $this->getTranslator()->trans($message, $arguments, $domain, $locale);
126134
}
127135

128-
public function createTranslatable(string $message, array $parameters = [], string $domain = 'messages'): Translatable
136+
public function createTranslatable(string $message, array $parameters = [], string $domain = null): Translatable
129137
{
138+
if (!class_exists(Translatable::class)) {
139+
throw new \LogicException(sprintf('You cannot use the "%s" as the Translation Component is not installed. Try running "composer require symfony/translation".', __CLASS__));
140+
}
141+
130142
return new Translatable($message, $parameters, $domain);
131143
}
132144
}

src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,19 +126,14 @@ public function getTransTests()
126126
// trans object
127127
['{{ t("Hello")|trans }}', 'Hello'],
128128
['{{ t(name)|trans }}', 'Symfony', ['name' => 'Symfony']],
129-
['{{ t(hello)|trans({ \'%name%\': \'Symfony\' }) }}', 'Hello Symfony', ['hello' => 'Hello %name%']],
130129
['{{ t(hello, { \'%name%\': \'Symfony\' })|trans }}', 'Hello Symfony', ['hello' => 'Hello %name%']],
131-
['{{ t(hello, { \'%name%\': \'Another Name\' })|trans({ \'%name%\': \'Symfony\' }) }}', 'Hello Symfony', ['hello' => 'Hello %name%']],
132-
['{% set vars = { \'%name%\': \'Symfony\' } %}{{ t(hello)|trans(vars) }}', 'Hello Symfony', ['hello' => 'Hello %name%']],
133130
['{% set vars = { \'%name%\': \'Symfony\' } %}{{ t(hello, vars)|trans }}', 'Hello Symfony', ['hello' => 'Hello %name%']],
131+
['{{ t("Hello")|trans("fr") }}', 'Hello'],
134132
['{{ t("Hello")|trans(locale="fr") }}', 'Hello'],
135133
['{{ t("Hello", {}, "messages")|trans(locale="fr") }}', 'Hello'],
136134

137135
// trans object with count
138-
['{{ 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]],
139-
['{{ 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%)']],
140-
['{{ 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%)']],
141-
['{{ 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]],
136+
['{{ 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]],
142137
];
143138
}
144139

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Translation;
13+
14+
/**
15+
* @author Nate Wiebe <[email protected]>
16+
*/
17+
function t(string $message, array $parameters = [], string $domain = null): Translatable
18+
{
19+
return new Translatable($message, $parameters, $domain);
20+
}

src/Symfony/Component/Translation/Resources/functions/translatable.php

Lines changed: 0 additions & 22 deletions
This file was deleted.

src/Symfony/Component/Translation/Tests/TranslatableTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public function testTrans($expected, $translatable, $translation, $locale)
2727
$translator->addLoader('array', new ArrayLoader());
2828
$translator->addResource('array', [$translatable->getMessage() => $translation], $locale, $translatable->getDomain());
2929

30-
$this->assertSame($expected, Translatable::trans($translator, $translatable, $locale));
30+
$this->assertSame($expected, $translatable->trans($translator, $locale));
3131
}
3232

3333
/**
@@ -39,7 +39,7 @@ public function testFlattenedTrans($expected, $messages, $translatable)
3939
$translator->addLoader('array', new ArrayLoader());
4040
$translator->addResource('array', $messages, 'fr', '');
4141

42-
$this->assertSame($expected, Translatable::trans($translator, $translatable, 'fr'));
42+
$this->assertSame($expected, $translatable->trans($translator, 'fr'));
4343
}
4444

4545
public function testToString()

src/Symfony/Component/Translation/Translatable.php

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,19 @@
1111

1212
namespace Symfony\Component\Translation;
1313

14+
use Symfony\Contracts\Translation\TranslatableInterface;
1415
use Symfony\Contracts\Translation\TranslatorInterface;
1516

1617
/**
1718
* @author Nate Wiebe <[email protected]>
1819
*/
19-
final class Translatable
20+
class Translatable implements TranslatableInterface
2021
{
2122
private $message;
2223
private $parameters;
2324
private $domain;
2425

25-
public function __construct(string $message, array $parameters = [], string $domain = 'messages')
26+
public function __construct(string $message, array $parameters = [], string $domain = null)
2627
{
2728
$this->message = $message;
2829
$this->parameters = $parameters;
@@ -31,7 +32,7 @@ public function __construct(string $message, array $parameters = [], string $dom
3132

3233
public function __toString(): string
3334
{
34-
return $this->message;
35+
return $this->getMessage();
3536
}
3637

3738
public function getMessage(): string
@@ -44,13 +45,13 @@ public function getParameters(): array
4445
return $this->parameters;
4546
}
4647

47-
public function getDomain(): string
48+
public function getDomain(): ?string
4849
{
4950
return $this->domain;
5051
}
5152

52-
public static function trans(TranslatorInterface $translator, self $translatable, ?string $locale = null): string
53+
public function trans(TranslatorInterface $translator, string $locale = null): string
5354
{
54-
return $translator->trans($translatable->getMessage(), $translatable->getParameters(), $translatable->getDomain(), $locale);
55+
return $translator->trans($this->getMessage(), $this->getParameters(), $this->getDomain(), $locale);
5556
}
5657
}

src/Symfony/Component/Translation/composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"php": ">=7.2.5",
2020
"symfony/polyfill-mbstring": "~1.0",
2121
"symfony/polyfill-php80": "^1.15",
22-
"symfony/translation-contracts": "^2"
22+
"symfony/translation-contracts": "^2.3"
2323
},
2424
"require-dev": {
2525
"symfony/config": "^4.4|^5.0",
@@ -48,7 +48,7 @@
4848
"psr/log-implementation": "To use logging capability in translator"
4949
},
5050
"autoload": {
51-
"files": [ "Resources/functions/translatable.php" ],
51+
"files": [ "Resources/functions.php" ],
5252
"psr-4": { "Symfony\\Component\\Translation\\": "" },
5353
"exclude-from-classmap": [
5454
"/Tests/"

src/Symfony/Contracts/CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
CHANGELOG
22
=========
33

4+
2.3.0
5+
-----
6+
7+
* added `Translation\TranslatableInterface` to enable value-objects to be translated
8+
* made `Translation\TranslatorTrait::getLocale()` fallback to intl's `Locale::getDefault()` when available
9+
10+
2.2.0
11+
-----
12+
13+
* added `Service\Attribute\Required` attribute for PHP 8
14+
415
2.1.3
516
-----
617

src/Symfony/Contracts/Cache/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"minimum-stability": "dev",
2929
"extra": {
3030
"branch-alias": {
31-
"dev-master": "2.2-dev"
31+
"dev-master": "2.3-dev"
3232
},
3333
"thanks": {
3434
"name": "symfony/contracts",

0 commit comments

Comments
 (0)