Skip to content

Commit 89d8015

Browse files
authored
fix: send transactional emails in recipient's preferred locale (#4627)
Switches translator to the recipient's locale preference (falling back to the forum default) before translating subject/body, and carries that locale through to SendInformationalEmailJob and SendAbandonedExtensionsEmailJob so the wrapper template's greeting/signoff render in the same language. For abandoned-extensions notifications, moves translation inside the per-admin loop so each admin receives the email in their own preferred locale rather than all admins receiving the same language. Mirrors the existing pattern in NotificationMailer. Refs #4626
1 parent b100154 commit 89d8015

7 files changed

Lines changed: 99 additions & 40 deletions

framework/core/src/Api/Controller/SendTestMailController.php

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,25 @@ public function handle(ServerRequestInterface $request): ResponseInterface
3535
$actor = RequestUtil::getActor($request);
3636
$actor->assertAdmin();
3737

38-
$this->queue->connection('sync')->push(
39-
new SendInformationalEmailJob(
40-
email: $actor->email,
41-
displayName: $actor->display_name,
42-
subject: $this->translator->trans('core.email.send_test.subject'),
43-
body: $this->translator->trans('core.email.send_test.body'),
44-
forumTitle: $this->settings->get('forum_title'),
45-
bodyTitle: $this->translator->trans('core.email.send_test.subject')
46-
)
47-
);
38+
$locale = $actor->getPreference('locale') ?? $this->settings->get('default_locale');
39+
$previousLocale = $this->translator->getLocale();
40+
$this->translator->setLocale($locale);
41+
42+
try {
43+
$this->queue->connection('sync')->push(
44+
new SendInformationalEmailJob(
45+
email: $actor->email,
46+
displayName: $actor->display_name,
47+
subject: $this->translator->trans('core.email.send_test.subject'),
48+
body: $this->translator->trans('core.email.send_test.body'),
49+
forumTitle: $this->settings->get('forum_title'),
50+
bodyTitle: $this->translator->trans('core.email.send_test.subject'),
51+
locale: $locale,
52+
)
53+
);
54+
} finally {
55+
$this->translator->setLocale($previousLocale);
56+
}
4857

4958
return new EmptyResponse();
5059
}

framework/core/src/Extension/AbandonedExtensionsFetcher.php

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@
1111

1212
use Flarum\Foundation\Application;
1313
use Flarum\Group\Group;
14+
use Flarum\Locale\TranslatorInterface;
1415
use Flarum\Mail\Job\SendAbandonedExtensionsEmailJob;
1516
use Flarum\Settings\SettingsRepositoryInterface;
1617
use Flarum\User\User;
1718
use GuzzleHttp\Client;
1819
use GuzzleHttp\Exception\GuzzleException;
1920
use Illuminate\Contracts\Queue\Queue;
2021
use RuntimeException;
21-
use Symfony\Contracts\Translation\TranslatorInterface;
2222

2323
class AbandonedExtensionsFetcher
2424
{
@@ -113,25 +113,36 @@ protected function notifyAdmins(array $newPackages, array $map): void
113113
$q->where('id', Group::ADMINISTRATOR_ID);
114114
})->get();
115115

116-
$lines = array_map(function (string $package) use ($map) {
117-
$replacement = $map[$package]['replacement'] ?? null;
118-
119-
return $replacement
120-
? $this->translator->trans('core.email.abandoned_extensions.line_with_replacement', compact('package', 'replacement'))
121-
: $this->translator->trans('core.email.abandoned_extensions.line_no_replacement', compact('package'));
122-
}, $newPackages);
123-
124-
$subject = $this->translator->trans('core.email.abandoned_extensions.subject');
125116
$forumTitle = $this->settings->get('forum_title', '');
117+
$defaultLocale = $this->settings->get('default_locale');
118+
$previousLocale = $this->translator->getLocale();
126119

127-
foreach ($admins as $admin) {
128-
$this->queue->push(new SendAbandonedExtensionsEmailJob(
129-
email: $admin->email,
130-
username: $admin->display_name,
131-
subject: $subject,
132-
extensionLines: $lines,
133-
forumTitle: $forumTitle,
134-
));
120+
try {
121+
foreach ($admins as $admin) {
122+
$locale = $admin->getPreference('locale') ?? $defaultLocale;
123+
$this->translator->setLocale($locale);
124+
125+
$lines = array_map(function (string $package) use ($map) {
126+
$replacement = $map[$package]['replacement'] ?? null;
127+
128+
return $replacement
129+
? $this->translator->trans('core.email.abandoned_extensions.line_with_replacement', compact('package', 'replacement'))
130+
: $this->translator->trans('core.email.abandoned_extensions.line_no_replacement', compact('package'));
131+
}, $newPackages);
132+
133+
$subject = $this->translator->trans('core.email.abandoned_extensions.subject');
134+
135+
$this->queue->push(new SendAbandonedExtensionsEmailJob(
136+
email: $admin->email,
137+
username: $admin->display_name,
138+
subject: $subject,
139+
extensionLines: $lines,
140+
forumTitle: $forumTitle,
141+
locale: $locale,
142+
));
143+
}
144+
} finally {
145+
$this->translator->setLocale($previousLocale);
135146
}
136147
}
137148

framework/core/src/Mail/Job/SendAbandonedExtensionsEmailJob.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
namespace Flarum\Mail\Job;
1111

