wotz/filament-translatable-strings
Filament Translatable Strings adds a simple Filament admin interface for managing Laravel translation strings. Browse, search, and edit language lines across locales without touching files, making multilingual app maintenance faster and easier for developers and content teams.
Because this is a single-star, zero-dependent package with no ecosystem track record, the first step is not composer require — it's a 10-minute source audit.
Before installing, read:
composer.json — check the Filament major version constraint (^3.0 vs ^4.0), Laravel/PHP floor, and whether it pulls in anything heavy.src/*ServiceProvider.php — see what it registers: a Filament plugin, a resource, migrations, commands, or all four.config/filament-translatable-strings.php (after publishing) — the locales whitelist, translation paths, and storage driver are almost always here.database/migrations/ — determines whether translations live in the DB or are written back to lang/*.php files. This single fact drives your entire workflow.README.md for the plugin registration line and any artisan commands.Install and wire up:
composer require wotz/filament-translatable-strings
php artisan vendor:publish --tag="filament-translatable-strings-config"
php artisan vendor:publish --tag="filament-translatable-strings-migrations"
php artisan migrate
Register the plugin in your panel provider (exact class name per README — typically):
public function panel(Panel $panel): Panel
{
return $panel
->plugins([
\Wotz\FilamentTranslatableStrings\FilamentTranslatableStringsPlugin::make(),
]);
}
First use case: give a non-technical editor a UI to fix marketing copy in lang/en/*.php and lang/de/*.php without a PR. Start narrow — one locale pair, one namespace (lang/en/pages.php) — verify the resource lists your existing keys, edit one string, and confirm the change round-trips to the file or DB. Only then widen the scanned paths. If the round-trip is lossy (comments stripped, ordering scrambled), stop and reconsider before letting editors loose.
Pin the version exactly ("wotz/filament-translatable-strings": "1.x.y") — with one star and no dependents, a minor bump is effectively an unreviewed breaking change.
Establish the source of truth first. There are two architectures this package can plausibly use, and they demand opposite workflows:
translatable_strings table, read back via a loader): the DB becomes authoritative; lang/*.php is seed data. Deploy must run a seed/sync command or translations diverge between environments. Back up the table like any other content.lang/**/*.php): git remains authoritative. Treat the Filament UI as a convenience editor on staging, then commit the diff. Never let two editors write simultaneously on a shared filesystem without backup.Confirm which one you have before designing anything else — the README and the presence/absence of a migration will tell you.
Seeding and scanning. Most packages in this space ship a scan command that walks lang/ (or your __() calls) and imports keys. Wire it into your deploy script once, idempotently:
php artisan translatable-strings:scan # verify actual command name in README
php artisan migrate --force
Run it after config:cache so the scanner sees production locale paths, not your local ones.
Editor workflow. The healthy loop is: developer adds __('pages.hero.title') in a Blade file → scan picks up the new key → translator fills the German cell in Filament → export/commit (file mode) or cache flush (DB mode). Document this loop for your team; the package provides the UI, not the process.
Authorization. A translation resource exposes every string in your app, including error messages and internal labels. Register a policy or use canAccess() scoped to a translator/admin role (Filament Shield pairs well). Never leave it open to all panel users.
Cache invalidation on save. If your app caches translations (Spatie's loader, custom Cache::remember('lang.'.$locale)), add an observer or model event that flushes the relevant locale key on save. Without it, editors save a string, reload, and see the old value — the #1 support ticket for these tools.
Locale strategy. Restrict the Filament locale switcher to the locales you actually maintain. Editing the fallback locale through the UI is how you get an English string silently overwriting a translation when a key is missing.
CI guardrail. Add a check that lang/en keys are a superset of every other locale, e.g. a small artisan command comparing array_dot(require ...) output. This catches strings added by developers who never opened the Filament UI.
Customization. Filament resources are meant to be extended: subclass the package resource, override table(), form(), getNavigationGroup(), and register yours instead of the package's if the config allows. Publish views (--tag=...-views) before editing them so upgrades don't blow them away.
lang/*.php, expect lost comments, reordered keys, and quoting bugs on strings containing ', \, or $. Test with adversarial strings (It's 100% "free" — $5) before trusting it. Prefer DB mode or a JSON-based single file if offered.{"Welcome": "Willkommen"}) and PHP array files (['welcome' => '...']) behave differently. Scanners frequently handle only one. Verify both if you use both.__('vendor::messages.foo') and nested messages.validation.required keys are common blind spots in scanners — confirm they appear in the resource.:name or breaking {0} none|{1} one|[2,*] many. Add validation in a custom form field or accept that broken strings reach production.composer.json against your installed version; a mismatch surfaces as a Class not found or a missing navigation item, not a helpful error.php artisan config:clear locally and config:cache in deploy — the scanned paths and locale list are read from config and will silently be the old values otherwise.canAccess()/policy, check shouldRegisterNavigation(), and run php artisan filament:optimize-clear (or filament:cache-components --clear).lang/. That's a deploy-time permissions problem that only appears in production — and a security surface worth avoiding entirely. Prefer an export command plus a CI commit.lang/ directory in sync at all times so the package is always disposable.How can I help you explore Laravel packages today?