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 Translatable Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

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:

  • Content is record-shaped — a translation is a full copy of the entity (post, page, product description), not a per-field overlay.
  • The business wants relational, queryable locale data (indexable, joinable, reportable) rather than JSON columns.
  • Locale scope is bounded (roughly <20 locales) and the editorial model is "author creates a translated copy."
  • No approval workflow, translation memory, or machine-translation integration is in scope.

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:

  • Pilot (one model, one read path, one write path): 1–3 engineer-days.
  • Production hardening (locale scoping across every listing/count/sitemap/search-index path, fallback policy, admin copy flow, soft-delete lifecycle): 2–4 engineer-weeks plus QA.

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

  1. Licensing — Is our distribution model AGPL-compatible? If SaaS, has legal ruled in writing? If not, the decision is a paid/negotiated fork, an MIT-licensed alternative (spatie/laravel-translatable, astrotomic/laravel-translatable), or an in-house build.
  2. Ownership — Who is the named internal owner of this dependency? Who reviews upstream PRs and absorbs a Laravel 14 upgrade if Novius does not?
  3. Semantics — Do we need record-level or field-level translation? Answer this first; it eliminates roughly half the candidate approaches.
  4. Fallback policy — What does a user in 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.
  5. Scale envelope — Expected record count × locale count × growth horizon. Does it fit in one table with indexing, or do we need partitioning we would own?
  6. Exit cost — If we adopt and later leave, what does migration look like? (Answer: a data migration plus call-site rewrite. Quantify it.)

Integration Approach

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:

  1. Add locale (default en or the current language) and nullable locale_parent_id; backfill existing rows with the default locale.
  2. Decide whether legacy rows are their own parents (locale_parent_id = null) or children of a canonical root. The latter complicates getTranslation() semantics.
  3. Introduce the trait and a locale-scoping convention before enabling a second locale, so the unscoped-query defect never reaches a release.
  4. Backfill only for pilot models first; resist a big-bang across every translatable table.

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

  1. Gate 0 — Legal. AGPL determination in writing. No engineering time before this clears.
  2. Gate 1 — Semantic fit. Confirm record-level translation suffices for the target domain.
  3. Spike (2–3 days). One model, production-shaped dataset, measure query plans for withLocale() and translations.
  4. Pilot (1–2 sprints). One domain (e.g. blog posts or product descriptions), locale-scoping convention enforced, fallback policy implemented and documented.
  5. Review. Compare actual effort and defect rate against the estimate; decide broaden/extend/abandon.
  6. Broaden. Only then replicate to additional models, with the convention already codified.

Keep the pilot reversible: no other domain should depend on translation tables until Gate 5 passes.

Operational Impact

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:

  • Fork to our organization and maintain a patch branch (also a partial AGPL mitigation strategy, subject to legal guidance).
  • Write an internal one-pager: the trait's behavior, the locale-scoping convention, the fallback policy, and the "do not use for X" list.
  • Add a lint/static check or base repository that fails on unscoped queries against translatable models.

Scaling

Two scaling axes matter:

  • Read amplification. Row count multiplies by locale count. Every read path must filter, and any 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.
  • Write and lifecycle. Translation creation is a handful of inserts per record; not a throughput concern at typical CMS volumes. The real risk is table growth without pruning — thousands of records × a dozen locales is fine, millions × dozens needs partitioning, archival, or a separate table strategy that the package does not offer.

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.

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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings