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

Filament Translatable Tabs Laravel Package

codedor/filament-translatable-tabs

Filament Translatable Tabs organizes translatable fields into clean, locale-based tabs in Filament forms. Switch languages instantly, keep translations side by side, and reduce form clutter—simple setup for multilingual admin panels built with Filament and Laravel.

View on GitHub
Deep Wiki
Context7

Getting Started

Add the package with Composer and confirm the version resolves against your Filament major:

composer require wotzebra/filament-translatable-tabs

If the panel needs the bundled styles (locale badges, tab chrome), register the plugin in your panel provider:

->plugin(\Wotzebra\FilamentTranslatableTabs\FilamentTranslatableTabsPlugin::make())

Look here first:

  • README.md — the canonical signature for TranslatableTabs (locale list + schema callback).
  • src/Forms/Components/TranslatableTabs.php — the public API surface (locales(), schema(), badge/label options).
  • resources/css/plugin.css — now Tailwind v4; relevant only if you compile your own theme.

First use case: replace a form that repeats the same block of fields per language (either title.en, title.fr fields or three near-identical Sections) with a single tabbed component. In a PostResource form:

use Wotzebra\FilamentTranslatableTabs\Forms\Components\TranslatableTabs;

TranslatableTabs::make()
    ->locales(['en', 'fr', 'de'])
    ->schema(fn (string $locale) => [
        TextInput::make("title.{$locale}")->required($locale === 'en'),
        RichEditor::make("body.{$locale}"),
    ])

Each locale becomes a tab; the locale badge in the tab label carries the language name, so editors always know which tab they are editing.

Implementation Patterns

One component, one source of truth. Extract the per-locale schema into a static method on the resource or a dedicated PostForm class so the same field set is reused by create, edit, and any modal actions. The locale callback keeps field names assembled in one place:

->schema(fn (string $locale) => PostForm::translatableFields($locale))

JSON columns with spatie/laravel-translatable. If your model casts a column to spatie/laravel-translatable's Translatable, dotted field names (title.en) map cleanly onto the JSON payload. Set the model's $translatable array from the same locale list the component uses — a single config key (config('app.locales')) feeding both prevents drift.

Per-locale validation instead of blanket rules. Mark only the fallback locale ->required() and keep others optional; use ->validationAttribute() inside the callback so error messages read "Title (Français)" rather than "title.fr". For unique constraints, ->unique(ignoreRecord: true) inside the locale closure keeps scoping correct.

Reactive fields still work. ->live() / ->afterStateUpdated() inside the tab schema behave normally; just remember the state path includes the locale segment when you $set() or $get() from a sibling field.

Table and infolist parity. Pair the form with a column that surfaces which locales are populated — this is where the named locale badges from v3.1.0 help: a TextColumn rendering the badge HTML, or a ->formatStateUsing() that lists missing locales, gives editors a queue of untranslated records.

Custom theme integration. If you run a custom Filament theme, import the plugin's compiled CSS in your theme.css rather than its source, and only add Tailwind @source directives if you need utility classes generated from the package views.

Gotchas and Tips

Tailwind v4 CSS (v3.1.0). plugin.css is now authored against Tailwind v4. Precompiled output means most apps are unaffected, but if you previously imported the package's CSS source into a Filament v3 / Tailwind v3 theme build, your pipeline can choke or silently drop the badge styles. Either consume the shipped file or upgrade your theme build — do not mix v3 and v4 directives in one stylesheet.

Locale badges were renamed (v3.1.0). Any CSS overrides or test selectors targeting the old badge classes will break. Re-inspect the DOM in the browser after upgrading and move custom styling to the new class names. This is the one thing to grep for before bumping: grep -r "fi-.*locale" resources/ tests/.

Hidden tabs are still rendered. Every locale's fields are mounted in the Livewire component even when the tab is inactive. On models with many translatable columns × many locales, form payload and hydration cost grows quickly — trim rich editors and repeaters inside tabs, or lazy-load locales behind a Select filter for large language sets.

Dotted names need JSON-aware models. Fields named title.en only persist correctly when the column is cast to an array/JSON and the model handles translation keys. On plain string columns you will get a single title.en attribute, not a nested structure. Verify by dumping the saved record before wiring up more forms.

Debugging odd behaviour. Check the tab's locale list first — a locale present in config but missing from ->locales() silently produces no tab, so a "lost" field is almost always a mismatch between the form's locale list and the model's $translatable array. ->statePath() on the parent component shifts all dotted names, which is the second most common cause of missing values.

Extension points. The component's views are publishable, so overriding the badge markup is the supported route to custom labelling (flags, completion indicators). Prefer that over CSS hacks on generated class names, which have now changed once already.

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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource