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 Localization Laravel Package

mominalzaraa/filament-localization

Automatically localize your Filament app: this package scans resources, fields, columns, actions, and components, then generates structured translation files—eliminating the repetitive manual work of adding translation keys. Requires PHP 8.3+, Laravel 12/13, Filament 5.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and read the config before you run anything.

composer require mominalzaraa/filament-localization
php artisan vendor:publish --tag="filament-localization-config"

Then open config/filament-localization.php first — it is the only real API surface of this package. Three keys determine everything you'll live with afterwards:

  • structure → panel-based (default) writes lang/{locale}/filament/{panel}/{resource}.php. Choose this unless you have a pre-existing lang/ convention you need to match.
  • locales → the list the --locale flags default to.
  • translation_key_prefix → filament by default; changing it later means a repo-wide find/replace in your resources, so decide now.

First use case: one throwaway panel, on a branch, in dry-run.

git checkout -b chore/localization
php artisan filament:localize --panel=admin --dry-run

Read the diff output before dropping --dry-run. This package rewrites your PHP source files — it strips hardcoded labels off TextInput::make()->label('Name'), nulls out static properties like $modelLabel/$navigationLabel, and appends getModelLabel() / getNavigationLabel() methods returning __('filament/admin/user_resource.model_label'). That's the whole value proposition and also the whole risk. Never point it at main the first time.

Once the diff looks sane:

php artisan filament:localize --panel=admin --no-git

Use --no-git on the first real run so you author the commit and can review it against your own working tree, rather than letting the package commit for you.

