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

Backend Languages Bundle Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

First, a reality check that will save you a day: modera/backend-languages-bundle is a Symfony bundle, not a Laravel package. It has no service provider, no facade, no config/*.php, and it will not autoload usefully in a Laravel app. It is also unmaintained since June 2016 with 0 stars and 0 dependents. Treat it as a reference implementation of a feature, not a dependency you install.

Minimal steps if you are actually evaluating the concept:

  1. Read composer.json first — confirm the Symfony version constraint. It will pin you to Symfony 2.x/3.x era APIs (kernel.event_listener, YAML/XML service definitions), which is the single fastest way to decide "no."
  2. Look at DependencyInjection/Configuration.php and Resources/config/services.* to see how the preferred language is wired in — that's the whole feature surface.
  3. Find the controller action or form that changes the language, and the listener/subscriber that applies it on the next request. That two-step (persist preference → apply per-request) is the entire bundle.
  4. Note where the preference is stored: bundled with the user record, a separate preferences table, or the session. This is the design decision you'll copy.

Your first use case, as a Laravel dev, is not "install this." It's: "Let an admin user pick their own backend UI language, persisted per user, applied on every subsequent request." That is a ~40-line middleware + column in Laravel and you should just build it:

// migration
$table->string('locale', 10)->nullable()->after('email');

// app/Http/Middleware/SetBackendLocale.php
public function handle($request, Closure $next)
{
    if ($user = $request->user()) {
        app()->setLocale($user->locale ?? config('app.locale'));
    }
    return $next($request);
}

// controller
public function updateLocale(Request $request)
{
    $request->validate(['locale' => ['required', Rule::in(config('app.locales'))]]);
    $request->user()->update(['locale' => $request->locale]);
    return back();
}

Implementation Patterns

The bundle embodies four patterns worth stealing, all portable:

1. Preference persistence on the user, not the session. Backend language is a user attribute — it should follow the user across browsers and devices. Store it on the user model (or a user_preferences key/value table if you expect many such settings). Never store it solely in the session, or a user who logs in on a new machine silently gets the wrong language.

2. Request-scoped locale application via a listener/middleware. The bundle hooks the request lifecycle to set the locale before controllers render. In Laravel the equivalent is middleware, registered in bootstrap/app.php (L11+) or Kernel::$middleware. Order matters: it must run after Authenticate so $request->user() is populated.

3. Explicit "change language" endpoint. The switcher is a form POST to a dedicated route, then a redirect back. This avoids GET-based state mutation and makes the CSRF protection free. Replicate exactly:

<form method="POST" action="{{ route('locale.update') }}">
    @csrf
    <select name="locale" onchange="this.form.submit()">
        @foreach (config('app.locales') as $code => $label)
            <option value="{{ $code }}" @selected(app()->getLocale() === $code)>{{ $label }}</option>
        @endforeach
    </select>
</form>

4. Locale allow-listing. The bundle validates against a configured set of enabled languages rather than accepting arbitrary locales — important because setLocale() with an attacker-supplied string can leak into trans() file paths. Always Rule::in(...) against a config array.

Integration tips:

  • Derive the available list from what you actually ship: array_diff(scandir(lang_path()), ['.', '..']), or just hardcode in config/app.php as 'locales' => ['en' => 'English', 'de' => 'Deutsch'].
  • If you use per-user locale and per-request locale (e.g. ?lang=de on public pages), decide precedence explicitly and document it. The bundle's implicit precedence is a common source of confusion.
  • Cache-bust on switch: redirect with back() rather than rendering inline, so any locale-dependent cached fragments (Blade component caches, view caches keyed by locale) rebuild cleanly.
  • For APIs, don't reuse the backend-language setting. Set locale from Accept-Language in a separate middleware and keep admin UI language as an independent concern.

Gotchas and Tips

The bundle will not install into Laravel, and probably not into modern Symfony either. The 2016 release predates Symfony 4's services.yaml conventions, autowiring defaults, and AbstractController. Even on Symfony you are likely to hit signature mismatches in EventSubscriberInterface, deprecated ContainerAware usage, and PSR-4 vs PSR-0 assumptions. Do not add it to composer.json expecting it to work.

Hard coupling to the Modera ecosystem. Functionality almost certainly depends on sibling bundles (ModeraBackendBundle, the translations bundle) for its menu entry, toolbar widget, and routing. Extracting the feature means rewriting both the persistence and the UI hook. There is no meaningful "standalone" mode.

Pitfalls of the pattern itself (these will bite you regardless of framework):

  • Stale session vs. DB. If you cache the locale in the session and the DB, logout/login and multi-tab edits desync. Pick one source of truth; if you cache, invalidate on update.
  • setLocale() in a service constructor. Services are often resolved once and reused; setting locale at construction time freezes the locale for the worker. Do it in middleware, per request — especially under Octane, where a leaked locale will persist across requests.
  • Octane/queue workers retain state. app()->setLocale() is not reset between Octane requests unless you reset it in a terminating callback or at the top of the middleware unconditionally.
  • Validation messages and route names stay English. setLocale() changes trans() lookups, not your route names, validation rule messages you hardcoded, or Carbon's format defaults — set Carbon::setLocale() separately if you display dates.
  • lang/ directory must exist for the locale. Laravel's translator silently falls back; you won't get an error, just English strings. Add a test asserting a known key differs per locale.
  • Timezone ≠ language ≠ number format. The bundle only handles language. If you localize dates/numbers, you need ext-intl and a separate locale-formatting concern; don't overload one column for both.

Extension points worth knowing: the persistence layer is the only part you'd genuinely want to swap — if you adopt the concept on Symfony, override the user-preference repository rather than the listener. In Laravel terms, put the read/write behind a small LocalePreference service so tests and future multi-tenant scoping don't require touching middleware.

Debugging the concept: if the language "doesn't stick," check in this order — (1) middleware registered and ordered after auth; (2) column actually saved (dump $user->fresh()->locale); (3) setLocale() called before the response is rendered; (4) the translation file for that locale exists; (5) config('app.fallback_locale') isn't silently masking a missing key. Nine times out of ten it's step 1 or 2.

Verdict: the bundle is a 2016 Symfony artifact with no Laravel applicability and no maintenance. Its pattern — user-scoped locale persisted on the user, applied by middleware, changed via a POST endpoint — is correct and takes under an hour to reimplement natively. Do that instead.

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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console