8000 feature #59967 [Mailer][TwigBridge] Add support for translatable subj… · symfony/symfony@fcdc0cf · GitHub
[go: up one dir, main page]

Skip to content

Commit fcdc0cf

Browse files
committed
feature #59967 [Mailer][TwigBridge] Add support for translatable subject (norkunas)
This PR was merged into the 7.3 branch. Discussion ---------- [Mailer][TwigBridge] Add support for translatable subject | Q | A | ------------- | --- | Branch? | 7.3 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | Fix #54203 | License | MIT Commits ------- 97eb49b [Mailer][TwigBridge] Add support for translatable subject
2 parents f79ae58 + 97eb49b commit fcdc0cf

File tree

9 files changed

+87
-6
lines changed

9 files changed

+87
-6
lines changed

src/Symfony/Bridge/Twig/CHANGELOG.md

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

77
* Add `is_granted_for_user()` Twig function
88
* Add `field_id()` Twig form helper function
9+
* Add `TemplatedEmail::getTranslatableSubject()` method
910

1011
7.2
1112
---

src/Symfony/Bridge/Twig/Mime/TemplatedEmail.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,29 @@
1818
*/
1919
class TemplatedEmail extends Email
2020
{
21+
private string|\Stringable|null $subject = null;
2122
private ?string $htmlTemplate = null;
2223
private ?string $textTemplate = null;
2324
private ?string $locale = null;
2425
private array $context = [];
2526

27+
/**
28+
* @return $this
29+
*/
30+
public function subject(string|\Stringable $subject): static
31+
{
32+
parent::subject($subject);
33+
34+
$this->subject = $subject;
35+
36+
return $this;
37+
}
38+
39+
public function getTranslatableSubject(): string|\Stringable|null
40+
{
41+
return $this->subject;
42+
}
43+
2644
/**
2745
* @return $this
2846
*/
@@ -100,7 +118,7 @@ public function markAsRendered(): void
100118
*/
101119
public function __serialize(): array
102120
{
103-
return< 9E81 /span> [$this->htmlTemplate, $this->textTemplate, $this->context, parent::__serialize(), $this->locale];
121+
return [$this->htmlTemplate, $this->textTemplate, $this->context, parent::__serialize(), $this->locale, $this->subject];
104122
}
105123

106124
/**
@@ -110,6 +128,7 @@ public function __unserialize(array $data): void
110128
{
111129
[$this->htmlTemplate, $this->textTemplate, $this->context, $parentData] = $data;
112130
$this->locale = $data[4] ?? null;
131+
$this->subject = $data[5] ?? null;
113132

114133
parent::__unserialize($parentData);
115134
}

src/Symfony/Bridge/Twig/Tests/Mime/TemplatedEmailTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
use Symfony\Component\Serializer\Normalizer\MimeMessageNormalizer;
2121
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
2222
use Symfony\Component\Serializer\Normalizer\PropertyNormalizer;
23+
use Symfony\Component\Serializer\Normalizer\TranslatableNormalizer;
2324
use Symfony\Component\Serializer\Serializer;
25+
use Symfony\Component\Translation\IdentityTranslator;
26+
use Symfony\Component\Translation\TranslatableMessage;
2427

2528
class TemplatedEmailTest extends TestCase
2629
{
@@ -44,19 +47,22 @@ public function testSerialize()
4447
->htmlTemplate('text.html.twig')
4548
->context($context = ['a' => 'b'])
4649
->locale($locale = 'fr_FR')
50+
->subject($subject = new TranslatableMessage('hello {{ name }}', ['name' => 'John'], 'greetings'))
4751
;
4852

4953
$email = unserialize(serialize($email));
5054
$this->assertEquals('text.txt.twig', $email->getTextTemplate());
5155
$this->assertEquals('text.html.twig', $email->getHtmlTemplate());
5256
$this->assertEquals($context, $email->getContext());
5357
$this->assertEquals($locale, $email->getLocale());
58+
$this->assertEquals($subject, $email->getTranslatableSubject());
5459
}
5560

5661
public function testSymfonySerialize()
5762
{
5863
// we don't add from/sender to check that validation is not triggered to serialize an email
5964
$e = new TemplatedEmail();
65+
$e->subject(new TranslatableMessage('hello.world'));
6066
$e->to('you@example.com');
6167
$e->textTemplate('email.txt.twig');
6268
$e->htmlTemplate('email.html.twig');
@@ -67,6 +73,7 @@ public function testSymfonySerialize()
6773

6874
$expectedJson = <<<EOF
6975
{
76+
"subject": "hello.world",
7077
"htmlTemplate": "email.html.twig",
7178
"textTemplate": "email.txt.twig",
7279
"locale": "en",
@@ -84,6 +91,15 @@ public function testSymfonySerialize()
8491
}
8592
],
8693
"headers": {
94+
"subject": [
95+
{
96+
"value": "hello.world",
97+
"name": "Subject",
98+
"lineLength": 76,
99+
"lang": null,
100+
"charset": "utf-8"
101+
}
102+
],
87103
"to": [
88104
{
89105
"addresses": [
@@ -108,6 +124,7 @@ public function testSymfonySerialize()
108124
$serializer = new Serializer([
109125
new ArrayDenormalizer(),
110126
new MimeMessageNormalizer($propertyNormalizer),
127+
new TranslatableNormalizer(new IdentityTranslator()),
111128
new ObjectNormalizer(null, null, null, $extractor),
112129
$propertyNormalizer,
113130
], [new JsonEncoder()]);

src/Symfony/Bundle/TwigBundle/Resources/config/mailer.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@
1818
return static function (ContainerConfigurator $container) {
1919
$container->services()
2020
->set('twig.mailer.message_listener', MessageListener::class)
21-
->args([null, service('twig.mime_body_renderer')])
21+
->args([
22+
null,
23+
service('twig.mime_body_renderer'),
24+
'$translator' => service('translator')->ignoreOnInvalid(),
25+
])
2226
->tag('kernel.event_subscriber')
2327

2428
->set('twig.mime_body_renderer', BodyRenderer::class)

src/Symfony/Component/Mailer/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ CHANGELOG
99
* Add DSN param `source_ip` to allow binding to a (specific) IPv4 or IPv6 address.
1010
* Add DSN param `require_tls` to enforce use of TLS/STARTTLS
1111
* Add `DkimSignedMessageListener`, `SmimeEncryptedMessageListener`, and `SmimeSignedMessageListener`
12+
* Add support for translatable subject in `TemplatedEmail`
1213

1314
7.2
1415
---

src/Symfony/Component/Mailer/EventListener/MessageListener.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Mailer\EventListener;
1313

14+
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
1415
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
1516
use Symfony\Component\Mailer\Event\MessageEvent;
1617
use Symfony\Component\Mailer\Exception\InvalidArgumentException;
@@ -19,9 +20,11 @@
1920
use Symfony\Component\Mime\Header\Headers;
2021
use Symfony\Component\Mime\Header\MailboxListHeader;
2122
use Symfony\Component\Mime\Message;
23+
use Symfony\Component\Translation\TranslatableMessage;
24+
use Symfony\Contracts\Translation\TranslatorInterface;
2225

2326
/**
24-
* Manipulates the headers and the body of a Message.
27+
* Manipulates the headers, subject, and the body of a Message.
2528
*
2629
* @author Fabien Potencier <fabien@symfony.com>
2730
*/
@@ -45,6 +48,7 @@ public function __construct(
4548
private ?Headers $headers = null,
4649
private ?BodyRendererInterface $renderer = null,
4750
array $headerRules = self::DEFAULT_RULES,
51+
private ?TranslatorInterface $translator = null,
4852
) {
4953
foreach ($headerRules as $headerName => $rule) {
5054
$this->addHeaderRule($headerName, $rule);
@@ -68,6 +72,7 @@ public function onMessage(MessageEvent $event): void
6872
}
6973

7074
$this->setHeaders($message);
75+
$this->translateSubject($message);
7176
$this->renderMessage($message);
7277
}
7378

@@ -115,6 +120,15 @@ private function setHeaders(Message $message): void
115120
}
116121
}
117122

