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

Nova Locale Switcher Laravel Package

eolica/nova-locale-switcher

A Laravel Nova tool that adds a locale switcher to your admin panel, letting you switch the application language from the Nova UI. Useful for multilingual apps where editors need to preview content or work across locales without leaving Nova. Simple to install and configure, it integrates directly i...

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and register the tool

composer require eolica/nova-locale-switcher

In app/Providers/NovaServiceProvider.php, add the tool to the tools() array so Nova renders the switcher in the sidebar:

use Eolica\NovaLocaleSwitcher\LocaleSwitcher;

public function tools()
{
    return [
        new LocaleSwitcher,
    ];
}

2. Publish and inspect the config

php artisan vendor:publish --provider="Eolica\NovaLocaleSwitcher\ToolServiceProvider"

The published config (typically config/nova-locale-switcher.php) holds the list of locales the dropdown exposes — an associative array of code => label. Look at vendor/eolica/nova-locale-switcher/src first: the package is small (a Tool class, a Vue component, and a config), and reading the source is faster than reading docs for a 13-star, 2020-era package.

3. Understand the contract before you wire anything

The description is the important part: "without any concrete localization implementation." The package only persists the chosen locale (session key) and renders the UI to change it. It does not call app()->setLocale(), does not translate your resources, and does not touch URLs. That is your job in step 4, and it is usually 10 lines of middleware.

4. First use case: your own middleware

The package's session value is only useful once something reads it on every request:

namespace App\Http\Middleware;

use Closure;

class SetLocaleFromNova
{
    public function handle($request, Closure $next)
    {
        $allowed = array_keys(config('nova-locale-switcher.locales', []));
        $locale  = $request->session()->get('locale');

        if ($locale && in_array($locale, $allowed, true)) {
            app()->setLocale($locale);
        }

        return $next($request);
    }
}

Register it in the web group after \Illuminate\Session\Middleware\StartSession::class in app/Http/Kernel.php. Reload Nova, switch locale, and confirm with dd(app()->getLocale()) inside a Nova resource's fields() method.

That's the whole loop: package stores it → your middleware applies it → your app's normal translation layer (Laravel lang/, spatie translatable, etc.) does the rest.

Implementation Patterns

Scope the middleware to Nova only

Applying the locale globally will surprise your public site if a customer service agent's session leaks into a shared browser. Either register it just on Nova's middleware stack, or gate it:

if ($request->is('nova', 'nova/*')) {
    // apply locale
}

Nova 4+ exposes Nova::middleware([...]); on Nova 3 you'll either use the web group with a path check or a custom route middleware. Prefer the explicit path check — it survives Nova upgrades.

Validate against the config, never trust the session

$locale = $request->session()->get('locale', config('app.locale'));
if (! array_key_exists($locale, config('nova-locale-switcher.locales'))) {
    $locale = config('app.locale');
}

Sessions are long-lived; someone will remove 'de' from the config and a stale session will hit setLocale('de'), which then throws when a translation file is missing.

Persist per user instead of per session (the upgrade everyone wants)

Session scope means the locale dies at logout and is shared across tabs but not devices. If you need durable preference, add a locale column to users, listen for the package's switch action (or simply mirror the session value on the next request), and resolve in the middleware as $user->locale ?? session('locale'). This is a ~30-minute change and removes the main behavioural complaint about the package.

Pair it with a real translation layer

The switcher is only the control surface. Typical pairings:

  • Laravel's own lang/{locale}/*.php — no extra packages, works for Nova labels via __('...').
  • spatie/laravel-translatable — for model attribute translation; the switcher gives you app()->getLocale().
  • mcamara/laravel-localization — if you also want URL prefixes on the front end. Do not try to make the Nova switcher drive front-end URLs; keep the two concerns separate.

Translate Nova UI strings

Nova ships its own translations. Once app()->setLocale('fr') is set, Nova's built-in labels follow automatically. Your custom label() calls need ->label(__('fields.name')) — those are evaluated at field-definition time, which is after your middleware runs, so they will reflect the switched locale correctly.

Testing

The switcher is testable without the UI by seeding the session:

$this->withSession(['locale' => 'fr'])
     ->get('/nova/resources/users')
     ->assertSee('Utilisateurs');

And test your middleware in isolation for the invalid-locale fallback path — that's where the bugs live.

Where to extend

If the dropdown's ordering or labels don't suit you, the Tool class is the extension point: subclass it, override the locales payload it serialises, and register the subclass in NovaServiceProvider. Forking the Vue component is almost never worth it for this package.

Gotchas and Tips

Released 2020-10-29 — treat it as a Nova 3 artifact. Zero dependents, 13 stars, no release in over four years. Before installing, check composer.json for the Nova version constraint. On Nova 4/5 you will likely need "eolica/nova-locale-switcher": "*" with a fork, or the asset pipeline (Mix → Vite) will not register the component. Budget for a fork; the source is small enough that vendoring it into nova-components/ is a legitimate option and removes the dependency risk entirely.

Assets must actually publish. If the switcher renders as a blank or missing component, it's almost always unpublished assets or a cached service provider. Run:

php artisan vendor:publish
php artisan nova:publish
php artisan optimize:clear

Then hard-refresh — Nova's Mix/Vite manifest is aggressively cached by the browser.

Session driver quirks. With SESSION_DRIVER=array (a common testing/local default) nothing persists between requests and the switcher appears broken. With cookie driver, watch the payload size. With multiple app domains, SESSION_DOMAIN must match the Nova host or the switch silently no-ops on the next request.

Octane and long-running workers. app()->setLocale() is request-scoped but the container is not always reset the way you expect. Confirm your middleware runs on every request, not once per worker bootstrap, and be explicit about resetting locale in Octane::tick/listeners if you set it anywhere global.

Queued jobs, notifications, and mail ignore the session. A job dispatched from Nova runs in a worker with no session — it will fall back to config('app.locale'). Pass the locale explicitly:

dispatch((new SendInvoice($invoice))->locale(app()->getLocale()));

Laravel's ->locale() on mailables and notifications is the correct hook here; don't try to read the session inside the job.

Carbon and number formatting are separate. app()->setLocale('fr') does not automatically localise diffForHumans() or Number::format() in every version. Set Carbon::setLocale() in the same middleware if you rely on it, and check intl extension availability — Number::format needs it.

Never trust the switch endpoint. If the package's route accepts a locale parameter, confirm it validates against the config allow-list server-side. An unvalidated locale reaching setLocale() is a path-traversal-ish smell once you start doing lang_path($locale).

Debugging checklist, in order:

  1. php artisan route:list | grep -i locale — does the switch route exist and use the web (session) group?
  2. Browser devtools → Network → the switch request: status, Set-Cookie, and whether the session cookie actually changed.
  3. session('locale') vs app()->getLocale() — if these disagree, your middleware isn't running or runs too early.
  4. php artisan config:clear — a cached config is the single most common cause of "I added a locale and it didn't appear."

Opportunity note: the package's low score reflects low adoption, not poor design — it does one narrow thing. The gap worth filling is the durable per-user locale, which the package deliberately leaves to you. Implement that with a users.locale column plus a small middleware and you've got the 90% solution without maintaining a fork of the UI.

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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager