Issue Summary Updates
The original issue discovery by OP was finding email's content and it's recovery reset URL doesn't aligned to the preferred langcode of the user, when site is set to multilingual with path prefix. In short, we see that the email content is french, but the password reset URL comes as "/en/user/reset/..." .
There are a total of three API setting $langcode along the way to set the language of recovery email.
- submitForm() in UserPasswordFrom.php, taking the current browsing langcode and passed to _user_mail_notify().
- _user_mail_notify in user.module line 1042, it has a condition written to use the $langcode as $account->getPreferredLangcode if not specified. Here is where it sends an email request by invoking MailManagerInterface::mail() service and passing $langcode in its parameter, which goes through user_mail (hook_mail) to modify it's email contents.
- user_mail in user.module line 772, it has set to retrieve it's email content base on getPreferredLangcode of User while token option was set to follow the $message[langcode] variable from MailManagerInterface::mail().
The descriptions above reveals the bug how email's text content doesn't align with content replaced by tokens (URL with langcode prefix in this case) is within user_mail, whereas it should use the langcode that _user_mail_notify() provided as said by @jonathanshaw #23.
To understand the details of discussion, you can start from #21.
On the aspect of account recovery email does not respect user account's preferred language is due to the $langcode set in submitForm (UserPasswordForm.php), further discussion can be found at #86287: "reset password" page ignores the user's language selection.
Original Problem/Motivation
In the user_mail function, the language manager is set to the preferred langcode of the user.
$language = $language_manager->getLanguage($params['account']->getPreferredLangcode());
$original_language = $language_manager->getConfigOverrideLanguage();
$language_manager->setConfigOverrideLanguage($language);
In some cases if we load config, the mail_config is translated in the language of the user:
$mail_config = \Drupal::config('user.mail');
While loading the token options, an other langcode is set?!
Steps required to reproduce the bug
While sending a "Password Recovery" mail, the mail subject and body is in the language of the user.
The [user:one-time-login-url] token is in another language.
Solution, see patch.
Proposed resolution
New proposal
Quoting #23:
The only thing we need to fix is in user_mail():
$language = $language_manager->getLanguage($params['account']->getPreferredLangcode());
should become
$language = $language_manager->getLanguage($langcode);
user_mail() should use the langcode that _user_mail_notify() has provided.
Original proposal
Set the correct langcode in $token_options array:
$token_options = ['langcode' => $language->getId(), 'callback' => 'user_mail_tokens', 'clear' => TRUE];
See Patch.
Remaining tasks
Review patch.
User interface changes
None
API changes
None
Data model changes
Assures the language content from token replacement is the same as email content by allowing user_mail() to following the same langcode passed from it's parameter.