3x1io/filament-translations
Manage translations for your Laravel and Filament app from a single, unified interface. Edit language files, scan for missing keys, and switch locales without leaving your admin panel.
3x1io/filament-translations adds a Translations resource to a Filament panel so non-developers can edit lang/ strings without touching code or opening a PR. It works with file-based translations and, optionally, spatie/laravel-translation-loader for DB-backed translations.
composer require 3x1io/filament-translationsphp artisan vendor:publish --tag="filament-translations-migrations" then php artisan migrate (only needed for the DB driver — skip it if you only edit files).app/Providers/Filament/AdminPanelProvider.php:->plugins([
\TomatoPHP\FilamentTranslations\FilamentTranslationsPlugin::make(),
])
Where to look first: the published config file (locale list, driver/loader settings) and the plugin class itself — its fluent setters control the navigation label, group, slug, and whether the resource is registered at all. Those two files answer 90% of "why isn't this showing up / why isn't this locale listed" questions.
First use case: open the Translations resource, pick a locale tab, and edit a marketing string that changes weekly. That is the sweet spot — copy tweaks by a content editor, not structural translation work.
File driver vs. DB driver. Decide once, early. File-based editing writes directly into lang/{locale}/*.php, which means edits made in production are invisible to your repo and get clobbered on the next deploy if the directory is part of the image. If editors are non-developers, use the DB driver via spatie/laravel-translation-loader and treat the files as the seed source.
Import-then-edit workflow. Add keys in code as you normally would, deploy, then use the resource to fill in the values for the remaining locales. Keep the source-of-truth locale (usually en) in files, and let the DB hold the rest. This avoids the classic "who overwrote lang/de.json" merge conflict.
Panel scoping. Register the plugin only on the admin panel, and gate it with a policy or Filament Shield permission. Translation editing is effectively arbitrary string injection into your UI — it belongs behind canAccessPanel() plus a dedicated permission, not open to every panel user.
Multi-line content. As of v1.2.1 the value field is a textarea instead of a text input. That matters for email bodies, markdown, legal copy, and HTML snippets, which previously rendered as one long horizontal scroll. If you maintain a fork or a custom resource that overrides the form schema, port the change over — otherwise your editors keep the worse UX.
Integration with navigation and settings. Keep short strings (nav labels, button text, enum labels) editable here too, but be disciplined: keys used in ->label() calls are easy to break by renaming, and the resource has no idea which keys are referenced in code. Prefer adding keys over renaming them.
Renaming or deleting a key breaks code silently. There is no static analysis tying a key to its __() call site. Missing translations just fall back to the key string in the UI. Rename in code and in the panel in the same change, or don't rename at all.
Caching is the usual culprit. After edits, values may not appear because of the config cache, the translation-loader cache, or OPcache in a container. Run php artisan optimize:clear and, if you use the loader's cache, clear the configured cache store. Bake this into your deploy/ops docs.
Read-only filesystems. File-driver saves fail on containerized or immutable deploys. The error often surfaces as a generic save failure in the Filament notification rather than a clear permission error — check storage/lang permissions and whether the path is writable before debugging the package.
Textarea side effects. A textarea makes it trivial to paste a trailing newline or leading space into a string that ends up in a <title>, a nav item, or a CSV export. Trim on save where it matters, and spot-check values used in tight layouts after editors have been in there.
Large key sets get slow. With thousands of keys the resource becomes a heavy page. Lean on the search field, and consider splitting per-group or per-namespace resources if your app has grown past a few hundred keys.
HTML and XSS. If translations are rendered unescaped ({!! !!}), the panel becomes a stored-XSS vector for anyone with edit rights. Restrict access accordingly; don't treat it as a low-risk screen.
Upgrading to v1.2.1 is a non-breaking UI change. The only real risk is if you've subclassed the resource and redefined the value field — re-check your override still matches, and confirm newline handling in any value you rely on for single-line display. Pin the version in composer.json and run your panel's smoke tests after the bump.
How can I help you explore Laravel packages today?