novius/laravel-translatable
A Laravel package that makes Eloquent models translatable using just two fields: locale and locale_parent_id. The trait adds translation relations, a translate() helper, getTranslation() lookup, and a withLocale() query scope.
Architecture fit
The package implements translation as sibling rows in the same table, distinguished by a locale column and linked back to an origin row via a self-referencing locale_parent_id. Schema::translatable() provisions both columns; the Translatable trait adds translations, translationsWithDeleted, translate(), getTranslation(), and a withLocale() scope, with translatableConfig() (locale whitelist, column renaming) and translateAttributes() (attribute mutation on copy) as the only customization seams.
From a product-architecture standpoint this is a narrow, opinionated primitive, not a translation platform. It is a fit when:
It is not a fit for field-level partial translation (translate title but not slug), many-to-many translation linkage (shared categories/tags), per-locale editorial workflow, or translation history. Those are re-architecture events, not configuration changes — worth stating explicitly in the decision record so the constraint is not rediscovered mid-sprint.
Integration feasibility
Low-to-moderate technical effort, three mechanical steps: migration macro → use Translatable on the model → wrap call sites. There are no service-container bindings, no config file beyond optional lang publishing, no queue, no external service, no dependency graph to reconcile.
Realistic effort framing for planning purposes:
The dominant integration cost is query discipline, not the package. Because every locale is a row in the same table, an unscoped Post::all() silently returns duplicate content. Every list view, count, sitemap, feed, and API response must be locale-filtered. This must be enforced as a team convention — ideally a base repository or global scope — or you ship an SEO and UX defect that surfaces in production rather than in tests. I would treat "locale scoping convention documented and linted" as a hard exit criterion for the pilot.
Technical risk
| Risk | Severity | Product implication |
|---|---|---|
| AGPL-3.0 license | High — gating | Network-use copyleft. A hosted/SaaS surface can trigger source-disclosure obligations for the combined work. This is a legal gate, not a caveat; resolve before any engineering time is spent. |
| Adoption signal: 0 stars, 0 dependents | High | No external user base means no community-discovered edge cases, no tutorials, no Stack Overflow corpus. We would be the first production user. |
| Single-vendor stewardship (Novius, an agency) | Medium-High | Bus factor of roughly one. No foundation, no co-maintainers, no published support policy. |
| Compatibility claims vs. release notes | Medium | README asserts Laravel 10.x–13.x / PHP 8.2–8.5, but release notes only evidence Laravel 11 support at 1.0.0. Verify the CI matrix against our actual Laravel/PHP pins before committing. |
| Single-table bloat | Medium | Row count = records × locales. 1M records × 12 locales = 12M rows, with no partitioning or pruning guidance. Validate with a production-shaped dataset. |
| No fallback logic | Medium | Missing-locale behavior is entirely ours to build. Inconsistent fallback across models is a recurring, user-visible bug class. |
| Soft-delete orphaning | Medium | Parent deletion does not cascade. translationsWithDeleted supports read paths but not lifecycle hygiene. |
| No audit/history | Low-Medium | No versioning. Regulatory or editorial-rollback needs require separate handling. |
Key questions to resolve before committing
spatie/laravel-translatable, astrotomic/laravel-translatable), or an in-house build.it see when no it record exists: 404, en fallback, or machine-translated stub? This is a product decision, and the package gives us nothing.Stack fit
Clean fit for a conventional Laravel application on the supported matrix: Eloquent-first, no new infrastructure, no runtime services. It composes naturally with existing Laravel patterns — policies for locale authorization, form requests for validation, API resources for output shaping — because the trait exposes plain relations and scopes rather than a bespoke abstraction. This is the package's strongest argument: the integration surface is small enough that a mid-level Laravel engineer can own it without specialist knowledge.
It is a poor fit if the platform is API-first for a separate CMS, runs multiple content stores, or needs translation as a shared service across applications. In those cases the correct move is a translation service or a field-level library, not this trait.
Migration path
For greenfield models: trivial — add the macro to the migration, add the trait, write locale-aware accessors.
For existing tables with content already in one language, the migration is a data operation, not a schema operation:
locale (default en or the current language) and nullable locale_parent_id; backfill existing rows with the default locale.locale_parent_id = null) or children of a canonical root. The latter complicates getTranslation() semantics.Compatibility
Verify three things in a spike, not a document review: (a) the CI matrix actually covers our pinned Laravel/PHP versions, (b) the migration macro's foreign key behaves correctly on our database engine (this was a defect fixed in 0.0.2 — read the macro, do not assume), and (c) interactions with SoftDeletes, upsert, and any existing global scopes. Also confirm the AGPL posture before the spike, not after.
Sequencing
withLocale() and translations.Keep the pilot reversible: no other domain should depend on translation tables until Gate 5 passes.
Maintenance
Ongoing maintenance is low in code volume, non-trivial in stewardship. The package surfaces little that can break, but with a bus factor of roughly one and no co-maintainers we must assume we own the upgrade path to future Laravel majors. Budget a small recurring allocation — call it 2–4 engineer-days per Laravel upgrade cycle — for dependency verification, plus a named owner. Pinning the version and vendoring a fork are both defensible; drifting on ^1.0 with no owner is not.
A second, easily missed maintenance load: every new model and every new listing endpoint is an opportunity to reintroduce the unscoped-query defect. That cost persists for the life of the feature, not the life of the integration.
Support
There is no support contract, no SLA, and effectively no community. Internal support must come from our own engineers reading the trait. Mitigations worth adopting on day one:
Scaling
Two scaling axes matter:
count() or aggregate that forgets to will be wrong in a direction stakeholders notice (inflated metrics, duplicate sitemap entries, wrong search result counts). Indexes on (locale, locale_parent_id) and on the parent foreign key are mandatory, not optional.Set an explicit ceiling during design ("we support up to N records × M locales on a single table") and treat breaching it as a re-architecture trigger rather than an incident.
Failure modes
| Failure mode | User-visible symptom | Mitigation |
|---|---|---|
| Unscoped query on a translatable model | Duplicate rows in lists, inflated counts, SEO penalties | Global scope or base repository; lint rule; code-review checklist |
| No fallback policy implemented | Broken page or blank fields for uncovered locales | Define policy before launch; centralize in one accessor |
| Parent soft-deleted, translations remain | Orphaned content reachable via search or direct URL | Lifecycle hooks; audit query for orphaned locale_parent_id |
| Locale added without content | Empty locale in the switcher | Gate the switcher on content existence, not config |
| Upstream stalls on a Laravel major | Forced version pin; blocked framework upgrade | Fork ownership; tracked upgrade budget |
| License obligation triggered post-launch | Legal exposure on the shipped product | Resolve at Gate 0, not retroactively |
Ramp-up
Genuinely low for engineers — the trait is a page of logic and the API is five methods. Expect half a day for a Laravel-competent engineer to be productive, plus the internal one-pager to absorb the query-discipline convention. The heavier ramp-up is on the non-engineering side: legal review of AGPL (unbounded, often weeks), product definition of the fallback policy (requires editorial stakeholder sign-off), and QA coverage for locale × content-state combinations, which multiplies test matrices in a way that surprises teams late.
TPM recommendation
Treat this as a pilot candidate, not a platform commitment. The engineering cost is small and the Laravel fit is genuinely good; the two disqualifiers are legal (AGPL-3.0) and stewardship (0 stars, 0 dependents, single-vendor). Both must be cleared before investment, and both are cheap to evaluate. If legal clears and a one-model pilot validates performance and query discipline, it buys real time-to-market for multilingual content. If either gate fails, the fallback is a well-adopted MIT alternative with a known migration path — decided in a day, not a quarter. Document the decision and its rationale either way.
How can I help you explore Laravel packages today?