Save which language was used during checkout

Created on 14 September 2022, over 2 years ago
Updated 12 July 2023, over 1 year ago

Describe your bug or feature request.

We use Commerce Email module to send notification e-mails when the payment was received and the package is sent to the customer. The problem is if the user didn't register an account, we don't know which language to use in the later e-mails. The order itself doesn't contain any info about that.

I would like to suggest that we save the language code when the order is placed:

$order->setData('checkout_langcode', \Drupal::languageManager()->getCurrentLanguage()->getId());

If a bug, provide steps to reproduce it from a clean install.

Feature request
Status

Needs review

Version

2.0

Component

Internationalization

Created by

🇸🇰Slovakia kaszarobert

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

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • Open in Jenkins → Open on Drupal.org →
    Core: 9.5.x + Environment: PHP 8.0 & MySQL 5.7
    last update over 1 year ago
    785 pass
  • 🇭🇺Hungary tikaszvince

    reroll patch to make possible to apply on 2.36

  • 🇩🇪Germany Anybody Porta Westfalica

    I just ran into something related and I'm not sure, if it should be solved combined here or as separate issue. I'm NOT using the patch from this issue (yet):

    If you create an order in one language, proceed with the first checkout step (confirm so that the order status changes from cart to something else) and then go back to a regular page with cart, the cart is empty, if you're changing to a different language. Going back into the checkout with that different language, also shows the checkout empty.

    Returning to the original language, the cart content and the checkout shows again. Of course, it would be expected ot see the contents in both languages, just like before the checkout started.

    Is this closely related to this issue or should I better create a separate one?
    Can you reproduce it?

  • 🇫🇷France flocondetoile Lyon

    Is this closely related to this issue

    I don't think. This issue is about adding the langcode on the order once it's completed. To be able to know in which language the order has been completed.

    Can you reproduce it?

    Tried on one of my commerce project and I don't reproduce your issue. I wonder if you do't have some custom code / contrib module which implements permissions based on the language for product / order item.

  • 🇩🇪Germany Anybody Porta Westfalica

    @flocondetoile thanks for your feedback! I'll try to reproduce that in a vanilla Drupal Commerce installation. No modules for that, but of course might be a side effect of another module. I'll try and if I'm able to reproduce it, create a separate issue, thanks!

  • 🇮🇹Italy trickfun

    Path stores checkout language but how can send checkout receipt or resend receipt in checkout language?

    Now i receive mail with user preferred language for some text and user interface language for other text.

    May be usefull to resend mail in checkout language form admin.
    Thank you

  • 🇩🇪Germany Anybody Porta Westfalica

    I guess this still makes sense. At least when finishing the order, the language should be stored into the order entity, so if the user is anonymous or not, we're able to determine in which language the order was made and base all further emails on that.

    At least for customers with a user account, I don't see many further use-cases. Eventually, if you provide further after-sales things like parcel tracking or sth. like that based on the order, it will also be useful.

    All in all it totally makes sense logically to have that information in the order.

  • 🇩🇪Germany Anybody Porta Westfalica

    I guess this should go into 3.0.x then? Do we need anything else here, besides preparing a fresh MR?

    Should the use of this for the order confirmation email be done here or in a follow-up? A related issue at commerce_email should be created, once this is ready.

    Any other final comments @jsacksick? Guess it makes sense to move this over the finish line for the reasons given above...

  • 🇷🇸Serbia bojanz

    There's also the older discussion at 📌 Clarify order language behavior Needs work which might have important information.

  • 🇨🇭Switzerland tcrawford

    I am not sure the checkout completion event is a reliable point in time to set the language, as order completion can occur in the IPN/webhoook and the user may not return to the site.

    Order completion is also not reliable as the completion may occur in the webhook, where the language context is not available (as not all payment gateways provide a way to set the callback URL per order).

  • 🇨🇭Switzerland tcrawford

    I have created a separate module with an event subscriber to set the checkout language on the order. The rational for a separate module is that it can be activated or de-activated, thereby avoiding the need for configuration. I use both the add to cart and checkout completion events (see the prior comment). I can create the module if you agree, otherwise, I can update the MR for core (with tests) if agreed.

    <?php
    
    declare(strict_types=1);
    
    namespace Drupal\commerce_checkout_language\EventSubscriber;
    
    use Drupal\commerce_cart\Event\CartEntityAddEvent;
    use Drupal\commerce_cart\Event\CartEvents;
    use Drupal\commerce_checkout\Event\CheckoutEvents;
    use Drupal\commerce_order\Entity\OrderInterface;
    use Drupal\commerce_order\Event\OrderEvent;
    use Drupal\Core\Language\LanguageManagerInterface;
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    
    /**
     * Sets the checkout language on the order.
     */
    final class CommerceOrderLanguageSubscriber implements EventSubscriberInterface {
    
      /**
       * Constructs a CommerceCheckoutLanguageSubscriber object.
       */
      public function __construct(
        private readonly LanguageManagerInterface $languageManager,
      ) {}
    
      /**
       * {@inheritdoc}
       */
      public static function getSubscribedEvents(): array {
        return [
          CheckoutEvents::COMPLETION => ['onCheckoutCompletion'],
          CartEvents::CART_ENTITY_ADD => ['onCartEntityAdd'],
        ];
      }
    
      /**
       * Respond to the checkout completion event.
       *
       * @param \Drupal\commerce_order\Event\OrderEvent $event
       *   The order event.
       */
      public function onCheckoutCompletion(OrderEvent $event): void {
        // The checkout completion event requires the user to return to the checkout
        // after any offsite gateway.
        $this->setOrderLanguage($event->getOrder());
      }
    
      /**
       * Respond to the cart entity add event.
       *
       * @param \Drupal\commerce_cart\Event\CartEntityAddEvent $event
       *   The cart entity add event.
       */
      public function onCartEntityAdd(CartEntityAddEvent $event): void {
        // The cart entity add event is fired when a purchasable entity is added to
        // the cart. This is used as a default, which may be overridden by the
        // checkout completion event. We do this to ensure that the order language
        // is set even if the order is completed in the IPN (and the user does not
        // return to the checkout).
        $this->setOrderLanguage($event->getCart());
      }
    
      /**
       * Sets the order language to the current language.
       *
       * @param \Drupal\commerce_order\Entity\OrderInterface $order
       *   The order.
       */
      protected function setOrderLanguage(OrderInterface $order): void {
        $order->setData('checkout_language', $this->languageManager->getCurrentLanguage()->getId());
      }
    
    }
    
    

    I will also open an issue on the Drupal Symfony Mailer module (which includes the CommerceOrderEmailBuilder), which will include the following patch:

    From 6cd4e1fbf60ad99d6673996b50fd0a5566fa5cb0 Mon Sep 17 00:00:00 2001
    From: Trent Crawford <trent.crawford@liip.ch>
    Date: Mon, 6 Jan 2025 14:47:35 +0100
    Subject: [PATCH] Set the language based on the checkout language.
    
    ---
     .../CommerceOrderEmailBuilder.php             | 22 +++++++++++++++++++
     1 file changed, 22 insertions(+)
    
    diff --git a/src/Plugin/EmailBuilder/CommerceOrderEmailBuilder.php b/src/Plugin/EmailBuilder/CommerceOrderEmailBuilder.php
    index 601cc8b..656833b 100644
    --- a/src/Plugin/EmailBuilder/CommerceOrderEmailBuilder.php
    +++ b/src/Plugin/EmailBuilder/CommerceOrderEmailBuilder.php
    @@ -5,6 +5,7 @@ namespace Drupal\symfony_mailer\Plugin\EmailBuilder;
     use Drupal\commerce_order\Entity\OrderInterface;
     use Drupal\commerce_order\Entity\OrderType;
     use Drupal\symfony_mailer\Address;
    +use Drupal\symfony_mailer\AddressInterface;
     use Drupal\symfony_mailer\EmailFactoryInterface;
     use Drupal\symfony_mailer\EmailInterface;
     use Drupal\symfony_mailer\Entity\MailerPolicy;
    @@ -92,6 +93,27 @@ class CommerceOrderEmailBuilder extends EmailBuilderBase {
           ->setVariable('order_number', $order->getOrderNumber())
           ->setVariable('store', $store->getName());
     
    +
    +    // This is a workaround to set the email language based on the
    +    // order checkout language.
    +    if ($checkout_language = $order->getData('checkout_language')) {
    +      // Calling setTo() above is still required to ensure we get the
    +      // list of addresses.
    +      $to_addresses = $email->getTo();
    +      // In this case we likely only have one to address.
    +      foreach ($to_addresses as $index => $to_address) {
    +        if ($to_address instanceof AddressInterface) {
    +          $to_addresses[$index] = new Address(
    +            $to_address->getEmail(),
    +            $to_address->getDisplayName(),
    +            $checkout_language,
    +            $to_address->getAccount()
    +          );
    +        }
    +      }
    +      $email->setTo($to_addresses);
    +    }
    +
         // Get the actual email value without picking up the default from the site
         // mail. Instead we prefer to default from Mailer policy.
         if ($store_email = $store->get('mail')->value) {
    -- 
    2.47.1
    
    

    I will open an issue on commerce core with an MR:

    From 344ef06b869b9535b2b558df380deb1146a0a5c7 Mon Sep 17 00:00:00 2001
    From: Trent Crawford <trent.crawford@liip.ch>
    Date: Mon, 6 Jan 2025 16:29:52 +0100
    Subject: [PATCH] Send the email in the checkout language if available.
    
    ---
     modules/order/src/Mail/OrderReceiptMail.php | 7 +++++++
     1 file changed, 7 insertions(+)
    
    diff --git a/modules/order/src/Mail/OrderReceiptMail.php b/modules/order/src/Mail/OrderReceiptMail.php
    index 67affa1e..ff0db9c7 100644
    --- a/modules/order/src/Mail/OrderReceiptMail.php
    +++ b/modules/order/src/Mail/OrderReceiptMail.php
    @@ -43,6 +43,8 @@ class OrderReceiptMail implements OrderReceiptMailInterface {
         $order_type = $order_type_storage->load($order->bundle());
         $subject = $order_type->getReceiptSubject();
         if (!empty($subject)) {
    +      // @todo Support token replacement in the checkout language.
    +      // @see Drupal\commerce\MailHandler::changeActiveLanguage().
           $subject = $this->token->replace($subject, [
             'commerce_order' => $order,
           ]);
    @@ -73,6 +75,11 @@ class OrderReceiptMail implements OrderReceiptMailInterface {
           $params['langcode'] = $customer->getPreferredLangcode();
         }
     
    +    // Use the checkout language if available.
    +    if ($checkout_language = $order->getData('checkout_language')) {
    +      $params['langcode'] = $checkout_language;
    +    }
    +
         return $this->mailHandler->sendMail($to, $subject, $body, $params);
       }
     
    -- 
    2.47.1
    
    
    

    I am welcome to any feedback on the approach.

    The commerce email module will potentially also need an update for this.

Production build 0.71.5 2024