ferdiunal/nova-translations
A Laravel Nova tool for managing application translations directly from your admin panel. Browse, edit, and organize language files without touching code, making multi-language content updates fast and simple for your whole team.
Minimal setup
composer require ferdiunal/nova-translations
php artisan vendor:publish --provider="Ferdiunal\NovaTranslations\ToolServiceProvider"
Register the tool in app/Nova/NovaServiceProvider.php:
public function tools()
{
return [
new \Ferdiunal\NovaTranslations\NovaTranslations(),
];
}
Then run php artisan nova:translations:install if the package ships an install command, or check config/nova-translations.php (created by the publish step) for the locale and path settings, and migrate if the config points at a database-backed translation store.
Where to look first
config/nova-translations.php — locales list, translation path/disk, and whether the source of truth is files or the DB.vendor/ferdiunal/nova-translations/src/ — the Tool class, any Http/Controllers, and the resources/js entrypoint that defines the UI screens.routes/ inside the package — confirms which endpoints the Nova UI hits, which is the fastest way to understand the data flow when something looks stale.First use case
Give non-developer stakeholders (marketing, support) a Nova screen where they can browse every locale and edit strings without touching lang/ or opening a PR. The highest-value first task is usually: import the existing lang/en/*.php + lang/{locale}/*.php (or lang/{locale}.json) into the tool, confirm the key tree renders correctly, then lock down who can reach the tool via Nova gates before rolling it out broadly.
Note on v1.0.3: the release ships with an empty changelog (only a compare link). Treat it as a patch-level maintenance bump — no documented breaking changes — but diff
v1.0.2...v1.0.3before merging into a locked-down project.
Locale as a first-class concern
Keep the tool's locale list sourced from the same place as the rest of the app, so a new locale in config/app.php doesn't silently miss the editor:
// config/nova-translations.php
'locales' => array_keys(config('app.supported_locales')),
If the package reads its own hardcoded list, override it in the published config rather than editing vendor files.
File-backed vs DB-backed workflows
lang/): pair every save with a cache flush. Wire the package's save event or an observer to php artisan cache:clear / your translation cache key, otherwise the Nova UI shows the new string while the front end serves the old one.translations table): this is the pattern that scales to per-string review workflows. Then decide explicitly whether Laravel reads from the DB at runtime (e.g. via a spatie/laravel-translation-loader-style driver) or whether a publish/export step syncs DB → files on deploy. Pick one and document it; mixed sources of truth is the most common source of "I edited it but nothing changed" tickets.Deploy integration
Bake the export/sync into CI so DB edits are reproducible:
php artisan nova-translations:export # or the package's equivalent command
php artisan view:clear && php artisan cache:clear
Commit the exported files (or run the export as part of the release step) so a rollback restores both code and strings together.
Permissions
Gate the tool behind a Nova policy or Gate::define rather than relying on it being "hidden" in the nav. Typical split: translators get view+update, developers get create/delete of keys. If the tool registers its own Nova Resource, override authorizedToViewAny() etc. in a subclass; if it's a pure Tool, restrict in Nova::auth() or a middleware.
Coexistence with php artisan lang:publish and vendor translations
Decide up front whether translators can see vendor-published groups (validation.php, auth.php, package strings). Most teams scope the tool to application-owned groups only, then handle framework strings via a separate PR-driven process — otherwise a well-meaning edit to validation.php breaks package upgrades.
git diff v1.0.2 v1.0.3 -- vendor/ferdiunal/nova-translations (or browse the compare page) before bumping; patch version doesn't guarantee patch-only changes in a pre-2.0 package.dist/. If the tool renders blank after an upgrade, run php artisan nova:publish and hard-refresh; stale published assets are the usual culprit.lang/. This works locally and fails on hardened production images. Prefer making translation edits a deploy-time or DB-backed operation on production.php artisan config:clear), and any OPcache on a long-running PHP-FPM/Octane worker. With Octane, file changes require a worker reload, not just a cache clear.messages.welcome.title) and JSON keys with dots collide easily; test keys containing . and : before letting translators loose. Deletions are usually harder to recover than edits — keep a git diff or DB audit trail.:name, :count, or ICU plural forms survive translation. Add a lint step in CI (a small script comparing placeholder sets against the source locale) so a dropped :count doesn't reach production.vendor/. If you need per-team or per-tenant scoping, wrap the package in your own Tool that reuses its controller endpoints rather than forking.How can I help you explore Laravel packages today?