Search by

belaaredj / filament-localized

chiina14

Multilingual infrastructure plugin for Filament 5.

Package info

github.com/chiina14/filament-localized

pkg:composer/belaaredj/filament-localized

Fund package maintenance!

Belaaredj

Statistics

Installs: 11

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.2.1 2026-09-25 09:34 UTC

This package is auto-updated.

Last update: 2026-09-25 09:35:01 UTC


README

Multilingual infrastructure for Filament 5, providing reusable localized form fields, relationship selects, table columns, infolist entries, multilingual search, and configurable translation fallbacks.

Features

  • 🌍 Multi-language form tabs
  • πŸ”Ž Multilingual table search
  • πŸ”— Localized relationship selects
  • πŸ” Multilingual relationship search
  • πŸ“‹ Localized table columns
  • πŸ“„ Localized infolist entries
  • ↔️ RTL/LTR locale support
  • πŸ”„ Configurable translation fallback
  • βš™οΈ Centralized locale configuration
  • 🧩 Reusable API through Localized
  • πŸ§ͺ Tested with Filament 5

Requirements

  • PHP ^8.2
  • Filament ^5.0
  • Laravel application compatible with Filament 5

Installation

Install the package with Composer:

composer require Belaaredj/filament-localized

Publish the package configuration:

php artisan vendor:publish --tag=filament-localized-config

The configuration file will be available at:

config/filament-localized.php

You can then customize the supported locales and fallback behavior.

Configuration

The package stores translations as JSON objects.

For example:

{
    "ar": "Ψ§Ω„Ψ±ΩˆΨ¨ΩˆΨͺΨ§Ψͺ",
    "fr": "Robotique",
    "en": "Robotics"
}

The default configuration supports Arabic, French, and English:

'locales' => [

    'ar' => [
        'label' => 'Ψ§Ω„ΨΉΨ±Ψ¨ΩŠΨ©',
        'short' => 'AR',
        'direction' => 'rtl',
    ],

    'fr' => [
        'label' => 'FranΓ§ais',
        'short' => 'FR',
        'direction' => 'ltr',
    ],

    'en' => [
        'label' => 'English',
        'short' => 'EN',
        'direction' => 'ltr',
    ],

],

You can add or remove locales according to your application.

For example:

'locales' => [

    'ar' => [
        'label' => 'Ψ§Ω„ΨΉΨ±Ψ¨ΩŠΨ©',
        'short' => 'AR',
        'direction' => 'rtl',
    ],

    'fr' => [
        'label' => 'FranΓ§ais',
        'short' => 'FR',
        'direction' => 'ltr',
    ],

    'en' => [
        'label' => 'English',
        'short' => 'EN',
        'direction' => 'ltr',
    ],

    'de' => [
        'label' => 'Deutsch',
        'short' => 'DE',
        'direction' => 'ltr',
    ],

],

Translation Fallback

The package resolves translations using the following order:

Current locale β†’ fr β†’ en β†’ ar

For example, when the current locale is ar:

[
    'fr' => 'Robotique',
    'en' => 'Robotics',
]

The resolved value will be:

Robotique

If French is also unavailable:

[
    'en' => 'Robotics',
]

the package will resolve:

Robotics

Configure the fallback order in:

'fallback_locales' => [
    'fr',
    'en',
    'ar',
],

The current application locale is always checked first.

Localized Form Tabs

Use Localized::tabs() to create language tabs for your form fields.

use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\RichEditor;
use Belaaredj\FilamentLocalized\Facades\Localized;

Localized::tabs([
    TextInput::make('name')
        ->label('Name'),

    RichEditor::make('description')
        ->label('Description'),
])

The package generates a field structure based on the configured locales.

For example:

name.ar
name.fr
name.en

description.ar
description.fr
description.en

The resulting state can be stored directly in a JSON column:

{
    "ar": "Ψ§Ω„Ψ±ΩˆΨ¨ΩˆΨͺΨ§Ψͺ",
    "fr": "Robotique",
    "en": "Robotics"
}

Database columns

Your translation fields should normally use a JSON-compatible database column.

For Laravel migrations:

$table->json('name')->nullable();
$table->json('description')->nullable();

For MySQL, make sure the database supports JSON columns.

Localized Select

Use Localized::select() for localized relationship options.

