modera/backend-languages-bundle
Modera Backend Languages Bundle provides backend language management for PHP applications, making it easy to define, organize, and expose supported locales for admin interfaces. It streamlines multilingual configuration and integration with Modera backend tooling.
Architecture fit — poor as a dependency, moderate as a reference design. The first thing to establish is a framing correction: despite being filed under a Laravel/PHP package index, modera/backend-languages-bundle is a Symfony bundle, not a Laravel package. It is a read-only split repository (indicated by the [READ-ONLY] prefix) pulled out of the Modera CMS monorepo, and it is built around Symfony's bundle/DependencyInjection model: Bundle subclass, service definitions in YAML/XML, event subscribers wired to Symfony's kernel and security events, and Twig-based backend templates. There is no service provider, no facade, no config/*.php publishing — the Laravel integration surface simply does not exist.
Functionally, the package is small in scope. Its stated purpose is to let a backend user change their preferred language for the admin UI — i.e. a per-user locale preference, a UI affordance (menu item / settings control) to change it, and the plumbing to make Symfony's locale-aware services honor it on subsequent requests. That is a genuinely useful, low-complexity feature, but it is roughly "one migration, one middleware, one form, one dropdown" worth of behavior.
Integration feasibility into a Laravel codebase — effectively zero as-is. To consume it you would need to either (a) run a Symfony kernel inside Laravel, which is disproportionate for a locale toggle, or (b) port the logic. Because the package is small and its domain is well-understood, porting is cheap; installing is not possible. Treat this repository strictly as a design specimen for the user-preference-locale pattern, not as a build dependency.
Technical risk.
| Risk | Severity | Notes |
|---|---|---|
| Framework mismatch (Symfony vs Laravel) | Blocking | No Laravel integration path; port required |
| Abandonment | High | Last release 2016-06-10; ~9+ years dormant |
| Version drift | High | Targets Symfony 2.x/3.0-era constraints and PHP 5.5–7.x; will conflict with PHP 8+ and modern Composer resolution |
| No ecosystem validation | Medium | 0 stars, 0 dependents — no community usage, no reported production deployments, no issue history to mine for known bugs |
| Test coverage unknown | Medium | Split repos frequently drop the test suite; assume none |
| Locale input handling | Medium | Any locale value that reaches translation-file resolution or a filesystem path must be allow-listed, not free-form |
| Security surface | Low | Feature is a user-scoped preference; no credential, payment, or PII handling |
Key questions for the requesting team.
users or a settings table, and is it being honored by our translation layer today?Verdict: Do not adopt. Reimplement natively; budget it as a small feature, not an integration project.
Stack fit. Laravel has clean, idiomatic equivalents for every element this bundle provides, and they are strictly better suited to the codebase:
| Bundle concept (Symfony) | Laravel equivalent |
|---|---|
| Backend user locale preference | users.locale column (or a user_settings key/value row) |
| Locale-switching event subscriber | SetLocale HTTP middleware reading auth()->user()->locale |
| Translation catalog / locale resolution | app()->setLocale(), lang/ directory, __() helper |
| Backend UI control | Livewire/Inertia/Blade settings form; PUT /settings/locale |
| Supported-language registry | config/locales.php returning an allow-list |
| Session fallback for guests | session('locale') behind the same middleware |
Migration path. There is nothing to migrate — no data, no schema, no persisted state exists on our side. If the goal is to replace an existing ad-hoc locale hack, the path is: inventory current locale handling → backfill the users.locale column from existing values or default to config('app.locale') → deploy middleware behind a feature flag → switch reads from the old mechanism to the new column → remove the flag and the old code path.
Compatibility. Not applicable in the dependency sense. If the team insists on evaluating the original source for behavioral reference, pin a throwaway checkout in a sandbox only — never add it to composer.json in a production application. PHP 8.x and current Laravel releases are unreachable for this package.
Sequencing (recommended: 3–5 engineering days).
users.locale (nullable, indexed only if queried) and config/locales.php with the allow-list. Add validation rule Rule::in(array_keys(config('locales.supported'))).SetLocale runs after Authenticate; resolves user preference → session → config('app.locale'); validates against the allow-list before calling setLocale(); sets Carbon::setLocale() for date formatting parity.Explicit anti-patterns to avoid. Do not accept a raw locale query parameter and pass it into a translation-loading path without allow-listing. Do not store the preference only in the session — session-scoped locale silently resets on logout and breaks "my preference follows me across devices," which is the whole point of the feature. Do not fork the bundle "just in case."
Maintenance. Near-zero if we build natively — the feature is a column, a middleware, a route, and a form; roughly 200 lines and a handful of tests that we own and understand. If instead the bundle were forked and carried, maintenance cost is unbounded: an unmaintained 2016 Symfony bundle in a modern stack becomes a perpetual compatibility liability with no upstream to rebase against and no issue tracker to consult. Zero dependents and zero stars mean there is no de facto maintainer community to inherit.
Support. Native implementation is supportable by anyone on the team with Laravel familiarity — no specialized Symfony knowledge, no archaeology. A fork would require someone to hold context on a framework generation we otherwise do not use, which is a staffing risk for a feature this small.
Scaling. Trivial at any scale. Locale is a per-request, per-user lookup — one column read on an already-loaded user model, no additional queries if eager-loaded, and it caches naturally with the session. The only scaling-adjacent consideration is translation catalog size: as locale count grows, confirm the loader isn't parsing every catalog on every request (Laravel lazy-loads per-group, so this is a non-issue unless we regress it).
Failure modes and mitigations.
config('app.locale').Ramp-up. Effectively a half-day for any Laravel-proficient engineer: read the bundle's README to confirm the intended UX, then build the native version. The expensive part of this evaluation is not the code — it is the product conversation about which locale list we support and which surfaces honor the preference. That conversation should happen before any implementation work begins, and it is the same conversation regardless of whether we started from this bundle or from a blank file.
How can I help you explore Laravel packages today?