| Install | |
|---|---|
composer require fnx-software/filament-astrotomic |
|
| Latest Version: | v2.0.0 |
| PHP: | ^8.2|^8.3|^8.4 |
This package is an extension for Filament v4 & 5 and laravel-translatable to easily manage multilingual content in your admin panel.
This is an enhanced fork of doriiaan/filament-astrotomic and the original cactus-galaxy/filament-astrotomic, updated for Filament 4 and 5 and introducing powerful new features like a reactive LocaleSwitcher and dedicated components for displaying translated content.
You can install the package via Composer:
composer require fnx-software/filament-astrotomic
Publish the configuration for astrotomic/laravel-translatable:
php artisan vendor:publish --tag="translatable"
Configure the locales your app should use in config/translatable.php:
// config/translatable.php
'locales' => [
'en',
'es',
'fr',
],
Register the plugin in your Panel Provider:
// app/Providers/Filament/AdminPanelProvider.php
use Fnxsoftware\FilamentAstrotomic\FilamentAstrotomicPlugin;
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->plugins([
FilamentAstrotomicPlugin::make(),
]);
}
By default, the main locale is taken from your config/translatable.php file. You can override this dynamically when registering the plugin.
Set a static main locale:
FilamentAstrotomicPlugin::make()
->mainLocale('ar')
Set a dynamic main locale (e.g., from database settings):
use App\Models\Setting;
FilamentAstrotomicPlugin::make()
->mainLocale(fn () => Setting::where('key', 'default_locale')->first()?->value ?? 'en')
By default, the available locales are loaded from your config/translatable.php file.
You can override the locale list when registering the plugin, which is useful when your locales are stored in the database (e.g., per-tenant settings).
Set a static locales list:
FilamentAstrotomicPlugin::make()
->locales(['ar', 'en', 'fr'])
Make your Eloquent model translatable as described in the laravel-translatable documentation.
// app/Models/Post.php
use Illuminate\Database\Eloquent\Model;
use Astrotomic\Translatable\Contracts\Translatable as TranslatableContract;
use Astrotomic\Translatable\Translatable;
class Post extends Model implements TranslatableContract
{
use Translatable;
public array $translatedAttributes = ['title', 'content'];
protected $fillable = ['author_id'];
}
Apply the ResourceTranslatable trait to your Filament resource class:
// app/Filament/Resources/PostResource.php
use Fnxsoftware\FilamentAstrotomic\Resources\Concerns\ResourceTranslatable;
use Filament\Resources\Resource;
class PostResource extends Resource
{
use ResourceTranslatable;
// ...
}
You must also apply the corresponding translatable trait to each of your resource's pages.
List Page:
// app/Filament/Resources/PostResource/Pages/ListPosts.php
use Fnxsoftware\FilamentAstrotomic\Resources\Pages\ListTranslatable;
use Filament\Resources\Pages\ListRecords;
class ListPosts extends ListRecords
{
use ListTranslatable;
// ...
}
Create Page:
// app/Filament/Resources/PostResource/Pages/CreatePost.php
use Fnxsoftware\FilamentAstrotomic\Resources\Pages\CreateTranslatable;
use Filament\Resources\Pages\CreateRecord;
class CreatePost extends CreateRecord
{
use CreateTranslatable;
// ...
}
Edit Page:
// app/Filament/Resources/PostResource/Pages/EditPost.php
use Fnxsoftware\FilamentAstrotomic\Resources\Pages\EditTranslatable;
use Filament\Resources\Pages\EditRecord;
class EditPost extends EditRecord
{
use EditTranslatable;
// ...
}
View Page:
// app/Filament/Resources/PostResource/Pages/ViewPost.php
use Fnxsoftware\FilamentAstrotomic\Resources\Pages\ViewTranslatable;
use Filament\Resources\Pages\ViewRecord;
class ViewPost extends ViewRecord
{
use ViewTranslatable;
// ...
}
TranslatableTabs for Localized FieldsTo manage translations for your model's attributes, use the TranslatableTabs component. It automatically creates a tab for each locale.
use Fnxsoftware\FilamentAstrotomic\Schemas\Components\TranslatableTabs;
use Fnxsoftware\FilamentAstrotomic\TranslatableTab;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\RichEditor;
use Filament\Forms\Form;
public static function form(Form $form): Form
{
return $form->schema([
TranslatableTabs::make()
->localeTabSchema(fn (TranslatableTab $tab) => [
TextInput::make($tab->makeName('title'))
->required($tab->isMainLocale()), // Required only for the main language
RichEditor::make($tab->makeName('content')),
])
]);
}
To make it clearer which language a field belongs to, you can use the makePrefixLabel() and makeSuffixLabel() methods on the TranslatableTab object to automatically add the locale name to your field labels.
use Fnxsoftware\FilamentAstrotomic\Schemas\Components\TranslatableTabs;
use Fnxsoftware\FilamentAstrotomic\TranslatableTab;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Textarea;
TranslatableTabs::make()
->localeTabSchema(fn (TranslatableTab $tab) => [
TextInput::make($tab->makeName('title'))
// Renders label as "Title (English)"
->label($tab->makePrefixLabel('Title')),
Textarea::make($tab->makeName('description'))
// Renders label as "(English) Description"
->label($tab->makeSuffixLabel('Description')),
])
TranslatableTabs InstanceBy default, TranslatableTabs will use the locales configured for the plugin (or the translatable.locales config).
If you need to override the locales for a specific form (or even for a single set of fields), you may pass a custom list directly to the component:
use Fnxsoftware\FilamentAstrotomic\Schemas\Components\TranslatableTabs;
use Fnxsoftware\FilamentAstrotomic\TranslatableTab;
use Filament\Forms\Components\TextInput;
TranslatableTabs::make()
->customLocales(['ar', 'en', 'fr', 'pt'])
->localeTabSchema(fn (TranslatableTab $tab) => [
TextInput::make($tab->makeName('label'))
->required($tab->isMainLocale()),
])
TranslatableSelect for RelationshipsThe standard Select::relationship() component does not work with astrotomic/laravel-translatable. This package provides a TranslatableSelect component to correctly load, search, and display options from a translatable BelongsTo relationship.
use Fnxsoftware\FilamentAstrotomic\Forms\Components\TranslatableSelect;
use Filament\Forms\Form;
public static function form(Form $form): Form
{
return $form->schema([
// ... other fields
TranslatableSelect::make('country_id')
->translatableRelationship('country', 'name')
->searchable()
->preload()
->label('Country')
->required(),
]);
}
This will correctly display the country names in the current locale, and the search functionality will also work on the translated names.
This package provides a seamless way to view translated content on your List and View pages using a LocaleSwitcher action that works with dedicated table columns and infolist entries.
LocaleSwitcher ActionAdd the LocaleSwitcher to the header actions of your List and View pages.
// In your List page (e.g., ListPosts.php)
use Fnxsoftware\FilamentAstrotomic\Actions\LocaleSwitcher;
use Filament\Actions\CreateAction;
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
LocaleSwitcher::make(), // Add this
];
}
TranslatableColumn in TablesThe TranslatableColumn automatically displays the translation for the selected locale and provides out-of-the-box search and sort functionality.
// In your Table definition
use Fnxsoftware\FilamentAstrotomic\Tables\Columns\TranslatableColumn;
use Filament\Tables\Table;
public static function table(Table $table): Table
{
return $table
->columns([
TranslatableColumn::make('title')
->searchable()
->sortable(),
// ... other columns
]);
}
TranslatableEntry in InfolistsSimilarly, use TranslatableEntry in your infolists to display translated content that reacts to the LocaleSwitcher.
// In your Infolist definition
use Fnxsoftware\FilamentAstrotomic\Infolists\Components\TranslatableEntry;
use Filament\Infolists\Infolist;
public static function infolist(Infolist $infolist): Infolist
{
return $infolist
->schema([
TranslatableEntry::make('title'),
TranslatableEntry::make('content'),
// ... other entries
]);
}
When your application or a specific tenant only has one locale active, TranslatableTabs will automatically switch its layout to a standard Grid.
This removes the unnecessary tab bar, rendering the fields directly. This provides a cleaner UI for single-language contexts without requiring code changes.
If you prefer to always show the language tabs (even when only one locale is available), you can force the layout using one of two methods:
Force tabs globally for all TranslatableTabs components in your AdminPanelProvider:
use Fnxsoftware\FilamentAstrotomic\FilamentAstrotomicPlugin;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->plugin(
FilamentAstrotomicPlugin::make()
->force() // Always show tabs
);
}
Force tabs on a specific component:
use Fnxsoftware\FilamentAstrotomic\Schemas\Components\TranslatableTabs;
TranslatableTabs::make('translations')
->force() // Always show tabs for this specific component
->localeTabSchema(fn (TranslatableTab $tab) => [
// ...
])
You can now define specific locales for the LocaleSwitcher action, overriding the global or tenant configurations. This is useful if specific resources or pages only support a subset of languages.
use Fnxsoftware\FilamentAstrotomic\Actions\LocaleSwitcher;
// Pass a list of locale codes (labels generated automatically)
LocaleSwitcher::make()
->locales(['en', 'fr']);
// Or pass custom labels
LocaleSwitcher::make()
->locales([
'en' => 'English (US)',
'fr' => 'Français (France)',
]);
Override the getTranslatableLocales() method in your resource to specify a different set of locales than the global configuration.
public static function getTranslatableLocales(): array
{
return ['en', 'fr'];
}
To use translatable fields inside modal actions (like an EditAction on a table row), you must correctly mutate the data.
use App\Models\Post;
use Filament\Tables\Actions\EditAction;
->actions([
EditAction::make()->mutateRecordDataUsing(function (Post $record, array $data) {
return static::mutateTranslatableData($record, $data);
}),
])
Both TranslatableColumn and TranslatableEntry fully support displaying, searching, and sorting translated attributes on nested relationships using dot notation.
TranslatableColumn with a relationship:
The column will display the country's name, and the search and sort functionality will correctly filter and order the governorates table based on the name of the related country.
// In your GovernorateResource table
TranslatableColumn::make('country.name')
->label('Country')
->searchable()
->sortable(),
TranslatableEntry with a relationship:
The entry will display the translated name of the related country on the governorate's view page.
// In your GovernorateResource infolist
TranslatableEntry::make('country.name')
->label('Country'),
composer test
Please see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.
The MIT License (MIT). Please see License File for more information.