optimistdigital/nova-translatable
Nova Translatable brings multilingual editing to Laravel Nova, wrapping text, textarea, and other fields in locale tabs so you can manage translations for spatie/laravel-translatable models directly inside the Nova admin panel.
Architecture fit. Nova Translatable is a thin presentation-layer package: it renders a locale tab strip over Nova form fields and delegates all storage and fallback behavior to spatie/laravel-translatable. It does not create schema, register the trait, handle localized routing, or manage translation memory. That is the right fit for a Laravel + Nova application where translated attributes already live in JSON columns and the only gap is editor UX. It is a poor fit anywhere Nova isn't licensed, or where translations are stored in per-locale tables rather than JSON — in those cases this package adds nothing.
Integration feasibility. High, with a hard floor. Requirements: a licensed Nova install, spatie/laravel-translatable already wired with HasTranslations and $translatable, JSON columns on the model's table, and a Nova major that satisfies the package's version constraints. Integration is one wrapper in fields() — Translatable::make([...]) around the fields that need per-locale editing — plus a published locale config. Non-translatable fields stay outside the wrapper. The developer effort is measured in hours, not days, and the package replaces hand-rolled locale tabs that would otherwise be rebuilt and re-debugged per resource.
Technical risk. Concentrated, and mostly at the upgrade boundary rather than day one.
Repeatable support means the package now depends on Nova's Repeatable internals in addition to standard field rendering. This is the roadmap-enabling feature and simultaneously the least-proven surface. Treat nested Repeatable + Translatable as beta-grade until add/switch/fill/save/reload, partial-row translation, reordering, half-filled deletion, and per-row-per-locale validation errors have all been exercised by hand.->rules('required') fires per locale, blocking save until every configured locale has content. Teams discover this on day one and respond by typing "TODO" into four languages. Expressing "required in the default locale only" requires a custom rule in the NovaRequest; it is not achievable with sometimes on the inner field.null, and ''; Nova submits '' for untouched inputs, which can silently mask fallback resolution.sortable() and filterable() operate on JSON strings and are effectively unusable for translated attributes without materialized columns or accessor-backed fields.Image field yields one upload control and one file per locale.Key questions for the TPM to answer before commit.
required a hard gate across all locales, or required-nowhere plus a publish-readiness check? Who owns that decision?fields().)Stack fit. Laravel + Nova + spatie/laravel-translatable on JSON columns (json in MySQL, jsonb in Postgres). PHP 8-era toolchain. The package is a Nova field, so it sits inside the admin panel only — zero footprint on public request paths, no runtime middleware, no route or service-provider surface beyond config publication. Front-end asset builds (Node 24 support in 3.1.0) matter only if you compile vendor assets yourself; consuming the published build does not.
Migration path. Two distinct cases:
json; a varchar or per-locale column layout produces a silently broken or empty form. That means a data migration to JSON, adding $translatable to each model, and a per-resource pass to decide which fields are translatable at all. Budget the TPM's time here, not on the Composer install.Vendor rename. The package moved from optimistdigital/nova-translatable to outl1ne/nova-translatable. Use outl1ne for new installs. Do not churn a working composer.json just for the name — that's change without value and a needless regression opportunity.
Compatibility posture. Pin the exact package version. Check the Nova major against the tag's constraints before pulling, and specifically confirm that the Nova version in use actually ships Repeatable if you intend to use the 3.1.0 capability. Keep the published config under version control; locale lists are cached configuration and will bite at deploy time if config:cache runs after a locale change.
Sequencing (recommended, lowest-risk first).
json.$translatable entries, and cached config in one pass.NovaRequest. Name the owner.->locales([...]) where the global list is too broad; group fields into concept-scoped wrappers (content vs. SEO metadata) rather than one wall of tabs.Title (EN) accessors) so QA never has to guess which language is rendering.Maintenance. Small MIT-licensed codebase with an active release line (3.1.0, Repeatable support, dependency updates) — but a modest community: ~203 stars and a reported dependent count of zero on Packagist. That does not mean unsafe; it means you are the support tier. Pin the version, and keep the fork option genuinely open by confining usage to fields() in resources — no service-provider overrides, no internal patching. If upstream stalls, replacement becomes a mechanical edit across a handful of files rather than an archaeology project.
Support. Community GitHub issues only; no vendor SLA, no paid support path. Assign a named internal owner — one engineer accountable for the Nova-upgrade gate and the smoke-test resource. Without a named owner, the first Nova bump becomes an incident rather than a task.
Scaling. No application-runtime scaling concern; this is an admin form layer. The scaling constraints are editorial and combinatorial: locales × fields = form complexity, and editors degrade predictably as tab count grows. Mitigations are already in the package's grain — narrow locales per resource, split wrappers by concept, and approximate a per-editor locale context dynamically in fields() for small ops teams rather than building a resource-level locale filter. If admins need to sort or filter by translated values, plan a materialized column or accessor-backed sortable field; don't fight the JSON storage format.
Failure modes and their signatures.
| Symptom | Likely cause |
|---|---|
| Form renders empty or silently broken | Column is varchar, not json |
| Field appears as a plain input, no tabs | Attribute missing from $translatable |
| Locale missing from the switcher | Not in config/nova-translatable.php or the field's ->locales() |
| Locale list stale in staging/CI | Cached config; run config:clear |
| Save blocked on all languages | required firing per locale |
| Fallback refuses to resolve | Empty-string key masking null/absent |
| Disk usage climbing unexpectedly | Image/File wrapped → one asset per locale |
| Sort/filter produces nonsense | JSON column being sorted as a string |
| Field breaks after a Nova bump | Major version incompatibility |
Ramp-up. Developer learning curve is low for anyone already fluent in Nova and Spatie — the wrapper API is one constructor. Editor ramp-up needs a one-page guide covering the tab model and, critically, the required-locale policy, or you will get "TODO" in five languages. QA needs the explicit-locale column convention and the two-resource smoke script (one flat translatable field, one Repeatable-backed) run on every Nova upgrade.
TPM leverage summary. This package is the smallest credible increment for an "N languages in the admin" roadmap item: MIT license, near-zero acquisition cost, and a clear adopt-over-build call against hand-rolling locale tabs per resource. It also forces three product decisions that would otherwise surface in code review — required-locale policy, media localization model, and per-resource locale scope. Scope the first delivery to one resource and 2–3 locales; treat Repeatable-backed structured content as a separately-scoped, separately-tested follow-on; and treat the Nova upgrade gate as a standing operational commitment rather than a project task.
How can I help you explore Laravel packages today?