Проблема. Локаль - це стан поточного процесу: middleware встановлює App::setLocale('uk') для запиту. Але:
- адміністратор з англійським інтерфейсом змінює статус замовлення - і лист клієнту генерується англійською;
- воркер черги та запланована команда взагалі не мають запиту - вони працюють з локаллю за замовчуванням з
config/app.php; - воркер обробляє завдання різних користувачів поспіль - локаль, встановлена одним завданням через
setLocale, «протікає» в наступні.
Рішення 1 - явна локаль при надсиланні:
Mail::to($customer)->locale('uk')->queue(new OrderShipped($order));
$user->notify((new InvoicePaid($invoice))->locale('pl'));
Laravel запам'ятовує локаль у самому завданні, і воркер перемикається на неї лише на час рендерингу листа, а потім повертає попередню.
Рішення 2 - бажана локаль на моделі (краще):
use Illuminate\Contracts\Translation\HasLocalePreference;
class User extends Authenticatable implements HasLocalePreference
{
public function preferredLocale(): string
{
return $this->locale ?? config('app.locale');
}
}
Тепер усі листи й сповіщення для цього користувача автоматично йдуть його мовою - незалежно від того, хто і звідки їх надіслав. Викликати locale() не потрібно.
Що ще залежить від локалі - і про що забувають:
- дати й числа:
Carbonмає власну локаль;$date->translatedFormat('j F')у листі має відповідати мові отримувача; - URL у листах: якщо мова закодована в адресі (
/uk/orders/42), посилання мають генеруватися для локалі отримувача, а не поточного запиту; - тема листа, що формується в
envelope()через__(), теж рендериться в локалі завдання - тож перекладайте її там, а не в конструкторі (конструктор виконується в локалі відправника); - довільні завдання, що формують тексти (PDF, експорт), - для них перемикання не автоматичне:
use Illuminate\Support\Traits\Localizable;
class GenerateInvoicePdf implements ShouldQueue
{
use Localizable;
public function handle(): void
{
$this->withLocale($this->user->preferredLocale(), fn () => $this->render());
}
}
Трейт Localizable (його ж використовують Mailable і відправник сповіщень) повертає попередню локаль у блоці finally - навіть після винятку, на відміну від ручного setLocale.
Тести: відправити лист користувачу з локаллю uk, коли застосунок працює з en, і перевірити assertSeeInHtml('Замовлення відправлено') - цей тест ловить більшість проблем.
Докладніше в документації: Пошта: бажані локалі користувачів