Email Notifications in Different Languages
-
Hi,
First of all many thanks for this great plugin (I have the Pro version).
I am facing the following problem: I have a buyer that made a purchase in German (I am using Polylang):This buyer got the “Order Confirmation” email notification in German, the “Order shipped” email notification in English and the “Invoice (PDF)” in English.
Given that this order purchase was realized in German, I would like to have all email notifications in German.
How to solve this?
Worth mentioning that I am using the latest versions of Germanized and Germanized Pro.
Thanks!
-
Hi there,
we do not currently support PolyLang explicitly, that’s why translating those emails + documents via PolyLang will not work out of the box. We do offer a full compatibility with WPML though. Translating transactional data (e.g. orders, invoices, emails etc) is a “hard” thing to do with WP as WP does not provide functionality within it’s core to do so. All third-party plugins offer their own “tweaks” to integrate that but there is no real standard-compliant way to do so..
Best,
DennisHi Dennis,
Thank your for your answer.
It would be great to have Polylang support in the future.
In the meantime, is there a way to find/change the language of the emails? Polylang has a documentation on how to translate strings (as described here). I did not find the words stated in the “Order shipped” and “Invoice (PDF)” email notification, like:
“Your order on XXXX has been shipped via DHL. Find details below for your reference”
“Invoice 2024 XXXX has been attached to this email. Find details below for your reference”
My questions:
– Are these templates coming from Germanized? If yes, where are they located (path)?
– Can I force the template to be send out in German? Given the lack of support to Polylang, I would prefer to send all these emails in German.
Thanks and kind regardsHi,
Are these templates coming from Germanized? If yes, where are they located (path)?
Yes, they are part of Germanized. You may check the (original) template file via WooCommerce > Settings > E-Mails by clicking on the related mail (e.g. order shipped).
Can I force the template to be send out in German? Given the lack of support to Polylang, I would prefer to send all these emails in German.
Unfortunately that won’t work without providing a PolyLang compatibility as the language file (where those strings are registered/translated) is loaded/managed by WP. Only the current loaded language file (during the email sending request) is relevant here and would need to be loaded/unloaded dynamically based on the email’s language (or German).
Best,
Dennis- This reply was modified 3 months, 1 week ago by vendidero.
Hi, thank you for your answer.
So if I understood it right, the Germanized email templates like the customer-shipment.php retrieves the translations fromwp-content\languages\plugins\woocommerce-germanized-de_DE.l10n.php
.But how is the language retrieved from the user (or is it coming from the order)? Where does Germanized get this information?
Is there no other solution to solve the problem of emails for a same order being send into multiple languages?
Maybe you’ll need to learn more on how WP handles translations internally. There is no built-in way to switch between languages during one request. When sending emails, by default, Woo does not change the language based on the recipient. The language of the current request (depending on the language setup for the current logged-in user or site’s default) is used instead. Germanized does not deal with changing the language neither..
Is there no other solution to solve the problem of emails for a same order being send into multiple languages?
That’s what you are using PolyLang for I guess? As Germanized does not provide a built-in compatibility with that plugin it just won’t work..
Best,
DennisHi Dennis,
Thank you for your reply. I investigated it further and was able to partially solve the problem.
I get the language in which the order was placed in Polylang ($order_language = pll_get_post_language($order->get_id(), 'slug')
) and then I temporary switch the locale (switch_to_locale($order_locale)
) so that the email is send in the correct language. It works when I manually click to submit the email.
Unfortunately it’s not working when I submit the invoice to the client (and I expect that it will also not work when I send the order shipped notification to the client – but this was yet not tested).
Maybe Germanized uses custom hooks or methods for sending emails? Could you support me here?
Thanks!<?php
// WooCommerce - Order email notifications language set based on order using Polylang
// Last update: 2024-08-28
// To prevent issues where simultaneous purchases by different users in different languages might result in email notifications being sent in the wrong language, the code avoids using global variables. Instead, it handles locale switching in an isolated manner for each request.
if (class_exists('WooCommerce') && WC() && class_exists('Polylang')) {
class WC_Email_Locale_Handler
{
public function __construct()
{
// Hook into the email header to switch locale before email content
add_action($hook_name = 'woocommerce_email_header', $callback = [$this, 'set_email_locale_based_on_order'], $priority = 10, $accepted_args = 2);
// Hook into the email footer to restore the original locale after email content
add_action($hook_name = 'woocommerce_email_footer', $callback = [$this, 'restore_email_locale'], $priority = 10, $accepted_args = 1);
// Hook into resending emails to handle locale switching
add_action($hook_name = 'woocommerce_before_resend_order_emails', $callback = [$this, 'set_locale_before_resending_email'], $priority = 10, $accepted_args = 1);
}
public function set_email_locale_based_on_order($email_heading, $email)
{
if (isset($email->object) && is_a($email->object, 'WC_Order')) {
$order = $email->object;
if (function_exists('pll_get_post_language')) {
// Get the language slug of the order
$order_language = pll_get_post_language($order->get_id(), 'slug');
// Map the language slug to a locale code
$order_locale = $this->map_language_to_locale($order_language);
if ($order_locale) {
// Switch to the order's locale
switch_to_locale($order_locale);
}
}
}
}
public function restore_email_locale()
{
restore_previous_locale();
}
public function set_locale_before_resending_email($order)
{
if (is_a($order, 'WC_Order')) {
if (function_exists('pll_get_post_language')) {
// Get the language slug of the order
$order_language = pll_get_post_language($order->get_id(), 'slug');
// Map the language slug to a locale code
$order_locale = $this->map_language_to_locale($order_language);
if ($order_locale) {
// Switch to the order's locale
switch_to_locale($order_locale);
}
}
}
}
private function map_language_to_locale($language_slug)
{
// Define a mapping of language slugs to locale codes
$mapping = array(
'de' => 'de_DE',
'en' => 'en_US',
'pt' => 'pt_BR',
// Add more languages as needed
);
return isset($mapping[$language_slug]) ? $mapping[$language_slug] : false;
}
}
// Initialize the handler class
new WC_Email_Locale_Handler();
}No, that won’t work out of the box as there is no order object available to check when sending an invoice or shipment notification – the primary object (related to the email) differs. You might check the
$email->object
of the corresponding mail (e.g. invoice) and update your code to support those objects too. You might be able to retrieve the order (if it still exists) by special methods for those objects too, e.g. for a shipment: https://github.com/vendidero/woocommerce-germanized-shipments/blob/master/src/Shipment.php#L2537Hi Dennis,
Thank you for your answer. From here I understood that the shipment object is “Vendidero\Germanized\Shipments\Shipment“. Is that correct?
What would the invoice object be (I haven’t found this one in the documentation)?
I have updated the code and included a new function calledget_locale_from_object()
. The code works for re-submitting “Send order details to customer” and “Resend order confirmation” email notifications.
From what I understood from your message, I should pass$email->object
and check whether it is ais_a($email->object, 'Vendidero\Germanized\Shipments\Shipment')
object, if it is, I should get the$order = $email->object->get_order();
. Is that right?
Thanks!<?php
// WooCommerce - Order email notifications language set based on order using Polylang
// Last update: 2024-08-29
if (class_exists('WooCommerce') && WC() && class_exists('Polylang')) {
class WC_Email_Locale_Handler
{
public function __construct()
{
// Hook into the email header to switch locale before email content
add_action($hook_name = 'woocommerce_email_header', $callback = [$this, 'set_email_locale_based_on_order'], $priority = 10, $accepted_args = 2);
// Hook into the email footer to restore the original locale after email content
add_action($hook_name = 'woocommerce_email_footer', $callback = [$this, 'restore_email_locale'], $priority = 10, $accepted_args = 1);
// Hook into resending emails to handle locale switching
add_action($hook_name = 'woocommerce_before_resend_order_emails', $callback = [$this, 'set_locale_before_resending_email'], $priority = 10, $accepted_args = 1);
}
public function set_email_locale_based_on_order($email_heading, $email)
{
if (isset($email->object)) {
// Get the language locale of the order
$order_locale = $this->get_locale_from_object($email->object);
if ($order_locale) {
// Switch to the order's locale
switch_to_locale($order_locale);
}
}
}
public function restore_email_locale()
{
restore_previous_locale();
}
public function set_locale_before_resending_email($order)
{
if ($order) {
// Get the language locale of the order
$order_locale = $this->get_locale_from_object($order);
if ($order_locale) {
// Switch to the order's locale
switch_to_locale($order_locale);
}
}
}
private function map_language_to_locale($language_slug)
{
// Define a mapping of language slugs to locale codes
$mapping = array(
'de' => 'de_DE',
'en' => 'en_US',
'pt' => 'pt_BR',
// Add more languages as needed
);
return isset($mapping[$language_slug]) ? $mapping[$language_slug] : false;
}
private function get_locale_from_object($object)
{
$order = null;
// Check if the object is a WC_Order
if (is_a($object, 'WC_Order')) {
$order = $object;
}
// Check if the object is a Shipment from Germanized
elseif (is_a($object, 'Vendidero\Germanized\Shipments\Shipment')) {
$order = $object->get_order();
}
if ($order && function_exists('pll_get_post_language')) {
// Get the language slug of the order
$order_language = pll_get_post_language($order->get_id(), 'slug');
// Map the language slug to a locale code
return $this->map_language_to_locale($order_language);
}
return null;
}
}
// Initialize the handler class
new WC_Email_Locale_Handler();
}Yes, that’s right. The invoice is a
Vendidero\StoreaBill\Invoice\Invoice
object. You may find the definition withinwoocommerce-germanized-pro/packages/storeabill/src/Invoice
.Thanks for the quick answer! I have just added “Vendidero\StoreaBill\Invoice\Invoice” to the code and it worked as expected! The invoice notification email was sent in the language that the customer ordered.
Are there any additional objects besides shipping object (“Vendidero\Germanized\Shipments\Shipment“) and invoice object (“Vendidero\StoreaBill\Invoice\Invoice“) I should consider on my code?
Also, is there any method you recommend me to use to check whether Germanized plugin is activated? I am currently using:if (class_exists('Vendidero\Germanized\Packages')
.
Thanks!<?php
// WooCommerce - Order email notifications language set based on order using Polylang
// Last update: 2024-08-29
if (class_exists('WooCommerce') && WC() && class_exists('Polylang')) {
class WC_Email_Locale_Handler
{
public function __construct()
{
// Hook into the email header to switch locale before email content
add_action($hook_name = 'woocommerce_email_header', $callback = [$this, 'set_email_locale_based_on_order'], $priority = 10, $accepted_args = 2);
// Hook into the email footer to restore the original locale after email content
add_action($hook_name = 'woocommerce_email_footer', $callback = [$this, 'restore_email_locale'], $priority = 10, $accepted_args = 1);
// Hook into resending emails to handle locale switching
add_action($hook_name = 'woocommerce_before_resend_order_emails', $callback = [$this, 'set_locale_before_resending_email'], $priority = 10, $accepted_args = 1);
}
public function set_email_locale_based_on_order($email_heading, $email)
{
if (isset($email->object)) {
// Get the language locale of the order
$order_locale = $this->get_locale_from_object($email->object);
if ($order_locale) {
// Switch to the order's locale
switch_to_locale($order_locale);
}
}
}
public function restore_email_locale()
{
restore_previous_locale();
}
public function set_locale_before_resending_email($order)
{
if ($order) {
// Get the language locale of the order
$order_locale = $this->get_locale_from_object($order);
if ($order_locale) {
// Switch to the order's locale
switch_to_locale($order_locale);
}
}
}
private function map_language_to_locale($language_slug)
{
// Define a mapping of language slugs to locale codes
$mapping = array(
'de' => 'de_DE',
'en' => 'en_US',
'pt' => 'pt_BR',
// Add more languages as needed
);
return isset($mapping[$language_slug]) ? $mapping[$language_slug] : false;
}
private function get_locale_from_object($object)
{
$order = null;
// Check if the object is a WC_Order
if (is_a($object, 'WC_Order')) {
$order = $object;
}
// Check if the object is a Shipment or Invoice from Germanized
elseif (class_exists('Vendidero\Germanized\Packages') && (is_a($object, 'Vendidero\Germanized\Shipments\Shipment') || is_a($object, 'Vendidero\StoreaBill\Invoice\Invoice'))) {
$order = $object->get_order();
}
if ($order && function_exists('pll_get_post_language')) {
// Get the language slug of the order
$order_language = pll_get_post_language($order->get_id(), 'slug');
// Map the language slug to a locale code
return $this->map_language_to_locale($order_language);
}
return null;
}
}
// Initialize the handler class
new WC_Email_Locale_Handler();
}No, not really (at least not very relevant to emails). You do not need to check for plugin existence as you are doing a
is_a
check.Hi Dennis, thanks – I will share the final code below in case anyone else needs it.
Thank you for your support.<?php
// WooCommerce - Order email notifications language set based on order using Polylang
// Last update: 2024-08-29
// To prevent issues where simultaneous purchases by different users in different languages might result in email notifications being sent in the wrong language, the code avoids using global variables. Instead, it handles locale switching in an isolated manner for each request.
if (class_exists('WooCommerce') && WC() && class_exists('Polylang')) {
class WC_Email_Locale_Handler
{
public function __construct()
{
// Hook into the email header to switch locale before email content
add_action($hook_name = 'woocommerce_email_header', $callback = [$this, 'set_email_locale_based_on_order'], $priority = 10, $accepted_args = 2);
// Hook into the email footer to restore the original locale after email content
add_action($hook_name = 'woocommerce_email_footer', $callback = [$this, 'restore_email_locale'], $priority = 10, $accepted_args = 1);
// Hook into resending emails to handle locale switching
add_action($hook_name = 'woocommerce_before_resend_order_emails', $callback = [$this, 'set_locale_before_resending_email'], $priority = 10, $accepted_args = 1);
}
public function set_email_locale_based_on_order($email_heading, $email)
{
if (isset($email->object)) {
// Get the language locale of the order
$order_locale = $this->get_locale_from_object($email->object);
if ($order_locale) {
// Switch to the order's locale
switch_to_locale($order_locale);
}
}
}
public function restore_email_locale()
{
restore_previous_locale();
}
public function set_locale_before_resending_email($order)
{
if ($order) {
// Get the language locale of the order
$order_locale = $this->get_locale_from_object($order);
if ($order_locale) {
// Switch to the order's locale
switch_to_locale($order_locale);
}
}
}
private function map_language_to_locale($language_slug)
{
// Define a mapping of language slugs to locale codes
$mapping = array(
'de' => 'de_DE',
'en' => 'en_US',
'pt' => 'pt_BR',
// Add more languages as needed
);
return isset($mapping[$language_slug]) ? $mapping[$language_slug] : false;
}
private function get_locale_from_object($object)
{
$order = null;
// Check if the object is a WC_Order
if (is_a($object, 'WC_Order')) {
$order = $object;
}
// Check if the object is a Shipment or Invoice from Germanized for WooCommerce
elseif (is_a($object, 'Vendidero\Germanized\Shipments\Shipment') || is_a($object, 'Vendidero\StoreaBill\Invoice\Invoice')) {
$order = $object->get_order();
}
if ($order && function_exists('pll_get_post_language')) {
// Get the language slug of the order
$order_language = pll_get_post_language($order->get_id(), 'slug');
// Map the language slug to a locale code
return $this->map_language_to_locale($order_language);
}
return null;
}
}
// Initialize the handler class
new WC_Email_Locale_Handler();
}I had to make some changes to work also with the email subject and email content heading. For reference, the updated code can be found here: https://stackoverflow.com/a/79002683/9195104
Hi Dennis,
Unfortunately I have noted that my latest code (posted in Stack Overflow) and the original code (posted in this thread) do not translate, for the invoice emails, the subject line (“Invoice 2024 xxx from website”) and the word “Invoice” in the email content header and the content (“Hi first name last name, Invoice 2024 xxx…”).
Probably I need to triggeradd_action
before hooks not yet added to my code. I did not find where. Could you please support me?
Maybe also needed for shipping information, but I was not able to test the need for it.
Thanks!Not sure if you got a notification, thus replying to this topic: @vendidero
Kind regards
- You must be logged in to reply to this topic.