alessandrobelli/lingua
Lingua is a Laravel package that simplifies multilingual support and localization. It helps organize language files, manage translations, and streamline workflows so your application can serve users in multiple languages with less setup and cleaner configuration.
Architecture fit
Lingua's actual delivered scope, per the technical write-up, is narrower than its name suggests: it is a locale negotiation and application layer — a config file declaring supported locales and a fallback, a middleware that resolves a preferred locale (session and/or Accept-Language), applies it via App::setLocale(), plus Blade/helper conveniences. It sits cleanly on top of Laravel's native lang/ files rather than replacing them.
That is a good architectural fit shape — it's a thin, additive layer with no schema footprint and no changes to our data model. But the fit is also the problem: the surface area is small enough that the build-vs-buy delta is roughly one middleware and a config entry. We are not buying a translation platform; we are buying ~50–150 lines of glue.
Diligence flag: the stakeholder-facing summary provided describes Lingua as translating Eloquent model attributes (translated columns, a translations table, admin editing of per-locale fields). The technical assessment describes locale negotiation and UI-string switching. These are different products. Before we evaluate anything, we need a 30-minute verification of what the package actually does — this inconsistency means at least one of our source documents is unreliable, and it directly changes the value proposition.
Integration feasibility
Feasible and small. Setup is a composer require, a published config, middleware registration, and a locale-switch route. Realistic effort for a competent Laravel engineer: 1–2 days including a language switcher and tests. The hard parts are not the package — they are the design decisions around it: negotiation precedence (explicit choice → session → header → fallback), URL-localized routes vs. session state, and per-user persistence on the users table.
Technical risk
bootstrap/app.php middleware model. Whether the package's composer constraints even permit laravel/framework ^11 or ^12 is unverified — if not, we are pinned to an EOL-ish framework or forking.StartSession makes the switcher silently reset; response caching without locale in the key serves one language to everyone; missing Vary: Accept-Language cross-poisons CDNs; locale leaks across queue jobs and mailables. Each of these surfaces to stakeholders as "translation is broken," not as an integration defect.Accept-Language produces pt-BR, zh-Hans-CN, it-CH. Unmapped, these fail negotiation and fall to the fallback locale — a silent quality regression in our largest markets.Key questions
composer.json declare, and does it install cleanly on our target version?/it/pricing)? If so, the package doesn't solve that — we own the sync problem.config('app.fallback_locale')?Stack fit
Fits Laravel + Blade + Eloquent with no infrastructure implications — no service, no queue, no external API, no secrets. That is its main virtue: it is trivially removable. Nothing about our stack changes.
Migration path
Greenfield, so there is no migration in the data sense. The migration is organizational: moving from hardcoded English strings to a lang/{locale}/ corpus. That is the real cost — translation file authoring and ongoing maintenance for every locale in the supported list. Lingua negotiates a locale; it does not produce translations, and a successfully negotiated locale with missing files renders raw keys to users. Budget translation content separately and treat it as the dominant cost, not the package.
Recommended posture: wrap the integration behind our own thin middleware and a LocaleService-style boundary so a method rename or a swap to native Laravel (or a Spatie-class package) is a one-file change.
Compatibility
Verify before piloting: composer constraints against our Laravel/PHP versions, service-provider auto-discovery, and — critically — how middleware registration is documented. Any package from 2022 will document app/Http/Kernel.php, which does not exist in Laravel 11+. This is a documentation gap we will bridge ourselves, not a blocker.
Also confirm the config keys and middleware signature and write them down internally, since the empty changelog means we are the ones tracking the contract across versions.
Sequencing
POST /locale/{locale} switcher, session persistence, per-user column, tests for negotiation and persistence. Include one authenticated user flow and one queued mailable.Keep API routes out of session-backed detection entirely; if we want Accept-Language on API responses, it's a separate stateless registration with Vary documented.
Maintenance
Low ongoing engineering cost, but non-zero custodial cost that is easy to underestimate. Expected maintenance: periodic PHP/Laravel compatibility checks with no upstream support; config cache discipline after every locale change (config:cache — stale cached config is the #1 cause of "the new language doesn't exist"); translation file upkeep per locale. Pin to ^0.3 and treat 0.4 as breaking by default.
Support
There is none. Single maintainer, last release over two years ago, 2 stars. We are self-supporting: any bug is ours to fix or fork. Mitigant is that the codebase is small enough to read in an afternoon, which is the only reason this is acceptable at all. Escalation path must be "our own engineer reads the source," and that should be stated explicitly rather than assumed.
Scaling
Application-level only — no runtime scaling concern. Three scaling-adjacent items do need ownership before any traffic-bearing launch:
Vary: Accept-Language must be emitted wherever header-based detection influences a cacheable response.->locale($user->locale). This is the most likely silent production defect and needs a code-review checklist item, not a hope.Failure modes
Ordered by likelihood × user impact:
StartSession. High likelihood, high visibility, trivially fixed.it-CH, pt-BR) — no alias mapping; falls to fallback locale. Silent quality failure in exactly the markets we care about.lang/ files are incomplete. Prevents the fix: restrict the supported list to locales we actually translate, and gate additions on content delivery.tearDown().Ramp-up
Very low. A Laravel engineer is productive in under a day; setup is documented and the mental model is standard. The ramp-up cost is not learning Lingua, it is learning our locale policy — negotiation precedence, URL vs. session, region alias table, per-user persistence. Those decisions should be written down as an internal one-pager owned by the TPM, because they are the thing that will be re-litigated on every localization ticket if left implicit.
Net recommendation: Approve a bounded spike and one-sprint pilot on a non-critical, authenticated surface. Do not attach a market launch to this dependency. Revisit at 1.0, at the first breaking release, or if the scope-verification step reveals the package does less (or something different) than either source document claims.
How can I help you explore Laravel packages today?