diff --git a/automatic_updates.module b/automatic_updates.module index 42d886df60ea0b595b9b4d68d240a24cf7d20c06..c602db75cf9a40a5d13e96fed820317ea4703ac2 100644 --- a/automatic_updates.module +++ b/automatic_updates.module @@ -38,15 +38,20 @@ function automatic_updates_help($route_name, RouteMatchInterface $route_match) { * Implements hook_mail(). */ function automatic_updates_mail(string $key, array &$message, array $params): void { + // Explicitly pass the language code to all translated strings. + $options = [ + 'langcode' => $message['langcode'], + ]; + if ($key === 'cron_successful') { - $message['subject'] = t("Drupal core was successfully updated"); - $message['body'][] = t('Congratulations!'); + $message['subject'] = t("Drupal core was successfully updated", [], $options); + $message['body'][] = t('Congratulations!', [], $options); $message['body'][] = t('Drupal core was automatically updated from @previous_version to @updated_version.', [ '@previous_version' => $params['previous_version'], '@updated_version' => $params['updated_version'], - ]); - $message['body'][] = t('This e-mail was sent by the Automatic Updates module. Unattended updates are not yet fully supported.'); - $message['body'][] = t('If you are using this feature in production, it is strongly recommended for you to visit your site and ensure that everything still looks good.'); + ], $options); + $message['body'][] = t('This e-mail was sent by the Automatic Updates module. Unattended updates are not yet fully supported.', [], $options); + $message['body'][] = t('If you are using this feature in production, it is strongly recommended for you to visit your site and ensure that everything still looks good.', [], $options); } } diff --git a/src/CronUpdater.php b/src/CronUpdater.php index 538e0c9bff48c1e180629432915e287d181d01df..23f6183eefbafad478fe086f3d33fd55f788b178 100644 --- a/src/CronUpdater.php +++ b/src/CronUpdater.php @@ -278,7 +278,7 @@ class CronUpdater extends Updater { $recipients = $this->configFactory->get('update.settings') ->get('notification.emails'); foreach ($recipients as $recipient) { - $this->mailManager->mail('automatic_updates', 'cron_successful', $recipient, $this->languageManager->getDefaultLanguage()->getId(), $mail_params); + $this->mailManager->mail('automatic_updates', 'cron_successful', $recipient, $this->getEmailLangcode($recipient), $mail_params); } } catch (\Throwable $e) { @@ -299,6 +299,23 @@ class CronUpdater extends Updater { return new Response(); } + /** + * Retrieves preferred language to send email. + * + * @param string $recipient + * The email address of the recipient. + * + * @return string + * The preferred language of the recipient. + */ + protected function getEmailLangcode(string $recipient): string { + $user = user_load_by_mail($recipient); + if ($user) { + return $user->getPreferredLangcode(); + } + return $this->languageManager->getDefaultLanguage()->getId(); + } + /** * Gets the cron update mode. * diff --git a/tests/modules/automatic_updates_test/automatic_updates_test.module b/tests/modules/automatic_updates_test/automatic_updates_test.module new file mode 100644 index 0000000000000000000000000000000000000000..bd140ddb95a993b7e4547b45fad71232f5e65f5a --- /dev/null +++ b/tests/modules/automatic_updates_test/automatic_updates_test.module @@ -0,0 +1,31 @@ +<?php + +/** + * @file + * Contains hook implementations for testing Automatic Updates. + */ + +use Drupal\Core\StringTranslation\TranslatableMarkup; + +/** + * Implements hook_mail_alter(). + */ +function automatic_updates_test_mail_alter(array &$message): void { + if (str_starts_with($message['id'], 'automatic_updates_')) { + $line_langcodes = []; + + // Get the langcode of every translated line in the mssage, including the + // subject line. + $lines = array_merge($message['body'], [ + $message['subject'], + ]); + foreach ($lines as $line) { + if (!($line instanceof TranslatableMarkup)) { + // All lines need to be translated. + return; + } + $line_langcodes[] = $line->getOption('langcode'); + } + $message['line_langcodes'] = array_unique($line_langcodes); + } +} diff --git a/tests/src/Kernel/CronUpdaterTest.php b/tests/src/Kernel/CronUpdaterTest.php index 96373d3946ab12af9e06f4295f3b9c02cd1e4faf..c0e4c0c8c67e0a12210a214610756fb33261c483 100644 --- a/tests/src/Kernel/CronUpdaterTest.php +++ b/tests/src/Kernel/CronUpdaterTest.php @@ -19,6 +19,7 @@ use Drupal\package_manager\Event\PreCreateEvent; use Drupal\package_manager\Exception\StageValidationException; use Drupal\package_manager\ValidationResult; use Drupal\Tests\package_manager\Traits\PackageManagerBypassTestTrait; +use Drupal\Tests\user\Traits\UserCreationTrait; use Drupal\update\UpdateSettingsForm; use Psr\Log\Test\TestLogger; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -33,6 +34,7 @@ class CronUpdaterTest extends AutomaticUpdatesKernelTestBase { use AssertMailTrait; use PackageManagerBypassTestTrait; + use UserCreationTrait; /** * {@inheritdoc} @@ -60,6 +62,8 @@ class CronUpdaterTest extends AutomaticUpdatesKernelTestBase { $this->container->get('logger.factory') ->get('automatic_updates') ->addLogger($this->logger); + $this->installEntitySchema('user'); + $this->installSchema('user', ['users_data']); } /** @@ -348,18 +352,48 @@ class CronUpdaterTest extends AutomaticUpdatesKernelTestBase { * Tests that user 1 is emailed when an unattended update succeeds. */ public function testEmailOnSuccess(): void { + $default_language = $this->container->get('language_manager') + ->getDefaultLanguage() + ->getId(); + $this->assertNotSame('fr', $default_language); + + $account = $this->createUser([], NULL, FALSE, [ + 'preferred_langcode' => 'fr', + ]); + + $recipients = [ + 'emissary@deep.space', + $account->getEmail(), + ]; + $languages = [ + $default_language, + $account->getPreferredLangcode(), + ]; + $this->config('update.settings') - ->set('notification.emails', [ - 'emissary@deep.space', - ]) + ->set('notification.emails', $recipients) ->save(); $this->container->get('cron')->run(); - // Check that we actually sent a success email to the right person. - $this->assertMail('to', 'emissary@deep.space'); - $this->assertMail('subject', "Drupal core was successfully updated"); - $this->assertMailString('body', "Congratulations!\n\nDrupal core was automatically updated from 9.8.0 to 9.8.1.\n", 1); + // Ensure we sent a success message to all recipients. + $sent_messages = $this->getMails([ + 'subject' => "Drupal core was successfully updated", + ]); + $this->assertSame(count($recipients), count($sent_messages)); + // Ensure the messages had the correct body text, and were sent to the right + // people. + foreach ($sent_messages as $i => $message) { + $this->assertSame($message['to'], $recipients[$i]); + $this->assertStringStartsWith("Congratulations!\n\nDrupal core was automatically updated from 9.8.0 to 9.8.1.\n", $message['body']); + // The message, and every line in it, should have been sent in the + // expected language. + $langcode = $message['langcode']; + $this->assertSame($langcode, $languages[$i]); + // @see automatic_updates_test_mail_alter() + $this->assertArrayHasKey('line_langcodes', $message); + $this->assertSame([$langcode], $message['line_langcodes']); + } } }