pugx/i18n-bundle
PUGX I18nBundle brings database-backed translations to Symfony: store messages in Doctrine entities, edit them via your admin, and let a translator service fall back to standard YAML/XLIFF files. Includes import/export commands and caching for fast lookups.
This is a Symfony bundle, not a Laravel package — and it is archived. Do not add it to a Laravel app's composer.json expecting a service provider; there is nothing here that Laravel's container will auto-discover. Treat it as reference material for the "translatable entity" pattern, not as a dependency.
Where to look first:
PUGX\I18nBundle\Doctrine — the event listeners/subscribers that do the actual work (locale resolution on postLoad, metadata on loadClassMetadata).I18n/translator and locale-resolution services — this is where the current-locale + fallback chain lives.OneToMany to a per-locale translation entity.First use case, translated into Laravel terms: a Post model with a post_translations table (locale, title, body, post_id) where reading $post->title returns the row matching app()->getLocale(), falling back to config('app.fallback_locale'). Read the bundle's listener to see how it picks the locale from the request and how it walks the fallback chain, then reimplement that in an Eloquent accessor or a global scope.
If you are genuinely on an old Symfony 2/3/4 project: require it, register PUGXI18nBundle in AppKernel, configure locale and fallback_locale, annotate the entity. That path is legacy-only.
The core pattern worth stealing — parent + translation table. One row per locale, joined back to the parent. All the bundle does is hide the join behind the entity's getters. In Laravel:
// Model accessor with fallback
public function getTitleAttribute(): ?string
{
return $this->translation()?->title
?? $this->translations->firstWhere('locale', config('app.fallback_locale'))?->title;
}
Locale resolution from the request. The bundle reads _locale at kernel.request time and stores it in a locale-aware service. Laravel equivalent: middleware that calls App::setLocale() from a route segment, session, or Accept-Language header, before any model is touched.
Doctrine listeners → Eloquent hooks. Their postLoad maps to booted() model events or a global scope; their loadClassMetadata maps to schema/migration conventions. If you're bridging a shared Symfony domain layer into Laravel, keep the translation entity a plain POPO and do the locale swap in a Laravel accessor — don't let the bundle's listener leak into your Eloquent models.
Write path. The bundle assumes one form/CRUD per locale over the translation collection. In Laravel, use a nested translations[] payload validated with translations.*.locale unique rules, then updateOrCreate per locale.
Caching. Locale lookups are per-request; eager-load translations in list queries and cache resolved translations for hot read paths.
with('translations')._locale. The request listener never fires there; set the locale explicitly in jobs and commands or you'll write translations into the wrong bucket.TranslatableInterface contract plus the fallback-chain service. Define your own interface and a TranslationResolver bound in the container — that gives you a testable seam the original never quite had.astrotomic/laravel-translatable or spatie/laravel-translatable for the data model, mcamara/laravel-localization for the routing/locale layer. Use this bundle only as a design reference.How can I help you explore Laravel packages today?