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

Laravel Translation Loader Laravel Package

mgcodeur/laravel-translation-loader

A Laravel package that swaps the default file-based translator for a database-driven loader, letting you store, manage, and edit translations in the database. Includes caching, migration and model support, and works with Laravel's existing trans() and __() helpers.

View on GitHub
Deep Wiki
Context7

Getting Started

This package swaps Laravel's default file-based translation loader for a database-backed one, so __(), @lang, and the Translator facade keep working unchanged while the strings live in a table you can edit at runtime (admin panel, CMS, per-tenant overrides). Version 1.2.0 widens the support matrix to Laravel 10/11/12/13 and PHP 8.1–8.4, so it drops into both legacy and freshly created applications.

Minimal path from composer require mgcodeur/laravel-translation-loader:

  1. Publish the package assets — php artisan vendor:publish --provider="Mgcodeur\TranslationLoader\TranslationLoaderServiceProvider" (add --tag=config / --tag=migrations to be selective). You want the config file and the translations migration.
  2. Run the migration — php artisan migrate. Confirm the table exists before touching any Blade view; an empty table means every __('...') silently falls back to the key string, which looks like a broken install.
  3. Check the service provider registration — the package must rebind translation.loader in its register() method. If you have a TranslationServiceProvider override or a package that also touches the loader (e.g. a tenant-aware translator), verify boot order: the last binding wins.
  4. Seed or import your existing strings — either write a seeder, or iterate lang/**/*.php and insert one row per key/locale. A one-off artisan command is worth the 20 lines; without it you'll ship a UI with nothing in it.
  5. First real use case — a small admin CRUD screen over the translations table so a content editor can fix copy without a deploy. That is where the package pays for itself; everything else (tenant overrides, A/B copy) builds on the same table.

Look first at the published config (it controls connection, table name, and whether the DB loader is active) and the loader class itself — that's the whole contract, and reading it takes five minutes. Then confirm behaviour with php artisan tinker: app('translator')->get('messages.welcome') should hit the database, and you can watch it in DB::listen().

Implementation Patterns

Drop-in replacement, not a parallel API. Keep calling __('messages.welcome'), trans_choice, and @lang exactly as before. The value of the package is that no view code changes; migrating means moving rows, not rewriting templates. Resist the temptation to inject the model into views.

Import once, then treat the DB as the source of truth. A typical workflow: keep lang/ in git as the canonical set of keys for code review and CI, and run an idempotent translations:import (update-or-create on locale + group + key) as part of deploy, followed by an export step if editors changed rows. Decide early whether the file tree or the table wins on conflict — mixing both without a rule produces the classic "my edit disappeared after deploy" ticket.

Locale and group namespacing. Mirror Laravel's own semantics: JSON-style keys ("Welcome back") versus group files (messages.welcome) behave differently in the loader, and vendor namespaces (package::file.key) need a column or parse convention. Model the table with a unique index on (locale, group, key) — you will otherwise accumulate duplicates that resolve unpredictably depending on query order.

Tenancy and per-user overrides. Because the loader is just a resolver, a clean pattern is a scoped resolver: resolve the tenant from the request, query the tenant's rows, fall back to the global row, then to the file loader. Implement it as a decorator around the package's loader rather than editing vendor code — the package's binding is the seam.

Cache the result, not the query. Translation reads happen dozens of times per request. Wrap the loader in a cache layer keyed on locale (and tenant), invalidated on write. Laravel's translator already memoises within a request, so the win is across requests.

Testing. Use RefreshDatabase plus a seeder for the handful of keys the suite asserts on; don't assert against production copy. Keep a thin test asserting that a known key returns the seeded value in both the default and fallback locale — that catches binding regressions after a framework upgrade far faster than a full feature test.

Upgrading to 1.2.0. Bump and run the suite on your actual Laravel/PHP pair. The CI matrix in the release excludes some framework/PHP combinations, so verify your exact pairing rather than assuming the constraint solver's choice is a tested one.

Gotchas and Tips

  • Empty table is not an error. Missing keys render as the key itself (or the fallback locale's value). Always check fallback_locale behaviour first when a string "doesn't translate".
  • Config and route caching. After php artisan config:cache, the published config is frozen; changing the connection or table name requires a re-cache. In deployment scripts, order matters: migrate → import → config:cache → optimize.
  • Loader binding order. If another package or your AppServiceProvider rebinds translation.loader, the DB loader silently stops being used and everything falls back to files. Add the tinker check above to your smoke tests.
  • N+1 queries. A naive loader runs one query per group per locale. Loading a page with ten groups is ten queries. Eager-load whole locales into cache on first access rather than per-key lookups.
  • Pluralisation and replacements. trans_choice and :name replacement are handled by Laravel, not the loader — but your stored strings must still contain the | separators and placeholders. Editors will delete them; validate on save.
  • Escaping. Stored strings bypass the file-based review that used to catch stray HTML. If any translation is rendered unescaped in Blade, treat the admin UI as untrusted input.
  • PHP 8.1 support is new in 1.2.0. It's genuinely useful for older apps, but it also means the package's minimum is now lower than many of your other dependencies'. Let Composer resolve rather than pinning; run the suite on 8.1 if you actually target it, since CI coverage there is thinner.
  • Laravel 13 support is forward-looking. Confirm your other packages support 13 before upgrading the framework just because this one does. The orchestra/testbench ^11.0 and pest ^4.0 bumps are dev-only — they don't affect your runtime, but they do signal the package's own tests now target the newer toolchain.
  • Extension points. The service provider binding and the loader class are the intended seams; there's no formal extension API documented as such, so prefer decorating the binding over forking. If you need write-through behaviour, put it in your own repository class and keep the package as a read path.
  • Debugging. DB::listen() plus a breakpoint in the loader resolves most "wrong string" reports in one pass: is the row present, is the locale correct, is the tenant scope correct, is the cache stale. Clear the translation cache when in doubt — stale cache is the single most common cause of "I changed it and nothing happened".
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.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
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