Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Extended Localization Laravel Package

thettler/extended-localization

Extended localization for Laravel: manage translations in the database, add localized routes and locale switching, and extend Laravel's built-in localization with flexible, developer-friendly tools.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal path from composer require to a working DB-backed translation layer:

  1. Install and publish — composer require thettler/extended-localization then publish the config and migrations:
    php artisan vendor:publish --provider="Thettler\ExtendedLocalization\ExtendedLocalizationServiceProvider"
    php artisan migrate
    
  2. Read config/extended-localization.php first. This is the single highest-leverage file in the package. It controls the translation storage driver, cache behaviour, fallback chain, and (typically) the locale-resolution strategy. Everything else in the package is downstream of these keys.
  3. Run the import command to seed the database from your existing lang/ files. Check php artisan list for the package's import/export commands — importing rather than hand-inserting rows is the difference between a 10-minute setup and an afternoon of INSERT statements.
  4. Verify the swap happened. __('messages.welcome') in a Blade view should now resolve from the database rather than the filesystem. If it still reads files, the service provider isn't overriding the translator binding — check that the package provider is not deferred and is registered after TranslationServiceProvider.

First real use case: adding a locale your team can't ship through a deploy — e.g. a marketing team wants to correct a German string at 4pm without a release. That's the entire value proposition. Start there, not with a full migration of every string.

Starting from zero (v0.0.7): this is a 0.0.x release with no meaningful changelog (see Gotchas), so treat the README as the only source of truth and prototype against a throwaway database before touching a real locale set.

Implementation Patterns

Keep __() as the call site. The package's best trait is that it extends Laravel's localization rather than replacing it. Do not introduce a Translatable::get() facade across your codebase — you lose IDE support, the lang:cache workflow, and every third-party package that calls __() internally.

Seeder-first, then edits. Model your translations as seedable data so a fresh migrate:fresh --seed reproduces production:

// database/seeders/TranslationSeeder.php
// import from lang/ on first run, then let the DB become source of truth

Keep the lang/ files in the repo even after the switch. They are your fallback, your diff history, and your disaster recovery.

Locale resolution as middleware, not in controllers. Resolve the locale once, early, and store it on the request:

// app/Http/Middleware/SetLocale.php
public function handle($request, Closure $next)
{
    app()->setLocale(
        $request->user()?->locale
        ?? $request->session()->get('locale')
        ?? $request->getPreferredLanguage(config('app.supported_locales'))
    );
    return $next($request);
}

Register it in the web group before anything that renders views. Putting locale logic in individual controllers guarantees inconsistency six months later.

Cache aggressively, invalidate precisely. DB-backed translations mean a query per missing key unless the package's cache layer is enabled. Turn it on in config, then wire invalidation into your write path:

// after any translation create/update/delete
Cache::forget(config('extended-localization.cache_key'));

In dev, disable the cache entirely — stale translations are the #1 time sink when debugging "I changed it and nothing happened."

Integrate with existing model translation packages rather than replacing them. If you already use spatie/laravel-translatable for content models, let this package own UI strings and leave model attributes alone. Mixing the two into one table is a common and painful mistake.

Testing pattern: use RefreshDatabase plus the package's seeder so tests exercise the same loader path as production. For translation-specific assertions, assert on the resolved string, not on the DB row — the loader is what you actually care about.

Export back to files before major releases. A lang/ snapshot committed to git gives you reviewable diffs of translation changes, which the database alone does not.

Gotchas and Tips

v0.0.7 shipped with no release notes. The changelog is literally a compare link with zero described entries. Treat this release as unknown content: pin it exactly ("thettler/extended-localization": "0.0.7"), diff vendor/ against 0.0.6 if upgrading, and do not assume it's a patch-only bump. In a 0.0.x line, semver guarantees nothing — 0.0.7 → 0.0.8 can be a breaking change by design.

Pre-1.0 API churn is the dominant risk. Config keys, command signatures, and table schemas are all fair game to rename between patch releases. Encapsulate your usage: one middleware, one seeder, one service class. When the config keys move, you edit one file.

Migration ordering. The package's migration must run after your users table if it references a user ID for "last edited by" auditing. If vendor:publish drops the migration with a timestamp that sorts it before your own, rename the file.

Missing-key behaviour is the sharpest edge. Laravel returns the key itself when a translation is absent; a DB-backed loader can instead return null, '', or throw, depending on config. Decide deliberately and set it in config — silent empty strings in production are far worse than a visible messages.welcome.

Fallback chain ≠ filesystem fallback. With translations in the DB, Laravel's fallback_locale still applies, but the package may layer its own fallback on top. Test the exact case of "key exists in en, missing in de, user is de" — this is where packages diverge from native behaviour, and where users actually notice bugs.

Vendor translations don't automatically land in the DB. Spatie, Laravel's own validation messages, and every package's lang/vendor/ directory are filesystem-only. Either import them explicitly or leave them on the filesystem loader — decide per-namespace rather than globally.

Debugging checklist for "my translation isn't showing": (1) cache enabled and stale? (2) correct locale actually set on the container at render time — app()->getLocale() in a tinker session isn't proof? (3) key exists but under the wrong namespace? (4) config cached — php artisan config:clear after touching config/extended-localization.php?

Extension points to reach for before forking: the storage driver (if it's interface-backed, swap in your own repository rather than patching the package), the locale resolver in config, and any events fired on translation change — hooking an event to flush cache is cleaner than overriding the service provider. Forking a 0.0.x package means owning it forever.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels