financeua/i18n-routing-bundle
Internationalized routing bundle for Laravel/PHP apps, adding locale-aware route generation and URL handling so your application can serve translated, language-prefixed routes with minimal configuration.
Read this first: it is not a Laravel package. financeua/i18n-routing-bundle is a fork of the long-abandoned BeSimpleI18nRoutingBundle — a Symfony bundle wired into Symfony's Routing, HttpKernel, and DI container. There is no service provider, no facade, no RouteServiceProvider hook, and nothing that registers with Illuminate\Routing. composer require-ing it into a Laravel app buys you colliding Symfony components, 0 stars, 0 dependents, and no maintainer. You cannot use it day-to-day in Laravel.
What you can take from it is the pattern: one logical route name, many locale-specific path segments (/en/products, /fr/produits, /de/produkte), resolved per active locale so route('products.index') "just works."
Where to look first (as a port reference only):
Routing/Loader/ — where per-locale path maps are parsed into route definitions. This is the actual idea worth stealing.DependencyInjection/ — the i18n_routing config tree (locales, default_locale, redirect_to_host, use_route_name). Match these keys to your own config/localized-routes.php.First real use case, in Laravel: "I have /en/products and /fr/produits, both named products.index, and a locale switcher must generate the correct URL." Solve it with a maintained package, not this one:
composer require mcamara/laravel-localization # prefix-first, 90% case
composer require codezero/laravel-localized-routes # translated slugs
If your need is only /{locale}/... with untranslated slugs, skip packages entirely — a native route group with {locale} and a whereIn constraint is a one-day build.
The bundle's concepts, mapped to workflows you'd actually ship in Laravel.
1. Locale-prefixed groups — the default pattern. Mirrors the bundle's prefix-per-locale behavior with mcamara/laravel-localization:
Route::group([
'prefix' => LaravelLocalization::setLocale(),
'middleware' => ['localeSessionRedirect', 'localizationRedirect', 'localeViewPath'],
], function () {
Route::get('products', [ProductController::class, 'index'])->name('products.index');
});
Route names stay singular; route('products.index') emits the active locale's prefix. This is the invariant the Symfony bundle enforces through its loader — in Laravel you get it from the prefix + middleware stack.
2. Translated path segments — the bundle's actual feature. Use codezero/laravel-localized-routes, or a hand-rolled slug map:
Route::localized(function () {
Route::get(Lang::uri('products'), [ProductController::class, 'index'])
->name('products.index');
});
Back it with a single source of truth:
// config/localized-routes.php
'supported_locales' => ['en', 'fr', 'de'],
'routes' => [
'products.index' => ['en' => 'products', 'fr' => 'produits', 'de' => 'produkte'],
],
Centralizing this map is what keeps hreflang, canonicals, and the locale switcher from drifting.
3. Locale switcher. Generate the counterpart URL from the same route name and current params, not from string surgery:
@foreach (config('app.supported_locales') as $locale)
<a href="{{ localized_route('products.index', $product, $locale) }}">{{ $locale }}</a>
@endforeach
4. Middleware order matters. Put locale detection (localeSessionRedirect, localizationRedirect) before auth/web middleware that might redirect, and make the default-locale redirect a 301 — not a 302 — so you don't churn SEO.
5. hreflang + canonical. Emit from the route map, one entry per locale, plus x-default. Drive both from the same config that drives Lang::uri(), so a new locale is one config edit.
RequestContext, RouterInterface, and UrlMatcher/UrlGenerator contracts don't exist in Laravel. Any adapter you build becomes a first-party component you own and must re-test on every Laravel upgrade. The port is almost always cheaper than the bridge.php artisan route:cache and any config-driven slug map go stale after locale additions. Rebuild caches in deploy when config/localized-routes.php or translations change; otherwise you ship 404s for a language that "exists."route() generation. This is the failure mode everyone hits: a route registered under one locale's prefix and resolved while another locale is active. Keep prefixes and names 1:1, and if a name isn't localized, ensure it has no prefix or you'll double-prefix (/fr/en/products)./ → /{default_locale}/ 301 produces duplicate content and two ways in to every page. Also decide whether the prefix is stripped or required for the default locale — changing that later is an SEO-visible break.products.index.en), route() resolution order becomes load-bearing and brittle. Prefer one name + locale-aware generation.locales, default_locale, use_route_name, redirect_to_host) are a decent spec for your own config file — borrow the shape, write the implementation; it's ~a day of work and you own the tests.app()->getLocale(), the matched route name (Route::currentRouteName()), and the generated URL side by side. Nearly every i18n routing bug is one of those three disagreeing.How can I help you explore Laravel packages today?