Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Helpermodel Translatable Laravel Package

esign/laravel-helpermodel-translatable

Adds translatable attributes to Eloquent models in Laravel, letting you store and retrieve per-locale values through a simple helper trait. Includes locale fallbacks, mass assignment support, and query scopes for multilingual content.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and scaffold its assets:

composer require esign/laravel-helpermodel-translatable
php artisan vendor:publish --provider="Esign\HelperModelTranslatable\HelperModelTranslatableServiceProvider"
php artisan migrate

Add the trait to any Eloquent model that needs localized attributes, then declare which columns are translatable:

use Esign\HelperModelTranslatable\HasTranslations;

class Product extends Model
{
    use HasTranslations;

    public array $translatable = ['name', 'description'];
}

Look first at the published config file (config/translatable.php) — this is where the default locale, fallback locale, and (in 2.0) database-driven fallback behaviour are wired together. Then check the published migration to understand the shape of the translations table before writing code against it.

The first use case to get working is a single model with two locales: an English name and a French name, with the French locale falling back to English when the French value is missing. Confirm this works before layering on per-model or per-record fallbacks — it establishes the resolution order you'll be debugging against later.

Implementation Patterns

Reading and writing translations. Assign translated attributes as if they were normal properties — the trait intercepts getAttribute/setAttribute and routes them through the translation store. Locale selection is per-request via App::setLocale() or explicitly via $product->translate('fr')->name. Stick to one approach per codebase; mixing them makes fallback behaviour hard to reason about.

Database-defined fallbacks (new in 2.0). The headline feature lets individual records define their own fallback chain rather than relying solely on the config's static fallback_locale. This fits content that isn't uniformly translated — e.g. a product whose descriptions exist in EN and DE, but whose DE version should fall back to EN, while another product falls back to FR. Store the fallback locale alongside the model (a fallback_locale column in the migration is the usual pattern for this package) and let the trait resolve it at read time. Use this for per-record overrides; keep the config fallback as the global safety net.

Form/CRUD workflows. For admin panels, render one input group per configured locale. On save, loop the submitted locales and persist each one — don't try to save all translations in a single mass-assignment array unless the package's API explicitly supports it, or you'll silently drop values. Wrap translation writes in a transaction when a model has multiple translatable attributes; partial saves leave orphaned rows.

Querying and eager loading. Translation lookups are separate queries unless you eager load. Use ->with('translations') on list endpoints, and prefer the package's locale-aware scopes over raw whereHas joins on the translations table — the scope knows about the fallback chain, a hand-rolled join does not.

Serialization. For APIs, decide explicitly whether to expose only the current locale, or a translations map. The trait exposes translated values through accessors, but the raw relation is available too; returning the raw relation by accident leaks all locales and inflates payloads. Use API Resources (not toArray) to control this.

Testing. Set the locale in setUp() and assert on resolved values rather than on the underlying translation rows — this keeps tests valid when you later add a DB-defined fallback in 2.0.

Gotchas and Tips

Read UPGRADING.md before touching 2.0. This is a major version bump and the release notes explicitly defer to UPGRADING.md rather than listing the breaking changes inline. Do not upgrade a production app without reading it — 2.0 changes how fallback resolution works, and any code that assumed the config's fallback_locale is the final word may now resolve differently. Grep your codebase for direct reads of fallback_locale and for any hardcoded fallback logic.

Fallback resolution order matters. The likely order is: requested locale → record's DB-defined fallback (2.0) → config fallback locale → raw value or null. When a translation "isn't showing up," walk the chain in that order before assuming the trait is broken. Log the resolved chain in a debug accessor if you're chasing a bug.

Empty string vs. missing translation. Most translatable packages treat '' and null differently — an empty string is a present translation and will short-circuit the fallback, while null or an absent row triggers it. If editors blank out a field expecting the fallback to kick in, they'll get an empty string instead. Normalise on save.

N+1 queries sneak in via accessors. Any collection loop that touches a translated attribute outside an eager load will fire a query per model. This is invisible in tests with one record and painful on an index page with 200. Watch the query log on list views.

Locale validation as an extension point. The trait doesn't know which locales your app supports — that's on you. Validate locale inputs against your configured list before writing, otherwise a typo creates a stray locale row that never resolves. The translatable config array is the natural single source of truth to validate against.

Config caching in production. php artisan config:cache will freeze the fallback configuration. If you're tempted to make per-environment fallback changes at runtime, use the 2.0 database-defined fallbacks instead of editing config — that's exactly the problem the feature solves.

Migrations from 1.x. If you're mid-upgrade, run the publish/migrate steps for 2.0 in a fresh test database first and diff the schema. Any new columns for the DB-defined fallback need to exist on your model tables and be populated for existing records, or the new fallback path silently no-ops.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums