On-Demand and Localized Notifications

+15 Mana ✨

Introduction

Sometimes you need to notify someone who isn't a User in your database — a one-off email to a prospect, an SMS to a shipping carrier's webhook endpoint, a Slack alert to a channel that isn't tied to any specific user. Laravel's Notification::route() is the escape hatch, and ->locale() makes sure the recipient gets the message in their language, not yours.

Key Concepts

  • On-demand notification: A notification dispatched without a notifiable model. You pass the delivery addresses directly to Notification::route().
  • Localized notification: A notification rendered in a specific locale, overriding the app default for this one send.

Real World Context

A signup form that invites a friend needs to email someone who doesn't have an account yet. A status page needs to alert an ops Slack channel that isn't a Team model. A multilingual SaaS needs to email Japanese customers in Japanese even though the app default is English.

Deep Dive

On-demand delivery

php
use Illuminate\Support\Facades\Notification;
use App\Notifications\InvoiceOverdue;

Notification::route('mail', 'billing@acme.example')
    ->route('slack', 'https://hooks.slack.com/services/...')
    ->route('vonage', '+15551234567')
    ->notify(new InvoiceOverdue($invoice));

Each route() call pairs a channel name with a delivery address. When notify() runs, Laravel builds a transient "anonymous notifiable" and dispatches the notification as usual.

Localizing a single send

php
$user->notify(
    (new InvoiceOverdue($invoice))->locale('ja')
);

Or from the facade:

php
Notification::locale('ja')->send($user, new InvoiceOverdue($invoice));

The notification is rendered with App::setLocale('ja') active, so every __('messages.greeting') call inside toMail() resolves against the Japanese translation files. After the send, the locale snaps back to the request default.

Per-user localization

If your User model stores a preferred locale, implement HasLocalePreference:

php
use Illuminate\Contracts\Translation\HasLocalePreference;

class User extends Authenticatable implements HasLocalePreference
{
    public function preferredLocale(): string
    {
        return $this->locale ?? 'en';
    }
}

Now every notification sent to a User is auto-localized — no explicit ->locale() call needed.

Common Pitfalls

  1. Hardcoding recipient strings in your notification class — keep addresses on the caller side via route(), so the notification class stays reusable.
  2. Forgetting that ->locale() only affects translation lookups — it doesn't magically translate the static strings you wrote inline in the MailMessage. Run everything through __() or trans() so localization actually kicks in.

Best Practices

  1. Implement HasLocalePreference on User once — then every notification is auto-localized and you never forget.
  2. Centralize webhook URLs in config — don't hardcode https://hooks.slack.com/... in the controller.

Summary

  • Notification::route() sends to addresses that aren't bound to a model.
  • ->locale('ja') renders a single notification in a specific language.
  • HasLocalePreference on the notifiable model auto-localizes every send.
✓ Completed