use Belaaredj\FilamentLocalized\Facades\Localized;

Localized::select('skill_id')
    ->relationship('skill')
    ->localizedTitle('name')
    ->localizedSearch()
    ->searchable();

The option label is resolved using the configured translation fallback.

For example:

{
    "ar": "Ψ§Ω„Ψ±ΩˆΨ¨ΩˆΨͺΨ§Ψͺ",
    "fr": "Robotique",
    "en": "Robotics"
}

The displayed option automatically follows the current locale and fallback configuration.

Multiple relationships

The same API works with multiple relationships:

Localized::select('skills')
    ->multiple()
    ->relationship('skills')
    ->localizedTitle('name')
    ->localizedSearch()
    ->searchable();

Localized Relationship Search

When:

->localizedSearch()

is enabled, relationship searches are performed across the configured search locales.

For example:

Localized::select('skill_id')
    ->relationship('skill')
    ->localizedTitle('name')
    ->localizedSearch()
    ->searchable();

A search can match:

Ψ§Ω„ΨΉΨ±Ψ¨ΩŠΨ©

or:

Robotique

or:

Robotics

depending on the configured search locales.

By default:

'search_locales' => null,

means that all configured locales are searched.

You can restrict the locales:

'search_locales' => [
    'ar',
    'fr',
],

Localized Table Columns

Use Localized::column() for translated JSON attributes in Filament tables.

use Belaaredj\FilamentLocalized\Facades\Localized;

Localized::column('name')
    ->label('Name');

Enable multilingual searching with:

Localized::column('name')
    ->label('Name')
    ->localizedSearch()
    ->searchable();

The column automatically resolves the displayed translation using the configured fallback order.

Localized Infolist Entries

Use Localized::entry() for translated attributes in Filament infolists.

use Belaaredj\FilamentLocalized\Facades\Localized;

Localized::entry('description')
    ->label('Description');

The displayed value follows the same locale and fallback rules used by the other package components.

Supported API

The package provides a centralized API:

Method Purpose
Localized::tabs() Create multilingual form tabs
Localized::select() Create localized relationship selects
Localized::column() Display and search localized table columns
Localized::entry() Display localized infolist values

The underlying specialized components are also available:

LocalizedTabs
LocalizedSelect
LocalizedTextColumn
LocalizedTextEntry

Filament Panel Plugin

The package also provides a Filament plugin class:

use Belaaredj\FilamentLocalized\FilamentLocalizedPlugin;

$panel
    ->plugin(
        FilamentLocalizedPlugin::make()
    );

The plugin is intentionally lightweight. The localized components can be used independently and do not require additional panel-specific configuration.

Locale Switcher

The plugin includes an enabled-by-default locale switcher in the Filament topbar. Configure it per panel:

use Filament\View\PanelsRenderHook;
use Belaaredj\FilamentLocalized\FilamentLocalizedPlugin;

$panel->plugin(
    FilamentLocalizedPlugin::make()
        ->localeSwitcher()
        ->localeSwitcherHook(PanelsRenderHook::TOPBAR_END)
);

Disable it for a panel with ->localeSwitcher(false). The switcher validates locales, stores the selection in the session, and redirects back to the same-site referring page. Persistent panel middleware reapplies the locale before Filament renders each request, including Livewire requests.

Locale Configuration

The complete configuration is available in:

config/filament-localized.php

Example:

return [

    'locales' => [

        'ar' => [
            'label' => 'Ψ§Ω„ΨΉΨ±Ψ¨ΩŠΨ©',
            'short' => 'AR',
            'direction' => 'rtl',
        ],

        'fr' => [
            'label' => 'FranΓ§ais',
            'short' => 'FR',
            'direction' => 'ltr',
        ],

        'en' => [
            'label' => 'English',
            'short' => 'EN',
            'direction' => 'ltr',
        ],

    ],

    'default_locale' => 'fr',

    'fallback_locales' => [
        'fr',
        'en',
        'ar',
    ],

    'search_locales' => null,

    'locale_switcher' => [
        'enabled' => true,
        'show_flag' => true,
        'show_label' => true,
        'show_short' => false,
        'flag_fallback' => 'short',
    ],

    'locale_persistence' => [
        'session' => true,
        'user' => [
            'enabled' => false,
            'attribute' => 'locale',
        ],
        'browser' => [
            'enabled' => false,
        ],
    ],

];

