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

Translatable Laravel Package

fsi/translatable

FSI Translatable adds simple multilingual support to Laravel Eloquent models. Store translated attributes, switch locales, use fallbacks, and retrieve localized values with minimal configuration and clean, familiar model syntax.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. fsi/translatable is a model-layer extension: it plugs into an ORM and adds locale-aware accessors/mutators plus a storage strategy for per-locale attribute values (sibling translations table or JSON column, depending on schema). That makes it a narrow, low-coupling dependency — it touches model classes, not HTTP, queues, or the container. For a Laravel app whose only localization need is "content fields stored in multiple languages," the architectural surface area is small and reversible, which is the main thing working in its favor. It is not a full i18n stack (no route localization, no UI string catalogs, no translation-management workflow), so scope it as a content-model concern only.

This release (1.2.2) does not change that assessment, but it does sharpen one assumption. The only change is widening doctrine/doctrine-bundle to ^3.0. Doctrine is the Symfony-side ORM; its presence in the dependency graph is a signal that this package's centre of gravity is Doctrine/Symfony, with Eloquent as a secondary target — not the reverse as our original framing implied. That does not disqualify it, but it reframes the maintenance bet: we would be the minority consumer, and Eloquent-specific gaps are the ones least likely to be caught by upstream CI. First action on any spike: read composer.json and determine whether doctrine/doctrine-bundle is a hard runtime requirement, a require-dev, or a suggest. A hard requirement in a Laravel app pulls an entire unused ORM bundle into the graph and is a legitimate reason to stop.

Integration feasibility. Mechanically straightforward: composer require, publish config/migrations, add a trait to the target models, adjust any code that reads translated attributes. The real feasibility question is not installation but query semantics — whether translated values can be filtered, sorted, and eager-loaded without falling back to in-PHP filtering. Secondary feasibility checks: Laravel/PHP version constraints, support for your DB engine (JSON path queries behave differently on MySQL 5.7 vs 8, Postgres, and SQLite), and interaction with API resources, Filament/Nova, Scout, and any caching layer.

Technical risk. The dominant risk remains provenance, not code quality. Zero stars, zero dependents, and a single-vendor maintainer means essentially no production evidence outside the author's own estate: no community answer corpus, no third-party bug reports to learn from, no security-disclosure norm, no signal that breaking changes will be communicated. One data point softens this slightly: PR #43 is authored by a contributor outside the fsi-open org, which is the first evidence of a second maintainer-shaped participant and of a review path that isn't purely internal. It is a single commit and does not constitute a support commitment — do not reprice the risk on it, but do note it as a positive trend to re-check at the next quarterly review.

