Skip to content

Commit f40687e

Browse files
committed
feature #50212 [FrameworkBundle][Serializer] Add TranslatableNormalizer (Jean-Beru)
This PR was squashed before being merged into the 6.4 branch. Discussion ---------- [FrameworkBundle][Serializer] Add TranslatableNormalizer | Q | A | ------------- | --- | Branch? | 6.4 | Bug fix? | no | New feature? | yes | Deprecations? | maybe | Tickets | | License | MIT | Doc PR | WIP In 5.2, Symfony introduced translatable objects. This PR adds a dedicated normalizer to translate them. It can be useful for an API to return translated properties. Also I'm wondering if it could be considered as a BC break since translatable objects were handled by the ObjectNormalizer which currently returns a plain object. PS: if accepted, I'll rebase this PR on 6.4 since 6.3 is closed for new features. Commits ------- 1ac8a2b [FrameworkBundle][Serializer] Add TranslatableNormalizer
2 parents 54bfd57 + 1ac8a2b commit f40687e

File tree

7 files changed

+144
-0
lines changed

7 files changed

+144
-0
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1894,6 +1894,10 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder
18941894
$container->removeDefinition('serializer.normalizer.mime_message');
18951895
}
18961896

1897+
if (!class_exists(Translator::class)) {
1898+
$container->removeDefinition('serializer.normalizer.translatable');
1899+
}
1900+
18971901
// compat with Symfony < 6.3
18981902
if (!is_subclass_of(ProblemNormalizer::class, SerializerAwareInterface::class)) {
18991903
$container->getDefinition('serializer.normalizer.problem')

src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
4848
use Symfony\Component\Serializer\Normalizer\ProblemNormalizer;
4949
use Symfony\Component\Serializer\Normalizer\PropertyNormalizer;
50+
use Symfony\Component\Serializer\Normalizer\TranslatableNormalizer;
5051
use Symfony\Component\Serializer\Normalizer\UidNormalizer;
5152
use Symfony\Component\Serializer\Normalizer\UnwrappingDenormalizer;
5253
use Symfony\Component\Serializer\Serializer;
@@ -113,6 +114,10 @@
113114
->set('serializer.normalizer.uid', UidNormalizer::class)
114115
->tag('serializer.normalizer', ['priority' => -890])
115116

117+
->set('serializer.normalizer.translatable', TranslatableNormalizer::class)
118+
->args(['$translator' => service('translator')])
119+
->tag('serializer.normalizer', ['priority' => -890])
120+
116121
->set('serializer.normalizer.form_error', FormErrorNormalizer::class)
117122
->tag('serializer.normalizer', ['priority' => -915])
118123

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
use Symfony\Component\Serializer\Normalizer\FormErrorNormalizer;
7676
use Symfony\Component\Serializer\Normalizer\JsonSerializableNormalizer;
7777
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
78+
use Symfony\Component\Serializer\Normalizer\TranslatableNormalizer;
7879
use Symfony\Component\Serializer\Serializer;
7980
use Symfony\Component\Translation\DependencyInjection\TranslatorPass;
8081
use Symfony\Component\Translation\LocaleSwitcher;
@@ -1586,6 +1587,18 @@ public function testConstraintViolationListNormalizerRegistered()
15861587
$this->assertEquals(new Reference('serializer.name_converter.metadata_aware'), $definition->getArgument(1));
15871588
}
15881589

