yeswedev/nova-translatable
A Laravel Nova field for editing translatable model attributes across locales, integrating with spatie/laravel-translatable so you can manage multi-language content directly in Nova forms.
composer require yeswedev/nova-translatable. Before committing, open the package's composer.json and check its laravel/nova and spatie/laravel-translatable constraints — small packages often pin one Nova major version.use HasTranslations;, public $translatable = ['title', 'body'];, and the underlying columns must be JSON.php artisan vendor:publish --provider="YesWeDev\NovaTranslatable\..." (check vendor/yeswedev/nova-translatable/ for a service provider, config/, and resources/views/). If a config/nova-translatable.php exists, it's where the locale list and default locale live.src/ is the real documentation. Read its constructor and fluent methods to learn whether locales come from config or a ->locales([...]) call, and whether it's a wrapper around other Nova fields or a fixed text input. The README is short; the source is authoritative.Text::make('title') → the package's translatable field — and load the form. You should see one input per configured locale (tabs or stacked). If you see a single input, the locale list isn't resolving from config — fix that before wiring up more fields.$translatable attributes only; leave everything else untouched. If it's a wrapper, you can pass Text, Textarea, Trix, or Markdown into it; if it's a fixed input, that's a hard constraint to plan around.->locales([...]) on every field. One change updates every resource and prevents drift between forms.Text::make('Title', fn ($m) => $m->getTranslation('title', app()->getLocale())). Never let a listing dump raw translation JSON.title.en / title.fr against the actual request payload shape (verify this — it's the usual Nova array-field format but confirm).$fillable and $casts correct on the model and let Nova's normal fill write back. If the package calls setTranslations() directly instead, don't add conflicting casts or accessors.$translatable → swap fields on one resource → test save/load per locale → add index display → add per-locale validation → template to other resources.src/ end to end, don't assume fluent methods exist because a sibling package has them, and vendor-pin the version.jsonb, SQLite json. If the column is TEXT on an older MySQL, saves/loads can fail silently or round-trip incorrectly.getTranslation(..., $fallback) and whatever the display field does are separate code paths. Explicitly test the blank-locale case on detail/index, or you'll ship empty cells where a fallback was expected.->searchable() and ->sortable() against a translatable attribute need a generated/virtual column or a whereJsonContains scope. The package almost certainly doesn't solve this — don't assume it does.preserveActiveLocale-style option or publish/customize the view.->rules('required') is the classic trap. It applies to every locale input, so editors can't save unless all translations are filled. Wire per-locale rules on day one.app.locale often disagree, and the field's default tab may follow one while fallback display follows the other. Pick one, document it, and make the default tab match editorial expectation.src/) before bumping.How can I help you explore Laravel packages today?