DEV3 Technologies home

Filament Arabic and English locale switch with RTL layout

Let Filament 4 users switch between English and Arabic from the user menu, remembered in the session and profile, with the layout flipping to RTL.

FilamentInternationalisationfilamenti18nrtlarabic

Code

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class SetLocale
{
    public const SUPPORTED = ['en' => 'English', 'ar' => 'العربية'];

    public function handle(Request $request, Closure $next): Response
    {
        $locale = $request->session()->get('locale')
            ?? $request->user()?->locale
            ?? config('app.locale');

        // Whitelist: never pass raw input to setLocale().
        if (array_key_exists($locale, self::SUPPORTED)) {
            // Filament reads the direction from this locale's translation files,
            // so "ar" flips the whole panel to right-to-left on its own.
            app()->setLocale($locale);
        }

        return $next($request);
    }
}

// routes/web.php  (the switch endpoint used by the user menu items)
//
//   Route::get('/locale/{locale}', function (string $locale) {
//       abort_unless(array_key_exists($locale, SetLocale::SUPPORTED), 404);
//       session(['locale' => $locale]);
//       auth()->user()?->update(['locale' => $locale]);   // remember across devices
//
//       return redirect()->back();
//   })->name('locale.switch');

Needs: filament/filament@^4.0

How it works

What it solves

Panels for businesses in the Gulf, North Africa or any bilingual market need Arabic next to English. Translating strings is only half the job; the whole layout must also run right-to-left. Filament already ships Arabic translations, so the missing piece is a clean way for users to choose their language.

How it works

SetLocale is a middleware registered on the panel. On each request it takes the locale from the session first, then from the user's saved preference, then from the app default. The value is checked against the SUPPORTED whitelist before app()->setLocale() is called, so a tampered session or query string cannot select an arbitrary locale.

No custom CSS is needed for direction. Filament decides dir="rtl" or ltr from the active locale's translation files, so setting ar flips the sidebar, tables, icons and forms automatically.

The provider builds user menu entries from the same list, excluding the language that is already active, so the menu always offers the other choice. Each entry links to a small route (shown in the comment) that validates the code, stores it in the session and saves it on the user.

Adapting it

Add more languages by extending SUPPORTED; for each, check that Filament includes a translation. Put your own strings in lang/ar/*.php or JSON files. Use Number::useLocale() if you want Arabic-Indic digits.

Pitfalls

  • The user menu is built when the panel boots; if you compute the list before the middleware runs it may show the wrong "other" language, hence the except call at render time.
  • Add a locale string column to users before saving to it.
  • Mixed-direction content (English product names inside Arabic text) may need dir="auto" on custom Blade views.

How to use it

  1. Add a nullable locale column to users and migrate.
  2. Put SetLocale.php in app/Http/Middleware/ and add the locale.switch route from the comment.
  3. Register the middleware and user menu items in your panel provider.
  4. Add Arabic strings for your own labels in lang/ar/.
  5. Log in, open the user menu and choose the other language: the page reloads mirrored.

Licence: DEV3 Snippet Licence. Free to use, change and ship in personal and commercial projects. Attribution is appreciated, not required. Please do not redistribute snippets as a collection.

Related snippets