PHPackages                             bezhansalleh/filament-language-switch - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Localization &amp; i18n](/categories/localization)
4. /
5. bezhansalleh/filament-language-switch

ActiveLibrary[Localization &amp; i18n](/categories/localization)

bezhansalleh/filament-language-switch
=====================================

Zero config Language Switch(Changer/Localizer) plugin for filamentphp admin

5.0.0(1w ago)3581.3M↓24.3%7820MITPHPPHP ^8.2CI passing

Since Jun 24Pushed 1w ago3 watchersCompare

[ Source](https://github.com/bezhanSalleh/filament-language-switch)[ Packagist](https://packagist.org/packages/bezhansalleh/filament-language-switch)[ Docs](https://github.com/bezhansalleh/filament-language-switch)[ GitHub Sponsors](https://github.com/bezhanSalleh)[ RSS](/packages/bezhansalleh-filament-language-switch/feed)WikiDiscussions main Synced 3d ago

READMEChangelog (10)Dependencies (58)Versions (37)Used By (20)

[![Filament Language Switch](https://repository-images.githubusercontent.com/506847060/81f73ae9-6cef-4f89-a4cf-47de0412e0b5 "Filament Language Switch")](https://github.com/bezhansalleh/filament-language-switch) [ ![FILAMENT 5.x](https://camo.githubusercontent.com/3ec960aa304b803da9f1192f01e82da976a3d220ea85931fec930003e0532c77/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f46494c414d454e542d352e782d4542423330343f7374796c653d666f722d7468652d6261646765) ](https://filamentphp.com/docs/5.x/introduction/installation) [ ![FILAMENT 5.x](https://camo.githubusercontent.com/13befbe0cffd101b2484ce1cdd985dabcddcbf1176d17a675674d53af99cfdbc/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f46494c414d454e542d342e782d4542423330343f7374796c653d666f722d7468652d6261646765) ](https://filamentphp.com/docs/4.x/introduction/installation) [ ![Packagist](https://camo.githubusercontent.com/7db51092716a9ac447ccdf0e081fcc105039a1656b92dcdd9be82bec90489e3c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f62657a68616e73616c6c65682f66696c616d656e742d6c616e67756167652d7377697463682e7376673f7374796c653d666f722d7468652d6261646765266c6f676f3d7061636b6167697374) ](https://packagist.org/packages/bezhansalleh/filament-language-switch) [ ![Downloads](https://camo.githubusercontent.com/4d3bcbbe2574e5a24fe343d6656dd63b9fea70eb5fff89b66e05544db34459b6/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f62657a68616e73616c6c65682f66696c616d656e742d6c616e67756167652d7377697463682e7376673f7374796c653d666f722d7468652d6261646765) ](https://packagist.org/packages/bezhansalleh/filament-language-switch)

Language Switch
===============

[](#language-switch)

Zero-config language switching for Filament Panels. Drop it in, provide your locales, and you're done. The plugin auto-detects your panel layout and renders in the right place with the right design — topbar icon button, sidebar nav item, user menu item — without any manual wiring.

Table of Contents
-----------------

[](#table-of-contents)

- [Installation](#installation)
- [Quick Start](#quick-start)
- [Display Modes](#display-modes)
- [Trigger](#trigger)
- [Flags](#flags)
- [Labels](#labels)
- [Appearance](#appearance)
- [Placement](#placement)
- [Outside Panels](#outside-panels)
- [Inline Embedding](#inline-embedding-in-custom-pages)
- [Panel Exclusion](#panel-exclusion)
- [User Preferred Locale](#user-preferred-locale)
- [Customization](#customization)
- [Event](#event)
- [Control Panel](#control-panel)
- [Full Example](#full-example)
- [Version Support](#version-support)

Installation
------------

[](#installation)

```
composer require bezhansalleh/filament-language-switch
```

Important

The plugin follows Filament's theming rules. So, to use the plugin create a custom theme if you haven't already, and add the following line to your `theme.css` file:

```
@source '../../../../vendor/bezhansalleh/filament-language-switch/**';
```

Now build your theme using:

```
npm run build
```

Build your theme:

```
npm run build
```

Quick Start
-----------

[](#quick-start)

Add this to any service provider's `boot()` method:

```
use BezhanSalleh\LanguageSwitch\LanguageSwitch;

public function boot(): void
{
    LanguageSwitch::configureUsing(function (LanguageSwitch $switch) {
        $switch->locales(['en', 'fr', 'ar']);
    });
}
```

That's it. The switch appears in your topbar automatically. When the panel has no topbar, it moves to the sidebar.

Display Modes
-------------

[](#display-modes)

### Dropdown (default)

[](#dropdown-default)

The trigger opens a dropdown with your locales. The list is scrollable by default (`max-height: 18rem`), so it stays usable with many locales:

```
$switch->locales(['en', 'fr', 'ar']);
```

### Modal

[](#modal)

Opens a modal dialog instead. Better for many languages:

```
use BezhanSalleh\LanguageSwitch\Enums\DisplayMode;

$switch
    ->locales(['en', 'fr', 'ar', 'de', 'es', 'pt', 'ja', 'ko', 'zh'])
    ->displayMode(DisplayMode::Modal)
    ->modalWidth('lg');
```

### Modal with Grid

[](#modal-with-grid)

Arrange locales in columns:

```
$switch
    ->displayMode(DisplayMode::Modal)
    ->columns(2)
    ->modalWidth('lg');
```

### Slide-over

[](#slide-over)

```
$switch
    ->displayMode(DisplayMode::Modal)
    ->modalSlideOver();
```

### Modal Heading &amp; Icon

[](#modal-heading--icon)

```
$switch
    ->displayMode(DisplayMode::Modal)
    ->modalHeading('Choose Language')
    ->modalIcon('heroicon-o-language')
    ->modalIconColor('primary');
```

Trigger
-------

[](#trigger)

The trigger is configured through a single unified `trigger()` method that takes a style and/or an icon. Both are optional — pass whichever you want to override.

### Trigger Style

[](#trigger-style)

Use the `TriggerStyle` enum to control what the trigger shows. The default adapts to the render context automatically (e.g. flag if you've set flags, otherwise icon in topbar; icon+label in sidebar/user menu), so you only need to call this when you want to override it:

```
use BezhanSalleh\LanguageSwitch\Enums\TriggerStyle;

// Visual only
$switch->trigger(style: TriggerStyle::Icon);    // language icon
$switch->trigger(style: TriggerStyle::Flag);    // flag image (requires ->flags())
$switch->trigger(style: TriggerStyle::Avatar);  // locale abbreviation (EN, FR, AR)

// Visual with label
$switch->trigger(style: TriggerStyle::IconLabel);    // icon + locale name
$switch->trigger(style: TriggerStyle::FlagLabel);    // flag + locale name
$switch->trigger(style: TriggerStyle::AvatarLabel);  // abbreviation + locale name

// Label only — no visual
$switch->trigger(style: TriggerStyle::Label);
```

### Trigger Icon

[](#trigger-icon)

The trigger uses the language icon by default. Pass any icon from your installed [Blade Icons](https://blade-ui-kit.com/blade-icons) packages, or a `Heroicon` enum case:

```
use Filament\Support\Icons\Heroicon;

// Just change the icon (style stays as default)
$switch->trigger(icon: Heroicon::GlobeAlt);

// Any Blade Icons string works too
$switch->trigger(icon: 'heroicon-o-globe-alt');
$switch->trigger(icon: 'phosphor-translate');

// Or change both in one call
$switch->trigger(
    style: TriggerStyle::IconLabel,
    icon: Heroicon::GlobeAlt,
);
```

You can also override the icon globally via Filament's icon alias system:

```
use Filament\Support\Facades\FilamentIcon;

FilamentIcon::register([
    'language-switch::trigger' => 'heroicon-o-globe-alt',
]);
```

Flags
-----

[](#flags)

Associate each locale with a flag image:

```
$switch->flags([
    'en' => asset('flags/us.svg'),
    'fr' => asset('flags/fr.svg'),
    'ar' => asset('flags/sa.svg'),
]);
```

### Item Style

[](#item-style)

Control what each locale item shows in the dropdown or modal. The default auto-detects: `FlagWithLabel` when flags are configured, `AvatarWithLabel` otherwise.

```
use BezhanSalleh\LanguageSwitch\Enums\ItemStyle;

$switch->itemStyle(ItemStyle::FlagOnly);        // flag images only, tooltips on hover
$switch->itemStyle(ItemStyle::AvatarOnly);      // abbreviations only (EN, FR), tooltips on hover
$switch->itemStyle(ItemStyle::LabelOnly);       // text labels only, no visual
$switch->itemStyle(ItemStyle::FlagWithLabel);    // flag + locale name (default with flags)
$switch->itemStyle(ItemStyle::AvatarWithLabel);  // abbreviation + locale name (default without flags)
```

Compact styles (`FlagOnly`, `AvatarOnly`) use the same tight cell layout with tooltips. In modal mode, `FlagOnly` renders as a showcase grid with radio-card selection; `AvatarOnly` falls back to label-only cards since the abbreviation is too small for a showcase.

```
$switch
    ->flags([...])
    ->itemStyle(ItemStyle::FlagOnly)
    ->displayMode(DisplayMode::Modal)
    ->columns(3)
    ->modalWidth('lg');
```

Labels
------

[](#labels)

### Custom Labels

[](#custom-labels)

Override the auto-generated locale names:

```
$switch->labels([
    'pt_BR' => 'Brasileiro',
    'pt_PT' => 'Europeu',
]);
```

### Native Labels

[](#native-labels)

Show each locale name in its own language (e.g., "Français" instead of "French"):

```
$switch->nativeLabel();
```

### Display Locale

[](#display-locale)

Change the language used for auto-generated labels:

```
// Labels generated in French (e.g., "Anglais" for English)
$switch->displayLocale('fr');
```

Appearance
----------

[](#appearance)

### Circular

[](#circular)

Make flags and avatars fully rounded:

```
$switch->circular();
```

### Flag &amp; Avatar Sizing (Modal)

[](#flag--avatar-sizing-modal)

Control sizes in modal radio cards:

```
$switch
    ->displayMode(DisplayMode::Modal)
    ->flagHeight('h-20')     // Default: 'h-16'
    ->avatarHeight('size-10'); // Default: 'size-8'
```

Placement
---------

[](#placement)

### Render Hook

[](#render-hook)

Place the switch at any [Filament render hook](https://filamentphp.com/docs/5.x/support/render-hooks):

```
use Filament\View\PanelsRenderHook;

$switch->renderHook(PanelsRenderHook::SIDEBAR_NAV_END);
```

The trigger automatically adapts its design to match the surrounding UI:

Hook LocationTrigger Design`GLOBAL_SEARCH_*`, `TOPBAR_*`Icon button (matches topbar icons)`SIDEBAR_LOGO_*`Icon button (compact, hides when sidebar collapses)`SIDEBAR_START`, `SIDEBAR_NAV_*`, `SIDEBAR_FOOTER`Sidebar nav item (matches Dashboard, Welcome, etc.)`USER_MENU_BEFORE/AFTER` (topbar on)Icon button inside user menu area`USER_MENU_BEFORE/AFTER` (topbar off)Sidebar footer button (matches notifications)`USER_MENU_PROFILE_*`Dropdown list item (matches user menu items)`SIMPLE_LAYOUT_START/END`Floating button (see [Outside Panels](#outside-panels))> **Any other hook works too.** If you pass a hook the plugin doesn't explicitly classify, it still registers and renders a default icon button — but the visual fit is on you. Use the stable CSS hooks (`fi-ls`, `fi-ls-trigger`, etc.) from your own stylesheet, or publish the views for structural changes.

### Smart Defaults

[](#smart-defaults)

When you don't set a render hook, the plugin picks the best one based on your panel:

Panel ConfigDefault HookWhere it appearsTopbar enabled`GLOBAL_SEARCH_AFTER`Topbar, after search barTopbar disabled`SIDEBAR_LOGO_AFTER`Sidebar header, next to the logoTo render inside the user menu as a menu item, set the render hook explicitly:

```
$switch->renderHook(PanelsRenderHook::USER_MENU_PROFILE_AFTER);
```

### Dropdown Placement

[](#dropdown-placement)

Control which direction the dropdown opens:

```
$switch->dropdownPlacement('top-end');
```

### Max Height

[](#max-height)

The dropdown is scrollable by default (`18rem`). Override it for a different limit, or pass `'max-content'` to disable scrolling:

```
$switch->maxHeight('30rem');
$switch->maxHeight('max-content'); // no scroll, grows to fit content
```

Outside Panels
--------------

[](#outside-panels)

Filament's unauthenticated pages (login, register, password reset, email verification) render in a **simple layout** — no sidebar, no topbar, just the centered form card. You can show the language switcher on these pages so visitors can translate the UI before they sign in.

```
use BezhanSalleh\LanguageSwitch\Enums\Placement;

$switch
    ->visible(outsidePanels: true)
    ->outsidePanelPlacement(Placement::TopEnd);
```

`visible()` takes two independent toggles — `insidePanels` (default `true`) and `outsidePanels` (default `false`) — so you can enable either context on its own, both, or neither. For example, to render **only** on the simple-layout pages and hide the switcher once the user is inside the panel:

```
$switch->visible(insidePanels: false, outsidePanels: true);
```

By default the switcher renders as a content-sized pill anchored to the chosen `Placement`. All six placements are RTL-aware — `start`/`end` auto-flip for right-to-left locales:

PlacementPosition`Placement::TopStart`Top-left (LTR) / top-right (RTL)`Placement::TopCenter`Top-center`Placement::TopEnd`Top-right (LTR) / top-left (RTL)`Placement::BottomStart`Bottom-left (LTR) / bottom-right (RTL)`Placement::BottomCenter`Bottom-center`Placement::BottomEnd`Bottom-right (LTR) / bottom-left (RTL)### Placement mode

[](#placement-mode)

`outsidePanelPlacement()` accepts an optional second argument that controls **how** the switcher is attached to the page. Three modes, each with predictable CSS semantics — the names are chosen so they match what you'd read in the published view:

```
use BezhanSalleh\LanguageSwitch\Enums\Placement;
use BezhanSalleh\LanguageSwitch\Enums\PlacementMode;

$switch->outsidePanelPlacement(Placement::TopCenter, PlacementMode::Pinned);
```

ModeCSSBehavior**`PlacementMode::Static`** *(default)*`position: static`Renders **in the document flow** inside `.fi-simple-layout`, above the form card for `Top*` placements or below it for `Bottom*`. Scrolls with the page. Content-sized pill aligned horizontally via `w-fit` + `mx-auto` / `ms-auto`.**`PlacementMode::Pinned`**`position: fixed`**Stays visible while scrolling** — pinned to the viewport at the chosen corner/edge as a content-sized pill. Use this when the switcher should always be reachable (e.g. long registration forms).**`PlacementMode::Relative`**`position: relative`Same visual as `Static` out of the box, but the element is **positioned**, so you can offset it via custom CSS (`top: 1rem`, `inset-inline-start: 2rem`, etc.) in your theme file without publishing the view.> **Naming note:** we use `Pinned` instead of `Fixed` on purpose. In CSS, `position: fixed` means "always visible, stays on screen during scroll" — which in the dev brain is usually called "sticky". To avoid that collision every time someone opens the blade file, the mode is named after the *intent* (`Pinned` = pinned to viewport), while `Static` and `Relative` use the literal CSS names because those map 1:1 to their CSS behavior.

### Which routes it shows on

[](#which-routes-it-shows-on)

By default the switcher appears on Filament's standard auth routes. Override the list if your panel uses different route names:

```
$switch->outsidePanelRoutes([
    'auth.login',
    'auth.register',
    'auth.password-reset.request',
    'auth.password-reset.reset',
]);
```

The default list is `['auth.login', 'auth.profile', 'auth.register']`. The profile route is automatically excluded from the match unless the current panel uses a simple profile page (`->profile(isSimple: true)`), since a full-layout profile page already has a topbar/sidebar where the switcher lives.

### Render hook anchor (auto-derived)

[](#render-hook-anchor-auto-derived)

The anchor hook is derived from **both** the placement and the mode — you don't pick it manually:

ModeTop placementBottom placementWhy`Pinned``BODY_START``BODY_END`Pinned uses `position: fixed`, so the element needs a parent that gives reliable viewport-relative positioning. Direct body child is ideal — no flex-parent or transform containing block in the ancestor chain.`Static` / `Relative``SIMPLE_LAYOUT_START``SIMPLE_LAYOUT_END`In-flow elements are anchored **inside `.fi-simple-layout`** (which is `min-h-dvh flex-col`). This way the element shares the viewport with the centered form card instead of extending the body height and adding an unwanted page scroll.You can still override explicitly — pass any hook name to `outsidePanelsRenderHook()`:

```
use Filament\View\PanelsRenderHook;

// Dock as a profile item inside the user menu dropdown
$switch->outsidePanelsRenderHook(PanelsRenderHook::USER_MENU_PROFILE_AFTER);

// Or force body-level anchoring for an in-flow mode
$switch
    ->outsidePanelPlacement(Placement::BottomCenter, PlacementMode::Static)
    ->outsidePanelsRenderHook(PanelsRenderHook::BODY_END);
```

### Auto-docking into the user menu

[](#auto-docking-into-the-user-menu)

When **all** of these are true, `TopEnd` **automatically docks into the user menu** via `USER_MENU_BEFORE` instead of anchoring as a pinned overlay:

- `PlacementMode::Pinned`
- The current route is in the outside-panel list (e.g. a simple profile page)
- The user is authenticated
- The panel has a user menu

This avoids the collision between the pinned pill and Filament's own `.fi-simple-layout-header` (also anchored at `top-0 end-0`). `Static` and `Relative` modes don't trigger auto-docking because they're in flow and can't collide with the header.

To force a pinned overlay regardless, pin a different hook explicitly:

```
use Filament\View\PanelsRenderHook;

// Force a body-start overlay even when a user menu is present
$switch
    ->outsidePanelPlacement(Placement::TopEnd, PlacementMode::Pinned)
    ->outsidePanelsRenderHook(PanelsRenderHook::BODY_START);
```

Inline embedding in custom pages
--------------------------------

[](#inline-embedding-in-custom-pages)

The render-hook system handles the common cases automatically — topbar, sidebar, user menu, simple layout. If you need to drop the switcher into a custom page section or arbitrary location in your own views, use the Blade component:

```

```

It mounts the same `` directly at the tag's position — no render hook required. The trigger uses whatever styling your current configuration resolves to (for example, if your effective render hook is in the topbar, you'll get a topbar-style icon button inline in your view). If you need a different visual in your custom location, override with `->renderHook(...)` in a dedicated configureUsing callback or publish the views for full control.

Passing an explicit key is recommended when embedding in a parent component that re-renders, or when placing multiple instances on the same page:

```

...

```

If omitted, a unique key is auto-generated via `uniqid()` on every render, which is fine for static pages but can cause the component to re-mount unnecessarily on dynamic parent updates.

Panel Exclusion
---------------

[](#panel-exclusion)

Hide the switch from specific panels:

```
$switch->excludes(['admin']);
```

User Preferred Locale
---------------------

[](#user-preferred-locale)

Resolve the user's preferred locale from their profile or any source:

```
$switch->userPreferredLocale(fn () => auth()->user()?->locale);
```

The locale resolution order is:

1. Session
2. Query parameter (`?locale=`)
3. User preferred locale (this setting)
4. Browser `Accept-Language` header
5. Cookie
6. `app.locale` config

Customization
-------------

[](#customization)

For deep customization, publish the plugin's views and edit them in your app:

```
php artisan vendor:publish --tag="language-switch-views"
```

Published views live in `resources/views/vendor/language-switch/` and override the package versions automatically. For small tweaks, prefer targeting the plugin's stable CSS hooks (`fi-ls`, `fi-ls-trigger`, `fi-ls-item`, etc.) from your own stylesheet rather than publishing views.

Event
-----

[](#event)

A `LocaleChanged` event fires whenever the locale switches:

```
use BezhanSalleh\LanguageSwitch\Events\LocaleChanged;
use Illuminate\Support\Facades\Event;

Event::listen(function (LocaleChanged $event) {
    auth()->user()?->update(['locale' => $event->locale]);
});
```

Control Panel
-------------

[](#control-panel)

A floating developer configurator that lets you hot-swap every configuration option live in the browser — handy for previewing trigger styles, testing render-hook placements, and checking the outside-panel modes without editing your service provider.

### Enabling it

[](#enabling-it)

Opt in explicitly via `controlPanel()`:

```
LanguageSwitch::configureUsing(function (LanguageSwitch $switch) {
    $switch
        ->locales(['en', 'fr', 'ar'])
        ->controlPanel();
});
```

Disabled by default. When you call `controlPanel()` (or `controlPanel(true)`) **and** the app is running with `APP_ENV=local` + `APP_DEBUG=true`, a small language-icon button appears at the bottom-end of every panel page. The local + debug requirement is a safety guardrail — even if you leave `controlPanel(true)` committed, it won't render in production.

To disable it explicitly (e.g. in a shared configuration):

```
$switch->controlPanel(false);
```

### Live vs manual mode

[](#live-vs-manual-mode)

`controlPanel()` takes two arguments: `controlPanel(bool $enabled = true, bool $live = true)`. By default every change (toggle, select) is applied immediately via a Livewire round-trip. If you'd rather stage multiple changes and commit them together, pass `live: false`:

```
$switch->controlPanel(live: false);          // enabled + staged (Apply button)
$switch->controlPanel(true, live: false);    // same thing, explicit
```

In manual mode, changes are accumulated in the session and the panel footer shows an **Apply** button (only enabled when you have pending changes). Click it to commit everything in one reload. **Reset** still works the same way — it wipes every accumulated override and restores your configured defaults.

### Sections

[](#sections)

The panel is organized as an accordion — click a section to expand it; opening one collapses the others, so it stays compact on both mobile and desktop:

- **Trigger** — topbar on/off, trigger style, trigger icon, render hook
- **Display** — dropdown / modal mode, modal width, columns, slide-over
- **Appearance** — circular, native labels, use flags, item style
- **Outside Panels** — enable toggle, placement, placement mode (`Pinned` / `Static` / `Relative`), and an explicit render hook override (defaults to auto, with user-menu docking targets as alternatives)

Full Example
------------

[](#full-example)

```
use BezhanSalleh\LanguageSwitch\Enums\DisplayMode;
use BezhanSalleh\LanguageSwitch\Enums\TriggerStyle;
use BezhanSalleh\LanguageSwitch\LanguageSwitch;
use Filament\Support\Icons\Heroicon;

LanguageSwitch::configureUsing(function (LanguageSwitch $switch) {
    $switch
        ->locales(['en', 'fr', 'ar', 'de', 'es'])
        ->flags([
            'en' => asset('flags/us.svg'),
            'fr' => asset('flags/fr.svg'),
            'ar' => asset('flags/sa.svg'),
            'de' => asset('flags/de.svg'),
            'es' => asset('flags/es.svg'),
        ])
        ->displayMode(DisplayMode::Modal)
        ->columns(2)
        ->modalWidth('lg')
        ->circular()
        ->nativeLabel()
        ->trigger(
            style: TriggerStyle::FlagLabel,
            icon: Heroicon::GlobeAlt,
        )
        ->excludes(['admin'])
        ->userPreferredLocale(fn () => auth()->user()?->locale);
});
```

Version Support
---------------

[](#version-support)

Only the latest major plugin line receives active development. Older lines stay installable for legacy projects but get no new features; they only receive security fixes while their Filament target is still supported upstream.

PluginFilamentStatus**[`5.x`](https://github.com/bezhansalleh/filament-language-switch/tree/main)**[v5](https://filamentphp.com/docs/5.x/introduction/installation)**Active** — new features + bug fixes (lives on `main`)[`4.x`](https://github.com/bezhansalleh/filament-language-switch/tree/4.x)[v4](https://filamentphp.com/docs/4.x/introduction/installation) &amp; [v5](https://filamentphp.com/docs/5.x/introduction/installation)Security fixes only[`3.x`](https://github.com/bezhansalleh/filament-language-switch/tree/3.x)[v3](https://filamentphp.com/docs/3.x/panels/installation)End of life — follows Filament v3 EOL[`1.x`](https://github.com/bezhansalleh/filament-language-switch/tree/1.x)[v2](https://filamentphp.com/docs/2.x/admin/installation)End of lifeSee [CHANGELOG](CHANGELOG.md) for migration notes between majors.

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Credits
-------

[](#credits)

- [Bezhan Salleh](https://github.com/bezhanSalleh)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

70

—

ExcellentBetter than 100% of packages

Maintenance98

Actively maintained with recent releases

Popularity61

Solid adoption and visibility

Community39

Small or concentrated contributor base

Maturity72

Established project with proven stability

 Bus Factor1

Top contributor holds 76.6% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~44 days

Recently: every ~21 days

Total

34

Last Release

7d ago

Major Versions

v0.0.1 → v1.0.02022-07-22

1.x-dev → 2.0.02023-07-31

2.x-dev → 3.0.02023-11-20

3.x-dev → 4.0.0-beta22025-06-11

4.x-dev → 5.0.0-beta.12026-04-06

PHP version history (5 changes)v0.0.0PHP ^8.0|^8.1

1.0.7PHP ^8.0|^8.1|^8.2

2.0.0PHP ^8.1|^8.2

3.0.6PHP ^8.1|^8.2|^8.3

4.0.0-beta2PHP ^8.2

### Community

Maintainers

![](https://www.gravatar.com/avatar/19da25a8d07a5911120a520529905f744c8d56bf5e15901916f245b1d329bf03?d=identicon)[bezhanSalleh](/maintainers/bezhanSalleh)

---

Top Contributors

[![bezhanSalleh](https://avatars.githubusercontent.com/u/10007504?v=4)](https://github.com/bezhanSalleh "bezhanSalleh (223 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (24 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (18 commits)")[![ThorgerSuenert](https://avatars.githubusercontent.com/u/42864157?v=4)](https://github.com/ThorgerSuenert "ThorgerSuenert (4 commits)")[![medeiroz](https://avatars.githubusercontent.com/u/11270546?v=4)](https://github.com/medeiroz "medeiroz (3 commits)")[![erjanmx](https://avatars.githubusercontent.com/u/4899432?v=4)](https://github.com/erjanmx "erjanmx (2 commits)")[![gp-lnuff](https://avatars.githubusercontent.com/u/169065476?v=4)](https://github.com/gp-lnuff "gp-lnuff (2 commits)")[![jyrkidn](https://avatars.githubusercontent.com/u/2447042?v=4)](https://github.com/jyrkidn "jyrkidn (1 commits)")[![lukas-frey](https://avatars.githubusercontent.com/u/10926334?v=4)](https://github.com/lukas-frey "lukas-frey (1 commits)")[![michaelklopf](https://avatars.githubusercontent.com/u/2892138?v=4)](https://github.com/michaelklopf "michaelklopf (1 commits)")[![mustafa-online](https://avatars.githubusercontent.com/u/5323832?v=4)](https://github.com/mustafa-online "mustafa-online (1 commits)")[![nabileisa](https://avatars.githubusercontent.com/u/6467131?v=4)](https://github.com/nabileisa "nabileisa (1 commits)")[![Saad5400](https://avatars.githubusercontent.com/u/86385454?v=4)](https://github.com/Saad5400 "Saad5400 (1 commits)")[![tobiasla78](https://avatars.githubusercontent.com/u/17968060?v=4)](https://github.com/tobiasla78 "tobiasla78 (1 commits)")[![arsounet](https://avatars.githubusercontent.com/u/758451?v=4)](https://github.com/arsounet "arsounet (1 commits)")[![ziming](https://avatars.githubusercontent.com/u/679513?v=4)](https://github.com/ziming "ziming (1 commits)")[![ashwin-nath-m](https://avatars.githubusercontent.com/u/50395482?v=4)](https://github.com/ashwin-nath-m "ashwin-nath-m (1 commits)")[![AurelDemiri](https://avatars.githubusercontent.com/u/30560661?v=4)](https://github.com/AurelDemiri "AurelDemiri (1 commits)")[![dev3k](https://avatars.githubusercontent.com/u/241731?v=4)](https://github.com/dev3k "dev3k (1 commits)")[![donatiss](https://avatars.githubusercontent.com/u/6832828?v=4)](https://github.com/donatiss "donatiss (1 commits)")

---

Tags

filament-pluginfilamentadminfilamentpanelsfilamentphplaravelfilament-pluginfilamentphpbezhanSallehfilament-language-switchfilament-language-changerfilament-locale-changerfilament-localizer

###  Code Quality

TestsPest

Static AnalysisPHPStan, Rector

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/bezhansalleh-filament-language-switch/health.svg)

```
[![Health](https://phpackages.com/badges/bezhansalleh-filament-language-switch/health.svg)](https://phpackages.com/packages/bezhansalleh-filament-language-switch)
```

###  Alternatives

[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3914.6k](/packages/rawilk-profile-filament-plugin)[defstudio/telegraph

A laravel facade to interact with Telegram Bots

816333.9k3](/packages/defstudio-telegraph)[psalm/plugin-laravel

Psalm plugin for Laravel

3355.3M346](/packages/psalm-plugin-laravel)[harris21/laravel-fuse

Circuit breaker for Laravel queue jobs. Protect your workers from cascading failures.

44855.7k](/packages/harris21-laravel-fuse)[croustibat/filament-jobs-monitor

Background Jobs monitoring like Horizon for all drivers for FilamentPHP

274327.0k8](/packages/croustibat-filament-jobs-monitor)[stephenjude/filament-jetstream

A Laravel starter kit built with Filament inspired by Jetstream.

17760.2k3](/packages/stephenjude-filament-jetstream)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