123+
private function translateSubject(Message $message): void
124+
{
125+
if (!$message instanceof TemplatedEmail || !$this->translator || !$message->getTranslatableSubject() instanceof TranslatableMessage) {
126+
return;
127+
}
128+
129+
$message->subject($message->getTranslatableSubject()->trans($this->translator, $message->getLocale()));
130+
}
131+
118132
private function renderMessage(Message $message): void
119133
{
120134
if (!$this->renderer) {

src/Symfony/Component/Mailer/Tests/EventListener/MessageListenerTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Mailer\Tests\EventListener;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
1516
use Symfony\Component\Mailer\Envelope;
1617
use Symfony\Component\Mailer\Event\MessageEvent;
1718
use Symfony\Component\Mailer\EventListener\MessageListener;
@@ -20,6 +21,8 @@
2021
use Symfony\Component\Mime\Header\MailboxListHeader;
2122
use Symfony\Component\Mime\Header\UnstructuredHeader;
2223
use Symfony\Component\Mime\Message;
24+
use Symfony\Component\Translation\TranslatableMessage;
25+
use Symfony\Contracts\Translation\TranslatorInterface;
2326

2427
class MessageListenerTest extends TestCase
2528
{
@@ -114,4 +117,25 @@ public static function provideHeaders(): iterable
114117
];
115118
yield 'Capitalized header rule (case-insensitive), replace if set' => [$initialHeaders, $defaultHeaders, $defaultHeaders, $rules];
116119
}
120+
121+
public function testTranslatableSubject()
122+
{
123+
$message = new TemplatedEmail();
124+
$message->subject(new TranslatableMessage('hello.world'));
125+
$listener = new MessageListener(translator: new class implements TranslatorInterface {
126+
public function trans(string $id, array $parameters = [], ?string $domain = null, ?string $locale = null): string
127+
{
128+
return 'Hello World';
129+
}
130+
131+
public function getLocale(): string
132+
{
133+
return 'en';
134+
}
135+
});
136+
$event = new MessageEvent($message, new Envelope(new Address('sender@example.com'), [new Address('recipient@example.com')]), 'smtp');
137+
$listener->onMessage($event);
138+
139+
$this->assertSame('Hello World', $message->getSubject());
140+
}
117141
}

src/Symfony/Component/Mailer/composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@
2121
"psr/event-dispatcher": "^1",
2222
"psr/log": "^1|^2|^3",
2323
"symfony/event-dispatcher": "^6.4|^7.0",
24-
"symfony/mime": "^7.2",
24+
"symfony/mime": "^7.3",
2525
"symfony/service-contracts": "^2.5|^3"
2626
},
2727
"require-dev": {
2828
"symfony/console": "^6.4|^7.0",
2929
"symfony/http-client": "^6.4|^7.0",
3030
"symfony/messenger": "^6.4|^7.0",
31+
"symfony/translation": "^6.4|^7.0",
3132
"symfony/twig-bridge": "^6.4|^7.0"
3233
},
3334
"conflict": {

src/Symfony/Component/Mime/Email.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ class Email extends Message
5858
/**
5959
* @return $this
6060
*/
61-
public function subject(string $subject): static
61+
public function subject(string|\Stringable $subject): static
6262
{
63-
return $this->setHeaderBody('Text', 'Subject', $subject);
63+
return $this->setHeaderBody('Text', 'Subject', (string) $subject);
6464
}
6565

6666
public function getSubject(): ?string

0 commit comments

Comments
 (0)
0