The specific risk introduced by 1.2.2 is constraint widening without a corresponding CI signal in the notes. Accepting doctrine-bundle ^3.0 doubles the supported dependency matrix while the changelog shows no test-matrix change. That is low severity for a Laravel consumer (we likely don't resolve that package at all), but it does mean "1.2.2 is a safe patch" is an inference, not a fact — verify the resolver output in our lockfile rather than assuming. Treat the package as "code you are adopting," not "a product you are consuming," and price the cost of a fork into the decision.

Key questions to resolve before commit:

  1. Is doctrine/doctrine-bundle a runtime requirement or a dev/suggest dependency? If runtime, what does it drag in?
  2. Storage strategy: JSON column or translations table? What is the write amplification and index story per locale?
  3. Can translated attributes be used in WHERE/ORDER BY at the DB level, or only post-hydration? Are generated columns or functional indexes supported?
  4. Explicit fallback chain semantics — is a missing translation null, the fallback locale, or the key?
  5. Compatibility matrix (Laravel, PHP, DB drivers) and whether it is tested against the versions we run — and is the Eloquent path covered to the same depth as the Doctrine path?
  6. Test coverage and CI in the repo; are there characterization tests we can inherit?
  7. License, security policy, and maintainer's stated support commitment.
  8. Does it play correctly with $fillable, validation, model events, serialization/toArray, and queued jobs (locale context propagation)?

Integration Approach

Stack fit. Ideal for a Laravel monolith or modular monolith with Eloquent-backed CMS/content models and a small number of locales. It composes cleanly with Blade, Inertia, Livewire, or API-first stacks because it does not impose a rendering layer. It is a poor fit for services already standardized on spatie/laravel-translatable or astrotomic/laravel-translatable, or for content that is genuinely document-shaped (long-form, versioned, editorial workflow) where a dedicated CMS or a JSON-document store is the better boundary.

Migration path.

  • Greenfield: add the trait at model creation time. Cost is near zero; the only discipline required is a naming convention for translated attributes and a lint rule that no translated attribute is written without a locale.
  • Brownfield (from another translation package): do not rip-and-replace. Introduce a thin internal Translatable adapter interface, back the adapter with the existing package first, backfill the new storage schema with a one-shot artisan command, enable dual-write behind a config flag, and run a read-through comparison in staging (log any divergence between old and new reads). Cut reads over only after the divergence log is clean for a full content cycle. The trait contract (accessors, mutators, fallback behavior) is similar enough across packages that a swap is a one-file change if you own the interface.

Compatibility. Pin the package to an exact version in composer.json (not a caret range) — with a single-maintainer package, minor bumps should be reviewed, not absorbed, and 1.2.2 is a case in point: a patch release whose entire content is a dependency-constraint change. Review composer.lock diff, not just the changelog, when taking it. Validate against the specific DB engine and version in each environment; JSON path query behavior is the most common environment-specific break. Verify against IDE helper generation, static analysis (PHPStan/Larastan), and any model-reflection tooling.

Sequencing.

  1. Read composer.json for the Doctrine dependency classification and the Laravel/Eloquent version constraints — a 30-minute gate before any spike is funded.
  2. Timeboxed spike (1–2 days) on one low-risk model in a scratch branch, including the awkward queries: filter by translated value, sort, eager-load, serialize.
  3. Write characterization tests for accessors, fallbacks, eager loading, and query scopes before adoption, so the package's behavior is pinned by your test suite rather than by its README.
  4. Wrap the trait behind an internal adapter so a future swap is a single-file change.
  5. Introduce the fork-and-patch pipeline (private VCS path, documented patch set) at the same time as adoption, not later.
  6. Roll out to one production model behind a flag; then expand by model, not by feature.

Operational Impact

Maintenance. Budget for ownership. With no dependents and no external contributors of record, expect to apply security and framework-compatibility patches yourself. Keep the vendored diff as small as possible and record every patch with a rationale and an upstream PR link, so that if the maintainer merges or abandons, you can reconcile quickly. Dependency-constraint releases like 1.2.2 are cheap to skip and cheap to take; the discipline that matters is a quarterly review of upstream activity, Laravel major-version readiness, and dependency freshness — and, newly, whether the Eloquent path is still receiving attention relative to the Doctrine path.

Support. Effectively self-support. There is no StackOverflow corpus, no blog-post trail, and no third-party bug reports to search when something breaks. Internal documentation — a runbook covering schema, fallback rules, and the patch process — is not optional; it is the actual support model. Escalation path is the maintainer's GitHub issues, which should be treated as best-effort. The external contribution in #43 is a mild positive for responsiveness but is not an SLA.

Scaling. Two pressure points. First, N+1 on reads: translated attributes on collection endpoints require eager loading, and a missed with() turns a 10-row response into 30+ queries. Enforce with query-count assertions in tests. Second, write path: a side-table design writes one row per locale per save, so bulk imports and content syncs multiply row counts; a JSON-column design avoids that but makes indexed search harder. Cache rendered translated payloads at the edge or in Redis for read-heavy public pages, and keep translated-attribute lookups out of hot query paths where possible.

Failure modes.

  • Silent content gaps — missing translations resolving to null or empty string in production rather than the fallback locale. Detect with a completeness report and a CI check, not by trusting the accessor.
  • Locale context loss — queue workers, console commands, and scheduled jobs defaulting to app.locale rather than the user's or content's locale.
  • Environment-specific query failures — JSON path queries succeeding on MySQL 8 and failing on SQLite in CI, or vice versa.
  • Dependency-graph drift — a patch release silently widening a transitive constraint (as 1.2.2 does with doctrine-bundle) and shifting resolved versions elsewhere in the lockfile. Mitigated by exact pinning plus lockfile review.
  • Schema drift — package migrations updated upstream while your published copies are stale.
  • API shape changes — serialized output differing between translated and untranslated models, breaking consumers subtly.
  • Abandonment — no upstream release for a Laravel major version, leaving you pinned; more likely if the maintainers' attention sits on the Symfony/Doctrine side.

Ramp-up. For a competent Laravel developer the package itself is a few hours of learning; the trait-based API is conventional. The non-obvious cost is the fork-and-patch discipline and the fallback-semantics testing, which realistically adds 2–4 days of one-time setup (dependency gate, spike, characterization tests, adapter, fork pipeline) on top of integration. Recommendation: proceed only if the dependency gate and spike confirm DB-level filtering/sorting works and you are willing to own the fork; otherwise default to a higher-signal alternative and revisit if the package's maintenance footprint broadens. This release neither strengthens nor weakens that recommendation — it is a no-op for our decision, but it is a useful tell about where the project's priorities sit.

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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core