The generated lang/en/filament/admin/*.php files are your source of truth for the English strings. Translation (DeepL) is a separate, optional second pass:

php artisan filament:translate-with-deepl --source-lang=en --target-lang=el --panel=admin --dry-run

Finally, wire up a switcher — this package only generates files, it does not give you a locale UI. craft-forge/filament-language-switcher in your panel provider is the documented path.

Implementation Patterns

Adopt it early in a project, not retroactively mid-sprint. The cost of this package scales with how many resources you already have and how many manual __() calls you've already sprinkled in. On a greenfield panel, run filament:localize every time you add a resource and it stays cheap. On a 40-resource app with hand-written translations, it will fight you.

Commit the code rewrite and the translations separately.

php artisan filament:localize --panel=admin --no-git
git add app/Filament lang/en && git commit -m "chore: filament localization scaffolding"

php artisan filament:translate-with-deepl --source-lang=en --target-lang=el --panel=admin
git add lang/el && git commit -m "chore: greek translations for admin panel"

Two commits means a bad DeepL batch is a git revert of one directory, not an unpicking of resource files.

Treat it as a regeneration tool, not a run-once installer. The realistic loop is:

  1. Add a new resource / field with a plain string label.
  2. php artisan filament:localize --panel=admin --dry-run → confirm only the new component is touched.
  3. Run for real, --no-git.
  4. php artisan filament:translate-with-deepl --source-lang=en --target-lang=el --panel=admin (no --force) — smart detection only fills missing/identical keys, so existing human-reviewed strings survive.
  5. Commit.

Guard the language files with a parity test. Since the package generates files per resource per locale, drift is inevitable — someone adds a field, forgets to re-run for fr. A cheap Pest test catches it in CI without ever executing the mutating command:

it('has matching filament keys across locales', function () {
    $keys = fn (string $locale) => collect(File::allFiles(lang_path("$locale/filament")))
        ->mapWithKeys(fn ($f) => [
            $f->getRelativePathname() => array_keys(Arr::dot(require $f->getPathname())),
        ]);

    $en = $keys('en');
    foreach (config('filament-localization.locales') as $locale) {
        if ($locale === 'en') continue;
        expect($keys($locale)->map(fn ($k) => $k)->toArray())->toEqual($en->toArray());
    }
});

Never run filament:localize or translate-with-deepl in CI or a deploy script. They write source files. They belong on a developer machine, in a reviewed commit.

Scope exclusions in config, not by hand. When a panel is generated/admin-tooling and shouldn't be localized, add it to excluded_panels. Same for a resource that's intentionally English-only. Editing the files after every run because you keep forgetting an exclusion is a losing game.

Know the layer boundary. This package localizes Filament UI chrome — labels, headings, column headers, action names, page titles. It does nothing for model content. If you're storing title_en / title_el columns you still want spatie/laravel-translatable plus the Filament translatable plugin. They coexist fine; just don't expect the DeepL command to touch your articles table.

Extend by editing the generated files, not by forking. The output is plain return [...] arrays under lang/. Refine a machine translation by hand and the next non---force DeepL run will preserve it (it only overwrites keys missing from the target or byte-identical to the source).

Gotchas and Tips

Version constraints are the biggest day-to-day obstacle. This is Filament v5, Laravel 12/13, PHP 8.3+ — and Filament v4 is explicitly deprecated (use the 1.x line). If your app is on Filament v3/v4 or PHP 8.2, you cannot install this at all. Verify composer why-not before you plan any of the above.

11 stars, 0 dependents, code-mutating. That combination means: pin an exact version in composer.json, run it on a branch, and read every generated diff. Backup (backup: true) and the git auto-commit are conveniences, not a safety net — a backup directory inside your project will also get committed unless you gitignore it. Prefer --no-git plus your own branch hygiene.

--force is destructive. On filament:localize it re-localizes components that already have labels (potentially clobbering your hand-written __() calls). On translate-with-deepl it overwrites all target-locale strings, including translator-reviewed ones. Reserve it for "the English labels changed wholesale, retranslate the lot," and always --dry-run first.

The smart-detection heuristic misfires on short strings. A target value byte-identical to the source is treated as untranslated — unless it's in skip_identical_terms. So a legitimate 'Email' => 'Email' in Greek gets sent to DeepL; meanwhile the default skip list is heavy on acronyms (API, JSON, GDPR) and light on your domain vocabulary. Add your own product nouns, brand names and abbreviations to skip_identical_terms before your first DeepL run — after a bad batch you'll be hand-reverting.

DeepL locale codes won't always match your app's codes. DeepL uses EN, EL, PT-BR, ZH, etc. Run --dry-run with one target language and confirm the mapping resolves before batching five locales; a silent fallback to English is easy to miss in a large diff. Also budget the quota — batch_size defaults to 50 and a mid-size panel can consume the free tier's 500k characters quickly.

Renaming or deleting a resource orphans its translation file. lang/en/filament/admin/old_resource.php sticks around and filament:localize won't remove it, so your parity test keeps passing on dead keys. Sweep lang/*/filament/ against app/Filament/*/Resources periodically.

Method-injection can collide with your own overrides. The package appends getModelLabel(), getNavigationLabel(), getTitle(), getHeading() returning __(...). If you already defined any of those on a resource or page, review the result — a duplicated method is a fatal error, and a "helpfully" rewritten one silently discards your custom logic (e.g. a label derived from a record). Diff carefully on resources that already override these.

Idempotency isn't guaranteed by the docs. Run filament:localize twice in a row and check the second diff is empty. If the package re-injects methods or duplicates array keys in the generated files, it must only ever run from a clean tree — which is another argument for the branch-per-run habit.

The agents/, graft/ and skills/ docs in the repo are for contributing to the package, not for using it. Ignore them unless you're filing a PR; the consumer docs are the README and config/filament-localization.php.

Debugging a wrong label: check, in order, (1) the generated key in lang/{locale}/filament/{panel}/{resource}.php, (2) the __() call the package injected into the resource, (3) the panel's own ->locale() / middleware, and (4) Filament's component cache (php artisan filament:cache-components / optimize:clear). If the key exists and resolves but the UI shows the raw key, it's a locale-resolution problem in your panel, not a localization-file problem.

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.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log