Add the DispatchesTranslationEvents trait to have the model dispatch a single TranslationsChanged event once per persisted save when any translatable attribute changed. The base HasTranslations trait stays side-effect-free — nothing fires unless you opt in:
use RoundlyConsulting\Translatable\Concerns\DispatchesTranslationEvents;
use RoundlyConsulting\Translatable\Concerns\HasTranslations;
final class Topic extends Model implements Translatable
{
use HasTranslations;
use DispatchesTranslationEvents;
// …
}On create, the changed attributes are those with a non-empty map; on update, the translatable attributes that actually changed. A save that only touches non-translatable columns dispatches nothing. “Changed” means the decoded map changed: PostgreSQL and MySQL hand JSON back re-spaced and key-reordered, and re-saving an unchanged map is neither an UPDATE nor an event on any engine.
Listening
The event carries the model and the list of changed attribute names — ideal for queuing an AI service to fill missingLocales() or for busting a cache:
use Illuminate\Support\Facades\Event;
use RoundlyConsulting\Translatable\Events\TranslationsChanged;
Event::listen(TranslationsChanged::class, function (TranslationsChanged $event): void {
foreach ($event->changedAttributes as $attribute) { // list<string>
if ($event->model->missingLocales($attribute) !== []) {
FillMissingTranslations::dispatch($event->model, $attribute);
}
}
});| Property | Type | Description |
|---|---|---|
model | Model | The saved model (a Translatable). |
changedAttributes | list<string> | Translatable attributes that changed in this save. |
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.