1590+
public function testTranslatableNormalizerRegistered()
1591+
{
1592+
$container = $this->createContainerFromFile('full');
1593+
1594+
$definition = $container->getDefinition('serializer.normalizer.translatable');
1595+
$tag = $definition->getTag('serializer.normalizer');
1596+
1597+
$this->assertSame(TranslatableNormalizer::class, $definition->getClass());
1598+
$this->assertSame(-890, $tag[0]['priority']);
1599+
$this->assertEquals(new Reference('translator'), $definition->getArgument('$translator'));
1600+
}
1601+
15891602
public function testSerializerCacheActivated()
15901603
{
15911604
$container = $this->createContainerFromFile('serializer_enabled');

src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SerializerTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public static function provideNormalizersAndEncodersWithDefaultContextOption():
6060
['serializer.normalizer.json_serializable.alias'],
6161
['serializer.normalizer.problem.alias'],
6262
['serializer.normalizer.uid.alias'],
63+
['serializer.normalizer.translatable.alias'],
6364
['serializer.normalizer.object.alias'],
6465
['serializer.encoder.xml.alias'],
6566
['serializer.encoder.yaml.alias'],

src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/config.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ services:
3939
alias: serializer.normalizer.uid
4040
public: true
4141

42+
serializer.normalizer.translatable.alias:
43+
alias: serializer.normalizer.translatable
44+
public: true
45+
4246
serializer.normalizer.property.alias:
4347
alias: serializer.normalizer.property
4448
public: true
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Serializer\Normalizer;
13+
14+
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
15+
use Symfony\Contracts\Translation\TranslatableInterface;
16+
use Symfony\Contracts\Translation\TranslatorInterface;
17+
18+
final class TranslatableNormalizer implements NormalizerInterface
19+
{
20+
public const NORMALIZATION_LOCALE_KEY = 'translatable_normalization_locale';
21+
22+
private array $defaultContext = [
23+
self::NORMALIZATION_LOCALE_KEY => null,
24+
];
25+
26+
public function __construct(
27+
private readonly TranslatorInterface $translator,
28+
array $defaultContext = [],
29+
) {
30+
$this->defaultContext = array_merge($this->defaultContext, $defaultContext);
31+
}
32+
33+
/**
34+
* @throws InvalidArgumentException
35+
*/
36+
public function normalize(mixed $object, string $format = null, array $context = []): string
37+
{
38+
if (!$object instanceof TranslatableInterface) {
39+
throw new InvalidArgumentException(sprintf('The object must implement the "%s".', TranslatableInterface::class));
40+
}
41+
42+
return $object->trans($this->translator, $context[self::NORMALIZATION_LOCALE_KEY] ?? $this->defaultContext[self::NORMALIZATION_LOCALE_KEY]);
43+
}
44+
45+
public function supportsNormalization(mixed $data, string $format = null, array $context = []): bool
46+
{
47+
return $data instanceof TranslatableInterface;
48+
}
49+
50+
public function getSupportedTypes(?string $format): array
51+
{
52+
return [TranslatableInterface::class => true];
53+
}
54+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Serializer\Tests\Normalizer;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Serializer\Normalizer\TranslatableNormalizer;
16+
use Symfony\Contracts\Translation\TranslatableInterface;
17+
use Symfony\Contracts\Translation\TranslatorInterface;
18+
19+
class TranslatableNormalizerTest extends TestCase
20+
{
21+
private readonly TranslatableNormalizer $normalizer;
22+
23+
protected function setUp(): void
24+
{
25+
$this->normalizer = new TranslatableNormalizer($this->createMock(TranslatorInterface::class));
26+
}
27+
28+
public function testSupportsNormalization()
29+
{
30+
$this->assertTrue($this->normalizer->supportsNormalization(new TestMessage()));
31+
$this->assertFalse($this->normalizer->supportsNormalization(new \stdClass()));
32+
}
33+
34+
public function testNormalize()
35+
{
36+
$message = new TestMessage();
37+
38+
$this->assertSame('key_null', $this->normalizer->normalize($message));
39+
$this->assertSame('key_fr', $this->normalizer->normalize($message, context: ['translatable_normalization_locale' => 'fr']));
40+
$this->assertSame('key_en', $this->normalizer->normalize($message, context: ['translatable_normalization_locale' => 'en']));
41+
}
42+
43+
public function testNormalizeWithNormalizationLocalePassedInConstructor()
44+
{
45+
$normalizer = new TranslatableNormalizer(
46+
$this->createMock(TranslatorInterface::class),
47+
['translatable_normalization_locale' => 'es'],
48+
);
49+
$message = new TestMessage();
50+
51+
$this->assertSame('key_es', $normalizer->normalize($message));
52+
$this->assertSame('key_fr', $normalizer->normalize($message, context: ['translatable_normalization_locale' => 'fr']));
53+
$this->assertSame('key_en', $normalizer->normalize($message, context: ['translatable_normalization_locale' => 'en']));
54+
}
55+
}
56+
57+
class TestMessage implements TranslatableInterface
58+
{
59+
public function trans(TranslatorInterface $translator, string $locale = null): string
60+
{
61+
return 'key_'.($locale ?? 'null');
62+
}
63+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy