Mails not being send in user preferred language

Created on 4 July 2023, 8 months ago
Updated 19 February 2024, 6 days ago

Problem/Motivation

When blocking or activating a user as admin the mails are not send in the user's preferred language.

Steps to reproduce

Go to the user overview.
Go the the edit page of a user.
Select a preferred language for the user other than the default site language.
Block the user.

User wil get a mail in the site default language.

Proposed resolution

Make sure the config translations of the policies are being loaded in the adjuster plugins.

Remaining tasks

User interface changes

API changes

Data model changes

🐛 Bug report
Status

Active

Version

1.0

Component

Code

Created by

🇧🇪Belgium Wouter Waeytens

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @Wouter Waeytens
  • 🇬🇧United Kingdom AdamPS

    I don't think that's the right way to do translation. The email body and subject come from a config entity. Instead you should use the Drupal mechanism for translations of config.

  • Status changed to Closed: works as designed 7 months ago
  • 🇬🇧United Kingdom AdamPS
  • Status changed to Active 25 days ago
  • 🇩🇪Germany Patrick R.

    While the proposed patch is certainly not the way to go the problem with mails being sent in the wrong language when using adjusters like SubjectEmailAdjuster or BodyEmailAdjuster still persists. The adjuster plugins are already instantiated during initEmail() in the EmailFactory class which is happening quite some time before the eventual language switch might happen in Mailer::doSend(), so there's no way to get mails sent in the correct language using these adjusters even when using config translation properly. Not seeing a simple solution for this though. :-/

  • 🇩🇪Germany Patrick R.

    Added the following as a workaround to my email builder plugin. Not overly pretty, but it seems to work. 🤷🏻‍♂️

    
    /**
     * {@inheritdoc}
     */
    public function preRender(EmailInterface $email) {
      $adjusters = ['email_body', 'email_subject'];
      $suggestions = $email->getSuggestions('', '.');
    
      $adjuster_manager = \Drupal::service('plugin.manager.email_adjuster');
      $policy_config = MailerPolicy::loadInheritedConfig(end($suggestions));
    
      foreach ($policy_config as $plugin_id => $config) {
        if (in_array($plugin_id, $adjusters) && $adjuster_manager->hasDefinition($plugin_id)) {
          $adjuster_manager->createInstance($plugin_id, $config)->build($email);
        }
      }
    }
    
    
  • 🇬🇧United Kingdom AdamPS

    Ah good point thanks for the update.

  • 🇦🇺Australia Mingsong 🇦🇺

    I tested this issue with
    Drupal 10.2.3
    Symfony Mailer 1.4.1

    And two issues I came across

    1. Imported policies didn't load the translation email content.
    2. Translate mailer policy wasn't applied to the email sent to the user in user's language.
  • 🇦🇺Australia Mingsong 🇦🇺

    Right. after debugging the code, I think I figure out what is happening here.

    In the Mailer::doSend() function,

    https://git.drupalcode.org/project/symfony_mailer/-/blob/1.x/src/Mailer....

    We have four phases to process an email.

    In phase 'build', https://git.drupalcode.org/project/symfony_mailer/-/blob/1.x/src/Mailer....

    The email body, subject and other contents were built in the default language since the configuration language hasn't been changed yet.

    After that, the language will be changed to user's language at line 224
    https://git.drupalcode.org/project/symfony_mailer/-/blob/1.x/src/Mailer....

    Next phase is 'PHASE_PRE_RENDER', in this phase, the $email->process() will call the 'preRend()' function for each email adjuster, including the subject and body adjuster.
    BodyEmailAdjuster https://git.drupalcode.org/project/symfony_mailer/-/blob/1.x/src/Plugin/...
    SubjectEmailAdjuster https://git.drupalcode.org/project/symfony_mailer/-/blob/1.x/src/Plugin/...

    But the problem is that those email address doesn't have the preRend() fuction, so they won't change anything. The email subject and body still remain in the default language.

    And so on, after this stage, all coming phase won't change the language at all.

    So that is why the email will always be built in the default language of the website.

  • 🇦🇺Australia Mingsong 🇦🇺

    Another problem with the translation of an email is that, the configuration of an adjuster plugin is loaded in the default language when the plugin is initiated.
    Even if the languageManager->setConfigOverrideLanguage($language) is called after that, the configuration loaded before won't be changed, until the configuration is loaded again.

    So if I add the following preRender function to SubjectEmailAdjuster class,

      public function preRender(EmailInterface $email) {
        $mail_config = \Drupal::config('symfony_mailer.mailer_policy.user.status_activated');
    
        $config = $mail_config->get('configuration.email_subject');
    
        if (isset($config['value'])) {
          $subject = $config['value'];
        }
        else {
          $subject = $this->configuration['value'];
        }
    
        $email->setSubject($subject, TRUE);
      }
    

    Then the email subject is translated to the user language.

  • 🇬🇧United Kingdom AdamPS

    Yes I feel that #6 and #10 both contain the seed of the solution. But both are hard-coded to a specific case of course we can't check them in as is.
    - The problem could apply to many adjusters (the plain text body; the addresses have a display name; even the skip sending has a displayed message)
    - And to any builder

    I propose that once we have chosen the language in Mailer.php then we should reload the policy with the correct language. The code that loaded them originally was in EmailFactory:

        $this->emailAdjusterManager->applyPolicy($email);
    

    However at the moment, Email::addProcessor() would also keep the original adjusters - they don't overwrite. We can fix that by changing the code to

    $this->processors[$phase][$id] = 
    

    But we still have a problem that the subject/body are being used mainly in the build() function that had already run. We also need a fix to 📌 Change to the phases Active .

  • 🇩🇪Germany Patrick R.

    I propose that once we have chosen the language in Mailer.php then we should reload the policy with the correct language. The code that loaded them originally was in EmailFactory:

    Might also be an idea to only instantiate adjuster plugins when they are applied during a certain phase and have the plugins use different phases - address adjusters should probably run earlier than e.g. subject or body adjusters since they could be responsible for determining the language of the email. SearchApiProcessor plugins use "stages" in their plugin annotations, maybe EmailAdjuster plugins should define "phases" in their annotations. 🤔

Production build https://api.contrib.social 0.61.6-2-g546bc20