organi/translatables
Organi Translatables brings multilingual attribute support to Laravel Eloquent models. Attach a simple trait to store and retrieve translated values per locale, with locale fallbacks and straightforward setup for building multi-language applications.
Package category and architecture fit. organi/translatables sits in the crowded "translatable Eloquent attributes" niche alongside spatie/laravel-translatable, astrotomic/laravel-translatable, and dimsav/laravel-translatable. The architectural decision it encodes — whether translations live in a JSON/text column on the parent table, or in a side table keyed by (model_type, model_id, locale, attribute) — is the single most consequential thing to confirm before any design work. JSON-column storage is simpler to implement and read, but weak for locale-scoped indexing, partial-locale queries, and reporting; side-table storage is query-friendly but adds joins and write amplification. A TPM should not accept the package's README as the design; the storage model determines schema migrations, index budget, and API surface for years.
Integration feasibility. Mechanically, adoption is low-effort if the package is a standard Laravel service-provider package: composer require, optional config/migration publish, add a trait to models, done. The realistic cost is not installation but semantic alignment — how the trait resolves locale fallbacks, what happens on missing translations, whether accessors are magic ($model->name) or explicit ($model->translate('name', 'fr')), and whether whereTranslation()-style query scopes exist. These are the points where swapping implementations later becomes expensive.
Technical risk. The dominant risk signal here is metadata, not code: zero stars, zero dependents, zero score. That means no third-party production usage to borrow trust from, no ecosystem of issue reports revealing edge cases, and no evidence of maintenance cadence. For a package that touches every persisted user-facing string, that is a material supply-chain and bus-factor exposure. Secondary risks: unverified Laravel/PHP version constraints (a package without recent commits is typically pinned to older framework majors), no documented fallback semantics, and unclear interaction with toArray()/toJson() serialization, form-request validation, and Laravel's *_translations validation conventions.
Key questions to resolve before commitment.
composer.json constraints (laravel/framework, php, illuminate/*), and do they cover our target version?null, empty string, or silent locale fallback?Stack fit. This is a pure application-layer concern, so fit is high and blast radius is contained — provided our stack is a conventional Laravel app (Eloquent, Blade/Inertia/Livewire, standard migrations). It is not a fit for anything that needs locale-aware full-text search, read replicas with divergent collations, or a separate reporting warehouse reading the same tables, unless the storage model is confirmed to be side-table.
Migration path. Two distinct cases:
HasTranslations contract or a TranslatableRepository wrapper in app/ that delegates to the package. This keeps the package a swappable implementation detail and is the only cheap way to hedge the vendor risk.NOT NULL guarantees on the original columns typically have to be dropped. Budget for a reversible migration plus a dual-read window.Compatibility. Verify the Laravel major against the package's constraint and against the framework's current translatable-related conventions — Laravel has moved on request-level locale, lang/ publishing, and pluralization over recent majors. Also confirm PHP 8.x compatibility (typed properties, readonly, enums) since the trait will be composed into our own models; older packages frequently break on PHP 8.2+ dynamic-property deprecations.
Sequencing.
spatie).Maintenance. Assume we own this package. With zero dependents and no visible community, the probability that a Laravel major upgrade breaks it and nobody upstream fixes it is high. Practical mitigation: pin an exact version in composer.json (not a caret range), vendor or fork the source into our own namespace, and track it in our dependency register with an explicit "fork-and-own" owner. The real ongoing cost is not license fee but the recurring few hours per framework upgrade spent validating and patching.
Support. No commercial support, no visible maintainer SLA, no Stack Overflow footprint — meaning our team's institutional knowledge becomes the documentation. Require a short internal runbook at integration time: how translations are stored, how fallback resolves, how to add a locale, how to safely alter the schema. Without it, this becomes tribal knowledge concentrated in one engineer.
Scaling. Performance depends entirely on the storage model. JSON columns scale acceptably for read-by-primary-key with a single active locale, and poorly for "find all records missing a translation in locale X" style admin queries or locale-filtered list endpoints at volume. Side tables scale better for querying but multiply row counts by locale cardinality — worth sizing early, since a 2M-row content table at 6 locales becomes 12M translation rows. Also confirm it doesn't trigger N+1 accessor loads; eager-loading translated relations should be documented and enforced.
Failure modes to instrument.
fr rendering the en string to production users. Needs a monitoring hook, not just a config flag.Ramp-up. Engineer ramp on the package itself is short — a day for a mid-level Laravel developer. Ramp-up on the decision context is the real cost: why this package over spatie, what the exit plan is, and what conventions apply. That argues for choosing this only if it offers a concrete capability the established alternatives lack (e.g., a specific admin UI, a specific fallback model, or an in-house relationship) — otherwise the well-maintained alternative with actual dependents is the defensible TPM recommendation, and this package should be evaluated as a fallback rather than a default.
How can I help you explore Laravel packages today?