mcamara/laravel-localization
Easy i18n for Laravel: detect browser language, redirect with locale saved in session or cookie, define localized and translated routes once, hide the default locale in URLs, and use helpers like a language selector. Note: dynamic routes mean route:cache needs extra setup.
Positioning. mcamara/laravel-localization is a mature, narrowly-scoped routing/i18n helper for Laravel. It is not a translation-management system; it solves one specific problem — locale-prefixed URLs, locale detection, and locale-aware URL generation — with ~15 helper methods and 5 middleware aliases. The 3.5k stars and 10+ year Laravel version matrix (4.0 → 13.x) indicate real production usage, but the maintainer's own README note changes the strategic framing materially.
Architecture fit.
/en/..., /es/...) with server-rendered or Blade-driven marketing/content pages, and wants locale persisted in session or cookie.Route::group(['prefix' => LaravelLocalization::setLocale()], ...). This is an invasive structural assumption on routes/web.php, not a drop-in middleware.php artisan route:cache is unsupported out of the box. Every standard Laravel deploy checklist assumes route caching; this package forces a documented exception or a third-party workaround.Integration feasibility. High for a greenfield or content-led Laravel app; medium for a mature app with a large existing route file.
composer require, publish config, register aliases).supportedLocales, useAcceptLanguageHeader, hideDefaultLocaleInURL, localesOrder, localesMapping, utf8suffix, urlsIgnored).routes.* translation keys) are the highest-value feature and the highest-effort one: they force a discipline of named routes plus translation files, and getURLFromRouteNameTranslated() returns false on a missing key — a silent-failure pattern teams must test for.MethodNotAllowedHttpException, validation messages stuck in default locale). Each is a real defect class the team will hit during rollout, not an edge case.Technical risk.
| Risk | Severity | Notes |
|---|---|---|
Upstream declares a recommended successor (niels-numbers/laravel-localizer) with a migration guide |
High (strategic) | The current package is in explicit maintenance mode: "compatibility updates, security, small bug fixes." Any new build inherits a known-better replacement path. |
No native route:cache support |
High (operational) | Forces a fork of the standard deploy pipeline; also a per-request CPU cost at scale. |
| Middleware ordering fragility | Medium | localize / localizationRedirect / localeSessionRedirect / localeCookieRedirect / localeViewPath have ordering-dependent, mutually-referencing behavior; mis-ordering produces redirect loops or lost locale. |
| POST → GET on redirect | Medium | Any non-localized form action gets 302'd and the payload silently becomes a GET. Concrete data-loss/support-ticket class. |
| Behavioral coupling to session and cookies | Medium | Two competing persistence mechanisms with subtle precedence; QA matrix expands (fresh visitor, returning visitor, cookie-cleared, bot). |
Ecosystem signal Dependents: 0 |
Medium (data quality) | Inconsistent with 3.5k stars and a decade of adoption; treat as a scan artifact and re-verify on Packagist/GitHub before using it as evidence in a build-vs-buy decision. |
Last release: 2026-08-24 |
Low (data quality) | Future-dated relative to review; confirm the release timeline is current before citing "actively maintained" to stakeholders. |
Low opportunity: 34.55 against Score 61.6 |
Low–Medium | The weakest dimension in the scoring set; needs a qualitative read (likely reflects the maintenance-mode posture and the successor recommendation) rather than dismissal. |
Key questions to resolve before commitment.
route:cache support likely wins on lifecycle cost; for an existing app already on mcamara, staying is defensible.hreflang, canonical URLs, and per-locale sitemaps? Those are ours to build, not the package's.Stack fit.
2.0.x. Older combinations map to 1.8.x (Laravel 6–10) and below; the version matrix must be pinned explicitly in composer.json and in the platform ADR.localizeUrl() output from the backend rather than constructing paths.urlsIgnored and a separate route file) — locale-in-path for APIs invites caching and client-compatibility problems.Migration path.
$middlewareAliases for ≤10, bootstrap/app.php withMiddleware for 11+), wrap a 3–5 route slice, verify locale switching, redirects, and localizeUrl() output. Explicitly test a POST form to confirm the redirect behavior and document the mitigation.route:cache on the spike branch; record the failure mode and choose: (a) accept no route cache, (b) adopt a community workaround, or (c) escalate to the successor package. This gate should block wider rollout.localeSessionRedirect + localizationRedirect + localeViewPath to each group as it moves. Do not do a big-bang web.php rewrite.routes.* translation keys for the public-facing route names only. Add a test that asserts getURLFromRouteNameTranslated() is non-false for every supported locale × every key.hreflang alternates driven by getSupportedLocales()/getLocalesOrder(), per-locale sitemap, and a Vary: Accept-Language (or useAcceptLanguageHeader => false) decision on the CDN.Compatibility.
composer.json; the version matrix is the single source of truth and the package has a history of PHP-minimum bumps between majors.app/Http/Kernel.php.Sequencing (indicative).
| Phase | Duration | Exit criteria |
|---|---|---|
| Spike + caching gate | 1 wk | Locale switch, redirect, POST behavior, and route-cache decision documented |
| Core route migration | 2–3 wks | Localized routes live; localizeUrl() used on all internal links and form actions |
| Translated routes + tests | 2 wks | Zero false returns across locale × route-key matrix |
| SEO + sitemap + CDN config | 1 wk | hreflang validated; caching headers correct for locale variants |
| Hardening + runbook | 1 wk | Redirect-loop and fallback failure modes covered by tests and alerts |
Maintenance.
Support.
route:cache is disabled, why a form POST became a GET, and why a validation message is in the wrong language. Pre-write answers and link them from the PR template.Scaling.
hideDefaultLocaleInURL is true and the default locale shares a path. Cache-key design and Vary headers are prerequisites, not follow-ups.useAcceptLanguageHeader) makes the un-prefixed URL variant non-deterministic per visitor — correct with Vary: Accept-Language or disable detection and always redirect to an explicit prefix.Failure modes to instrument.
route:cache succeeds will break or be silently skipped; alert on the command's absence from CI.localizationRedirect and localeSessionRedirect when hideDefaultLocaleInURL and ordering interact badly — detect via 5xx/redirect-chain monitoring and a route-level integration test.localizeUrl().getURLFromRouteNameTranslated() returning false renders a broken link rather than throwing; cover with the locale × key test matrix.Accept-Language).Ramp-up.
localizeUrl(); the docs and named helpers carry most of the load.TPM recommendation. Proceed only with the caching gate resolved up front. For an existing Laravel app already on this package, staying is reasonable and cheap. For a new build, treat the maintainer's successor note as the decisive input and evaluate niels-numbers/laravel-localizer in the same spike — the feature set is described as equivalent, and native route:cache support removes the single largest operational liability documented here.
How can I help you explore Laravel packages today?