Reputation: 10082
I am looking for a working solution, to translate queued emails in laravel-5.
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
]);
Upvotes: 8
Views: 5410
Reputation: 540
using preferredLocale()
is the "official" way to go.
you can implement HasLocalePreference and add method preferredLocale() to the notifiable model (as @Peter-M suggested):
use Illuminate\Contracts\Translation\HasLocalePreference;
class User extends Model implements HasLocalePreference
{
/**
* Get the user's preferred locale.
*
* @return string
*/
public function preferredLocale()
{
return $this->locale;
}
}
and then you just send the notification to that model and locale will be automatically applied to your email template
$user->notify(new InvoicePaid($invoice));
This also works with queued mail notifications.
read more here: https://floyk.com/en/post/how-to-change-language-for-laravel-email-notifications
Upvotes: 0
Reputation: 2994
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
Upvotes: 9
Reputation: 145
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());
Upvotes: 2
Reputation: 31
In Laravel 5.6 is a locale function added to Mailable: $infoMail->locale('jp'); Mail::queue($infoMail);
Upvotes: 3
Reputation: 3998
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.
Upvotes: 0
Reputation: 522
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.
Upvotes: 3
Reputation: 1122
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.
Upvotes: -1