Locale properties

Each locale supports:

Property Description
label Full display name
short Short locale label
direction rtl or ltr
flag Text, emoji, or image URL

For example:

'ar' => [
    'label' => 'Ψ§Ω„ΨΉΨ±Ψ¨ΩŠΨ©',
    'short' => 'AR',
    'direction' => 'rtl',
],

Missing flags use flag_fallback (short, label, or none), so the switcher remains usable on systems whose fonts do not provide emoji flags. Locale resolution uses this priority: explicit valid request locale, session locale, optional authenticated-user attribute, optional browser language, then the configured default. User persistence and browser detection are disabled by default, so no user column or migration is required.

The locale direction is metadata for the selected language. The package does not force the entire Filament document into RTL or LTR by default. Apply LocaleManager::direction(app()->getLocale()) in an application layout only when the whole interface should follow that direction.

Troubleshooting

  • Clear configuration cache after changing the config with php artisan config:clear.
  • Confirm the locale code is a key in locales; unsupported route values are rejected.
  • Ensure the panel uses FilamentLocalizedPlugin so persistent middleware runs on navigation and Livewire requests.
  • If a flag does not display, use an image URL or set flag_fallback to short.

Using the Components Directly

The facade is the recommended API for most applications.

However, the underlying components can also be imported directly.

Localized Select

use Belaaredj\FilamentLocalized\Components\Forms\LocalizedSelect;

LocalizedSelect::make('skill_id')
    ->relationship('skill')
    ->localizedTitle('name')
    ->localizedSearch()
    ->searchable();

Localized Table Column

use Belaaredj\FilamentLocalized\Components\Tables\LocalizedTextColumn;

LocalizedTextColumn::make('name')
    ->localizedSearch()
    ->searchable();

Localized Infolist Entry

use Belaaredj\FilamentLocalized\Components\Infolists\LocalizedTextEntry;

LocalizedTextEntry::make('name');

Recommended Database Structure

A translated attribute should be stored as JSON.

Example migration:

Schema::create('skills', function (Blueprint $table) {
    $table->id();
    $table->json('name');
    $table->json('description')->nullable();
    $table->boolean('is_active')->default(true);
    $table->timestamps();
});

Example Eloquent model:

class Skill extends Model
{
    protected $fillable = [
        'name',
        'description',
        'is_active',
    ];

    protected function casts(): array
    {
        return [
            'name' => 'array',
            'description' => 'array',
            'is_active' => 'boolean',
        ];
    }
}

The package does not require a translation-specific database table.

Testing

The package uses Pest for automated testing.

Run the test suite:

composer test

Run static analysis:

composer analyse

Run code formatting:

composer lint

Run the complete development checks:

composer test
composer analyse
composer lint

Architecture

The package is organized around a small set of reusable components:

Belaaredj\FilamentLocalized
β”‚
β”œβ”€β”€ Components
β”‚   β”œβ”€β”€ Forms
β”‚   β”‚   β”œβ”€β”€ LocalizedTabs
β”‚   β”‚   └── LocalizedSelect
β”‚   β”‚
β”‚   β”œβ”€β”€ Infolists
β”‚   β”‚   └── LocalizedTextEntry
β”‚   β”‚
β”‚   └── Tables
β”‚       └── LocalizedTextColumn
β”‚
β”œβ”€β”€ Support
β”‚   β”œβ”€β”€ LocaleManager
β”‚   β”œβ”€β”€ TranslationManager
β”‚   └── TranslationQuery
β”‚
β”œβ”€β”€ Facades
β”‚   └── Localized
β”‚
β”œβ”€β”€ FilamentLocalized
β”œβ”€β”€ FilamentLocalizedPlugin
└── FilamentLocalizedServiceProvider

The package intentionally keeps translation resolution and multilingual querying separate from the UI components, making the underlying functionality reusable across forms, tables, infolists, and relationship fields.

Contributing

Contributions, bug reports, and feature requests are welcome.

Before submitting a pull request, please make sure the test suite and static analysis pass:

composer test
composer analyse
composer lint

For bugs and feature requests, please use the project's issue tracker.

License

The MIT License (MIT). Please see LICENSE for more information.

Author

Developed by Belaaredj Ahmed.

Filament Localized β€” reusable multilingual infrastructure for Filament 5.