Temporarily change the current language during a request Background information

I am developing a multilingual Drupal 8 commerce site where only authenticated users (customers) can place orders (anonymous users can't place orders). Customers can edit their preferred language using the default language field on their account. After an order is placed, another user with a special role (webmaster) has to prepare the order and physically fill a box with the contents of the order. When the webmaster is ready, he performs a transition on the order changing it's state. This transition triggers an e-mail that is sent to the customer, informing him that his order is ready for shipment. This e-mail is triggered through an event subscriber that reacts on the shipping transition of orders. The e-mail is formed by rendering a custom template.

Problem

A customer who's preferred language is Dutch, has placed an order. A webmaster is using the website in English and sets the order 'ready for shipment'. The customer gets an English e-mail while he should get a Dutch e-mail.

Code

The code that triggers the e-mail:

/** @var \Drupal\commerce_order\Entity\OrderInterface $order */
$order = $event-&gt;getEntity();

$to = $order-&gt;getCustomer()-&gt;getEmail();
$langcode = $order-&gt;getCustomer()-&gt;getPreferredLangcode();
$subject = $this-&gt;t('Your order #@order_number is packed', ['@order_number' =&gt; $order-&gt;getOrderNumber()], ['langcode' =&gt; $langcode]);

$build = [
  '#theme' =&gt; 'commerce_order_shipped_mail',
  '#subject' =&gt; $subject,
  '#order_number' =&gt; $order-&gt;getOrderNumber(),
  '#order_date' =&gt; $this-&gt;dateFormatter-&gt;format($order-&gt;getCompletedTime(), 'default_date_only'),
  '#order_url' =&gt; Url::fromRoute('entity.commerce_order.user_view', [
    'user' =&gt; $order-&gt;getCustomer()-&gt;id(),
    'commerce_order' =&gt; $order-&gt;id(),
  ], ['absolute' =&gt; TRUE]),
];

$mail = $this-&gt;mailManager-&gt;mail('order_shipped_mail', $to, $subject, $build, $langcode);


The mail function of the mail manger (wrapper):

$name = \Drupal::config('system.site')-&gt;get('name');
$email = \Drupal::config('system.site')-&gt;get('mail');
$from = '"' . $name . '" &lt;' . $email . '&gt;';

$params = [
  'headers' =&gt; [
    'Content-Type' =&gt; 'text/html; charset=UTF-8; format=flowed; delsp=yes',
    'Content-Transfer-Encoding' =&gt; '8Bit',
    'From' =&gt; $from,
  ],
  'from' =&gt; $from,
  'subject' =&gt; $subject,
  'body' =&gt; $this-&gt;renderer-&gt;renderRoot($body),
];

$langcode = $langcode ?: $this-&gt;languageManager-&gt;getDefaultLanguage()-&gt;getId();

return $this-&gt;mailManager-&gt;mail('XXX_general', $key, $to, $langcode, $params);


The custom template:

&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
    &lt;td&gt;
        &lt;h2&gt;{{ 'Shipping confirmation'|t }}&lt;/h2&gt;
        &lt;p&gt;{{ 'Dear customer,'|t }}&lt;/p&gt;
        &lt;p&gt;
            {% trans %}
            Your order #{{ order_number }} of {{ order_date }} has been handed over to XXX.&lt;br /&gt;
            You will receive an email from XXX with the tracking information of your package.
            {% endtrans %}
        &lt;/p&gt;

        &lt;p&gt;&lt;a href="{{ order_url }}" target="{{ 'Order details'|t }}"&gt;{{ 'Order details'|t }}&lt;/a&gt;&lt;/p&gt;

        &lt;p&gt;The XXX team&lt;/p&gt;
    &lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;




As you can see a custom template is rendered that contains the body of the e-mail.

Actual question

How can I temporarily change the current language during a request so rendering happens in the correct language? I know I can give a langcode to a t-function but my rendered order URL would still be an English URL so this doesn't fix the problem. I have tried suggestions from here, and I thought the LanguageNegotiation Plugin would do the trick, but it doesn't.

Please excuse me if my question is malformed or if I provided to little information.
See How to progragrammatically get NodeType translated value in D8?, that example is actually based on user_mail() which has exactly the same use case, to send things like password reset e-mails in the language that the user set in his profile.

That will however only work for stored configuration. I think you can't easily change the active interface language, what you can do is explicitly pass the language to each t() call, also inside twig, at least for t().

The only thing I can think of right now that might allow to override it is do a fake negotiator, set that with setNegotiator() on the language manager, then at the end set back the original one.