Home » Php » php – Translate queued mails (localization)

php – Translate queued mails (localization)

Posted by: admin July 12, 2020 Leave a comment

Questions:

I am looking for a working solution, to translate queued emails in .
Unfortunately, all emails use the default locale (defined under app.locale).

Let’s assume, we have two emails in the pipeline, one for an English en user and another for an Japanese jp user.

What data should I pass to the Mail facade to translate (localize) the queued emails?

  // User model
  $user = User:find(1)->first();

  Mailer::queue($email, 'Party at Batman\'s cave (Batcave)', 'emails.party-invitation', [

    ...

    'locale' => $user->getLocale(), // value: "jp", but does not work
    'lang' => $user->getLocale(), // value: "jp", but does not work
    'language' => $user->getLocale(), // value: "jp", but does not work
  ]);
How to&Answers:

I have been struggling to get this done in a more efficient way. Currently I have it set up like this. Hopefully this helps someone in the future with this issue:

// Fetch the locale of the receiver.
$user = Auth::user();
$locale = $user->locale;
Mail::queue('emails.welcome.template', ['user' => $user, 'locale' => $locale], function($mail) use ($user, $locale) {
     $mail->to($user->email);
     $mail->subject(
          trans(
               'mails.subject_welcome',
               [], null, $locale
          )
     );
});

And use the following in your template:

{{ trans('mails.welcome', ['name' => ucfirst($user['first_name'])], null, $locale) }}

Note: do not forget to restart your queue

Answer:

If your emails inherits the built-in Illuminate\Mail\Mailable class you can build your own Mailable class that will take care of translations.

Create your own SendQueuedMailable class that inherits from Illuminate\Mail\SendQueuedMailable.
Class constructor will take current app.location from config and remember it in a property (because laravel queues serializes it).
In queue worker it will take the property back from config and set the setting to the current environment.

<?php

namespace App\Mail;

use Illuminate\Contracts\Mail\Mailer as MailerContract;
use Illuminate\Contracts\Mail\Mailable as MailableContract;
use Illuminate\Mail\SendQueuedMailable as IlluminateSendQueuedMailable;

class SendQueuedMailable extends IlluminateSendQueuedMailable
{
    protected $locale;

    public function __construct(MailableContract $mailable)
    {
        parent::__construct($mailable);

        $this->locale = config('app.locale');
    }

    public function handle(MailerContract $mailer)
    {
        config(['app.locale' => $this->locale]);
        app('translator')->setLocale($this->locale);

        parent::handle($mailer);
    }
}

Then, create your own Mail class that inherits from Illuminate\Mail\Mailable

<?php

namespace App\Mail;

use Illuminate\Contracts\Queue\Factory as Queue;
use Illuminate\Mail\Mailable as IlluminateMailable;

class Mailable extends IlluminateMailable
{
    public function queue(Queue $queue)
    {
        $connection = property_exists($this, 'connection') ? $this->connection : null;

        $queueName = property_exists($this, 'queue') ? $this->queue : null;

        return $queue->connection($connection)->pushOn(
            $queueName ?: null, new SendQueuedMailable($this)
        );
    }
}

And, voila, all your queued mailables inherited from App\Mailable class automatically will take care of current locale.

Answer:

In Laravel 5.6 is a locale function added to Mailable:
$infoMail->locale(‘jp’);
Mail::queue($infoMail);

Answer:

Laravel 5.7.7 introduced the HasLocalePreference interface to solve this issue.

class User extends Model implements HasLocalePreference
{
    public function preferredLocale() { return $this->locale; }
}

Now if you use Mail::to() function, Laravel will send with the correct locale.

Also introduced was the Mail chainable locale() function.

Mail::to($address)->locale($locale)->send(new Email());

Answer:

Here’s a solution that worked for me. In my example, I am processing the booking on a queue, using a dedicated Job class.

When you dispatch a job on a queue, pass the locale, you want your email in. For example:

ProcessBooking::dispatch($booking, \App::getLocale());

Then, in your job class (ProcessBooking in my example) store the locale in a property:

protected $booking;
protected $locale;

public function __construct(Booking $booking, $locale)
{
    $this->booking = $booking;
    $this->locale = $locale;
}

And in your handle method temporarily switch locales:

public function handle()
{
    // store the locale the queue is currently running in
    $previousLocale = \App::getLocale();

    // change the locale to the one you need for job to run in
    \App::setLocale($this->locale);

    // Do the stuff you need, e.g. send an email
    Mail::to($this->booking->customer->email)->send(new NewBooking($this->booking));

    // go back to the original locale
    \App::setLocale($previousLocale);
}

Why do we need to do this?

Queued emails get the default locale since queue workers are separate Laravel apps. If you ever ran into a problem, when you cleared your cache, but queued ‘stuff’ (e.g. emails) still showed the old content, it’s because of that. Here’s an explanation from Laravel docs:

Remember, queue workers are long-lived processes and store the booted application state in memory. As a result, they will not notice changes in your code base after they have been started. So, during your deployment process, be sure to restart your queue workers.

Answer:

I’m faced with the same problem

Without extending the queue class the quickest / dirtiest solution would be to create an email template for each locale.

Then when you create the queue, select the local template i.e.

Mail::queue('emails.'.App::getLocale().'notification', function($message) 
{
    $message->to($emails)->subject('hello');
});

If the local is set to IT (italian) This will load the view emails/itnotification.blade.php

As I said… Dirty!

Since this method is actually horrible I came across this answer
And its working for me, you actually send the entire translated html version of the email in a variable to the queue and have a blank blade file that echos out the variable when the queue runs.