Skip to content

Commit

Permalink
feature #51690 [Mime] Add TemplatedEmail::locale() to set the local…
Browse files Browse the repository at this point in the history
…e for the email rendering (alexander-schranz)

This PR was squashed before being merged into the 6.4 branch.

Discussion
----------

[Mime] Add `TemplatedEmail::locale()` to set the locale for the email rendering

| Q             | A
| ------------- | ---
| Branch?       | 6.4
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Tickets       | Fix #... <!-- prefix each issue number with "Fix #", no need to create an issue if none exists, explain below instead -->
| License       | MIT
| Doc PR        | symfony/symfony-docs#... <!-- required for new features -->

Add LocaleSwitcher to TemplatedEmail BodyRenderer. Currently it is not possible to render a twig template or a converter in a specific locale context.

It is common to send the email in the language of the receiver.

### Usage

```php
$email = (new TemplatedEmail())
    ->to('fabien@symfony.com')
    ->from('helene@symfony.com')
    ->subject('Hallo!')
    ->locale('de')
    ->textTemplate('email.txt.twig')
    ->htmlTemplate('email.html.twig')
    ->context($context)
;
 ```

Commits
-------

d912384 [Mime] Add `TemplatedEmail::locale()` to set the locale for the email rendering
  • Loading branch information
fabpot committed Oct 1, 2023
2 parents 9778b08 + d912384 commit decb566
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 23 deletions.
3 changes: 2 additions & 1 deletion src/Symfony/Bridge/Twig/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ CHANGELOG
6.4
---

* Allow an array to be passed as the first argument to the `importmap()` Twig function
* Allow an array to be passed as the first argument to the `importmap()` Twig function
* Add `TemplatedEmail::locale()` to set the locale for the email rendering

6.3
---
Expand Down
53 changes: 34 additions & 19 deletions src/Symfony/Bridge/Twig/Mime/BodyRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Symfony\Component\Mime\HtmlToTextConverter\HtmlToTextConverterInterface;
use Symfony\Component\Mime\HtmlToTextConverter\LeagueHtmlToMarkdownConverter;
use Symfony\Component\Mime\Message;
use Symfony\Component\Translation\LocaleSwitcher;
use Twig\Environment;

/**
Expand All @@ -28,12 +29,14 @@ final class BodyRenderer implements BodyRendererInterface
private Environment $twig;
private array $context;
private HtmlToTextConverterInterface $converter;
private ?LocaleSwitcher $localeSwitcher = null;

public function __construct(Environment $twig, array $context = [], HtmlToTextConverterInterface $converter = null)
public function __construct(Environment $twig, array $context = [], HtmlToTextConverterInterface $converter = null, LocaleSwitcher $localeSwitcher = null)
{
$this->twig = $twig;
$this->context = $context;
$this->converter = $converter ?: (interface_exists(HtmlConverterInterface::class) ? new LeagueHtmlToMarkdownConverter() : new DefaultHtmlToTextConverter());
$this->localeSwitcher = $localeSwitcher;
}

public function render(Message $message): void
Expand All @@ -47,30 +50,42 @@ public function render(Message $message): void
return;
}

$messageContext = $message->getContext();
$callback = function () use ($message) {
$messageContext = $message->getContext();

if (isset($messageContext['email'])) {
throw new InvalidArgumentException(sprintf('A "%s" context cannot have an "email" entry as this is a reserved variable.', get_debug_type($message)));
}
if (isset($messageContext['email'])) {
throw new InvalidArgumentException(sprintf('A "%s" context cannot have an "email" entry as this is a reserved variable.', get_debug_type($message)));
}

$vars = array_merge($this->context, $messageContext, [
'email' => new WrappedTemplatedEmail($this->twig, $message),
]);
$vars = array_merge($this->context, $messageContext, [
'email' => new WrappedTemplatedEmail($this->twig, $message),
]);

if ($template = $message->getTextTemplate()) {
$message->text($this->twig->render($template, $vars));
}
if ($template = $message->getTextTemplate()) {
$message->text($this->twig->render($template, $vars));
}

if ($template = $message->getHtmlTemplate()) {
$message->html($this->twig->render($template, $vars));
}
if ($template = $message->getHtmlTemplate()) {
$message->html($this->twig->render($template, $vars));
}

$message->markAsRendered();

$message->markAsRendered();
// if text body is empty, compute one from the HTML body
if (!$message->getTextBody() && null !== $html = $message->getHtmlBody()) {
$text = $this->converter->convert(\is_resource($html) ? stream_get_contents($html) : $html, $message->getHtmlCharset());
$message->text($text, $message->getHtmlCharset());
}
};

// if text body is empty, compute one from the HTML body
if (!$message->getTextBody() && null !== $html = $message->getHtmlBody()) {
$text = $this->converter->convert(\is_resource($html) ? stream_get_contents($html) : $html, $message->getHtmlCharset());
$message->text($text, $message->getHtmlCharset());
$locale = $message->getLocale();

if ($locale && $this->localeSwitcher) {
$this->localeSwitcher->runWithLocale($locale, $callback);

return;
}

$callback();
}
}
16 changes: 16 additions & 0 deletions src/Symfony/Bridge/Twig/Mime/TemplatedEmail.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class TemplatedEmail extends Email
{
private ?string $htmlTemplate = null;
private ?string $textTemplate = null;
private ?string $locale = null;
private array $context = [];

/**
Expand All @@ -42,6 +43,16 @@ public function htmlTemplate(?string $template): static
return $this;
}

/**
* @return $this
*/
public function locale(?string $locale): static
{
$this->locale = $locale;

return $this;
}

