backpack/language-switcher
A Backpack for Laravel add-on that adds a language switcher dropdown to your admin panel's top bar, letting admins switch between configured locales for the interface or translated content. Simple configuration ties it to Laravel's localization system and your available languages.
backpack/language-switcher is a deliberately tiny, MIT-licensed Backpack addon: it renders a dropdown in the admin top bar, and when you pick a locale it stores it in the session and applies it with App::setLocale() on subsequent requests. That's it. There is no content translation, no URL strategy, no locale negotiation, no hreflang — those remain your job. Treat it as "a dropdown plus a two-line middleware," because that's roughly what you're installing.
What changed in 2.1.0: the only functional change is official Backpack v7 support (PR #21), plus a README refresh. No API changes, no deprecations, no new features — so existing Backpack 6 installs on 2.0.x have no reason to rush the upgrade, and the mental model above is unchanged. The practical consequence is a version-alignment rule you should encode in composer.json before anything else:
backpack/language-switcher 2.0.x works; 2.1.x should also work but is untested-by-you.^2.1, because 2.0.x predates v7 support entirely.Because the package is small (a few dozen stars, negligible dependents), the fastest way to get productive is still to read the source before you configure it: the service provider, the middleware, the single Blade view, and the route it registers. Ten minutes of reading removes all of the guesswork about config keys and view names — and this is doubly true on v7, where the surrounding Backpack config surface moved and the README may lag the code.
Minimal path:
composer require backpack/language-switcher:^2.1 on a Backpack 7 app (or let Composer resolve on Backpack 6). Confirm your Laravel constraint is satisfied — 2.1.0's composer.json is the authority, not the release notes.php artisan vendor:publish --provider="Backpack\LanguageSwitcher\LanguageSwitcherServiceProvider".middleware array in config/backpack/base.php or the backpack middleware group; on v7 verify which config file now owns that group before editing, and confirm with php artisan route:list -v | grep -i admin that your middleware actually appears on the dashboard route.resources/views/vendor/backpack/.... On v7, publish the views and look at the rendered topbar before assuming the v6 override path is still correct — theme changes are exactly where "supported" and "identical" diverge.config('app.locales') or the package's own config key. Keep this in a single helper/config so the dropdown, fallback_locale, and your lang/ directories never drift apart.Where to look first: the published config file (locale list + flag/emoji mapping), the route (php artisan route:list | grep -i lang), and the middleware. First real use case is the obvious one — an editor on a bilingual admin wants to see the panel in Spanish; you add es, click the flag, and Backpack's own UI strings come from lang/es on the next request.
Admin-only switching (the default, safest). Mount the middleware only on Backpack routes. The public site stays on app.fallback_locale. This is the right choice for 80% of installs — the switcher is a convenience for admins, not a public feature.
Site-wide locale via one shared session key. If you do want the front end to follow the admin choice, don't try to stretch the package's route; move the middleware into the web group and make sure it reads the same session key. A 15-line middleware of your own is usually cleaner than reusing theirs:
public function handle($request, Closure $next)
{
$locale = $request->session()->get('locale', config('app.fallback_locale'));
if (in_array($locale, config('app.locales'))) {
App::setLocale($locale);
}
return $next($request);
}
Pin the addon alongside Backpack. Since 2.1.0 exists solely to track Backpack v7, treat the two as a unit: bump backpack/crud and backpack/language-switcher in the same PR, and re-run your admin smoke test (switch locale, reload, assert app()->getLocale()) after every Backpack minor. A v7 upgrade with the addon left at 2.0.x is the most likely near-term breakage.
Persist a per-user preference. Add a locale column to users, set the session from it on login, and write it back in a listener on Illuminate\Auth\Events\Login / when the switch route is hit. The session-only model loses the choice when the browser closes or the session expires.
Keep content translation separate. The switcher localizes strings. For database content use spatie/laravel-translatable (or per-locale rows) and make sure both systems key off the same locale code and the same session key — otherwise you get a French UI showing German content.
Combine with URL strategies when SEO matters. The package's switch route is a POST/redirect-to-previous affair; it produces a session state change, not a crawlable URL. If you need shareable, indexable per-language URLs, build your own /{locale}/... route prefix and let the switcher only write the session as a fallback.
Restyle via view override. The dropdown is one Blade file. Publish the views, swap flags for emoji or a select component if you want, and don't fight the package's markup. On v7 the surrounding topbar/theme markup differs, so re-publish and diff rather than copying v6 overrides forward.
pt_BR vs pt-BR vs pt-br is the single most common failure: App::setLocale('pt-BR') silently falls back when no matching lang/pt-BR directory exists, so the dropdown "works" but nothing changes. Normalize once, in one config key.composer show backpack/language-switcher) before debugging your own config.config/backpack/base.php and config/backpack/ui.php may not map 1:1 onto v7. Read the config files that actually exist in your app (php artisan vendor:publish again, or inspect vendor/backpack/.../config) instead of applying v6 instructions from blog posts and older READMEs.fallback_locale. Explicitly set the locale wherever you render mailables or notifications.App::getLocale() at boot — menu builders registered in a service provider, View::share()'d labels, cached nav configs — runs before the switcher middleware. Build locale-dependent menus inside a middleware, or expect the first request after a switch to show stale labels.config:cache and route:cache. Adding a locale to config without clearing the cache leaves the dropdown unchanged in production and is maddening to diagnose locally against a cached deployment.backpack/* issue, not a switcher bug.url()->previous() after a switch can bounce off-site if the referer is attacker-controlled. Compare against url('/') or use a named route.$this->withSession(['locale' => 'es'])->get('/admin/dashboard') and $this->assertSame('es', app()->getLocale()). After the v7 bump, add one test that asserts the switcher dropdown markup is present in the topbar — that catches the "supported but silently not mounted" class of regression.How can I help you explore Laravel packages today?