12+
use Flarum\Locale\TranslatorInterface;
1213
use Flarum\Queue\AbstractJob;
1314
use Illuminate\Contracts\Mail\Mailer;
1415
use Illuminate\Contracts\View\Factory;
@@ -22,11 +23,16 @@ public function __construct(
2223
private readonly string $subject,
2324
private readonly array $extensionLines,
2425
private readonly string $forumTitle,
26+
private readonly ?string $locale = null,
2527
) {
2628
}
2729

28-
public function handle(Mailer $mailer, Factory $view): void
30+
public function handle(Mailer $mailer, Factory $view, TranslatorInterface $translator): void
2931
{
32+
if ($this->locale !== null) {
33+
$translator->setLocale($this->locale);
34+
}
35+
3036
$username = $this->username;
3137
$forumTitle = $this->forumTitle;
3238
$userEmail = $this->email;

framework/core/src/Mail/Job/SendInformationalEmailJob.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
namespace Flarum\Mail\Job;
1111

12+
use Flarum\Locale\TranslatorInterface;
1213
use Flarum\Queue\AbstractJob;
1314
use Illuminate\Contracts\Mail\Mailer;
1415
use Illuminate\Contracts\View\Factory;
@@ -26,12 +27,17 @@ public function __construct(
2627
protected array $views = [
2728
'text' => 'mail::plain.information.generic',
2829
'html' => 'mail::html.information.generic'
29-
]
30+
],
31+
private readonly ?string $locale = null,
3032
) {
3133
}
3234

33-
public function handle(Mailer $mailer, Factory $view): void
35+
public function handle(Mailer $mailer, Factory $view, TranslatorInterface $translator): void
3436
{
37+
if ($this->locale !== null) {
38+
$translator->setLocale($this->locale);
39+
}
40+
3541
$forumTitle = $this->forumTitle;
3642
$infoContent = $this->body;
3743
$userEmail = $this->email;

framework/core/src/User/AccountActivationMailerTrait.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,24 @@ protected function getEmailData(User $user, #[\SensitiveParameter] EmailToken $t
3636

3737
protected function sendConfirmationEmail(User $user, array $data): void
3838
{
39-
$body = $this->translator->trans('core.email.activate_account.body', $data);
40-
$subject = $this->translator->trans('core.email.activate_account.subject');
39+
$locale = $user->getPreference('locale') ?? $this->settings->get('default_locale');
40+
$previousLocale = $this->translator->getLocale();
41+
$this->translator->setLocale($locale);
42+
43+
try {
44+
$body = $this->translator->trans('core.email.activate_account.body', $data);
45+
$subject = $this->translator->trans('core.email.activate_account.subject');
46+
} finally {
47+
$this->translator->setLocale($previousLocale);
48+
}
4149

4250
$this->queue->push(new SendInformationalEmailJob(
4351
email: $user->email,
4452
displayName: Arr::get($data, 'username'),
4553
subject: $subject,
4654
body: $body,
47-
forumTitle: Arr::get($data, 'forum')
55+
forumTitle: Arr::get($data, 'forum'),
56+
locale: $locale,
4857
));
4958
}
5059
}

framework/core/src/User/EmailConfirmationMailer.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,24 @@ public function handle(EmailChangeRequested $event): void
3232
$email = $event->email;
3333
$data = $this->getEmailData($event->user, $email);
3434

35-
$body = $this->translator->trans('core.email.confirm_email.body', $data);
36-
$subject = $this->translator->trans('core.email.confirm_email.subject');
35+
$locale = $event->user->getPreference('locale') ?? $this->settings->get('default_locale');
36+
$previousLocale = $this->translator->getLocale();
37+
$this->translator->setLocale($locale);
38+
39+
try {
40+
$body = $this->translator->trans('core.email.confirm_email.body', $data);
41+
$subject = $this->translator->trans('core.email.confirm_email.subject');
42+
} finally {
43+
$this->translator->setLocale($previousLocale);
44+
}
3745

3846
$this->queue->push(new SendInformationalEmailJob(
3947
email: $email,
4048
displayName: Arr::get($data, 'username'),
4149
subject: $subject,
4250
body: $body,
43-
forumTitle: Arr::get($data, 'forum')
51+
forumTitle: Arr::get($data, 'forum'),
52+
locale: $locale,
4453
));
4554
}
4655

framework/core/src/User/Job/RequestPasswordResetJob.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,25 @@ public function handle(
5050
'forum' => $settings->get('forum_title'),
5151
];
5252

53-
$body = $translator->trans('core.email.reset_password.body', $data);
54-
$subject = $translator->trans('core.email.reset_password.subject');
53+
$locale = $user->getPreference('locale') ?? $settings->get('default_locale');
54+
$previousLocale = $translator->getLocale();
55+
$translator->setLocale($locale);
56+
57+
try {
58+
$body = $translator->trans('core.email.reset_password.body', $data);
59+
$subject = $translator->trans('core.email.reset_password.subject');
60+
} finally {
61+
$translator->setLocale($previousLocale);
62+
}
5563

5664
$queue->push(new SendInformationalEmailJob(
5765
email: $user->email,
5866
displayName: Arr::get($data, 'username'),
5967
subject: $subject,
6068
body: $body,
6169
forumTitle: Arr::get($data, 'forum'),
62-
bodyTitle: $subject
70+
bodyTitle: $subject,
71+
locale: $locale,
6372
));
6473
}
6574
}

0 commit comments

Comments
 (0)