public function getTextTemplate(): ?string
{
return $this->textTemplate;
Expand All @@ -52,6 +63,11 @@ public function getHtmlTemplate(): ?string
return $this->htmlTemplate;
}

public function getLocale(): ?string
{
return $this->locale;
}

/**
* @return $this
*/
Expand Down
22 changes: 20 additions & 2 deletions src/Symfony/Bridge/Twig/Tests/Mime/BodyRendererTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
use Symfony\Component\Mime\HtmlToTextConverter\DefaultHtmlToTextConverter;
use Symfony\Component\Mime\HtmlToTextConverter\HtmlToTextConverterInterface;
use Symfony\Component\Mime\Part\Multipart\AlternativePart;
use Symfony\Component\Translation\LocaleSwitcher;
use Twig\Environment;
use Twig\Loader\ArrayLoader;
use Twig\TwigFunction;

class BodyRendererTest extends TestCase
{
Expand Down Expand Up @@ -131,20 +133,36 @@ public function testRenderedOnceUnserializableContext()
$this->assertEquals('Text', $email->getTextBody());
}

private function prepareEmail(?string $text, ?string $html, array $context = [], HtmlToTextConverterInterface $converter = null): TemplatedEmail
public function testRenderWithLocale()
{
$localeSwitcher = new LocaleSwitcher('en', []);
$email = $this->prepareEmail(null, 'Locale: {{ locale_switcher_locale() }}', [], new DefaultHtmlToTextConverter(), $localeSwitcher, 'fr');

$this->assertEquals('Locale: fr', $email->getTextBody());
$this->assertEquals('Locale: fr', $email->getHtmlBody());
}

private function prepareEmail(?string $text, ?string $html, array $context = [], HtmlToTextConverterInterface $converter = null, LocaleSwitcher $localeSwitcher = null, string $locale = null): TemplatedEmail
{
$twig = new Environment(new ArrayLoader([
'text' => $text,
'html' => $html,
'document.txt' => 'Some text document...',
'image.jpg' => 'Some image data',
]));
$renderer = new BodyRenderer($twig, [], $converter);

if ($localeSwitcher instanceof LocaleSwitcher) {
$twig->addFunction(new TwigFunction('locale_switcher_locale', [$localeSwitcher, 'getLocale']));
}

$renderer = new BodyRenderer($twig, [], $converter, $localeSwitcher);
$email = (new TemplatedEmail())
->to('fabien@symfony.com')
->from('helene@symfony.com')
->locale($locale)
->context($context)
;

if (null !== $text) {
$email->textTemplate('text');
}
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Bridge/Twig/Tests/Mime/TemplatedEmailTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public function testSymfonySerialize()
$e->to('you@example.com');
$e->textTemplate('email.txt.twig');
$e->htmlTemplate('email.html.twig');
$e->locale('en');
$e->context(['foo' => 'bar']);
$e->addPart(new DataPart('Some Text file', 'test.txt'));
$expected = clone $e;
Expand All @@ -66,6 +67,7 @@ public function testSymfonySerialize()
{
"htmlTemplate": "email.html.twig",
"textTemplate": "email.txt.twig",
"locale": "en",
"context": {
"foo": "bar"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use Symfony\Component\Form\Form;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Translation\LocaleSwitcher;
use Symfony\Component\Translation\Translator;
use Symfony\Contracts\Service\ResetInterface;
use Twig\Extension\ExtensionInterface;
Expand Down Expand Up @@ -85,6 +86,10 @@ public function load(array $configs, ContainerBuilder $container)
if ($htmlToTextConverter = $config['mailer']['html_to_text_converter'] ?? null) {
$container->getDefinition('twig.mime_body_renderer')->setArgument('$converter', new Reference($htmlToTextConverter));
}

if (ContainerBuilder::willBeAvailable('symfony/translation', LocaleSwitcher::class, ['symfony/framework-bundle'])) {
$container->getDefinition('twig.mime_body_renderer')->setArgument('$localeSwitcher', new Reference('translation.locale_switcher'));
}
}

if ($container::willBeAvailable('symfony/asset-mapper', AssetMapper::class, ['symfony/twig-bundle'])) {
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Bundle/TwigBundle/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"composer-runtime-api": ">=2.1",
"symfony/config": "^6.1|^7.0",
"symfony/dependency-injection": "^6.1|^7.0",
"symfony/twig-bridge": "^6.3|^7.0",
"symfony/twig-bridge": "^6.4|^7.0",
"symfony/http-foundation": "^5.4|^6.0|^7.0",
"symfony/http-kernel": "^6.2|^7.0",
"twig/twig": "^2.13|^3.0.4"
Expand Down

0 comments on commit decb566

Please sign in to comment.