larasofthu / localized-routes-plus
Advanced Laravel package for creating localized routes with subdomain support, country-specific routing, and automatic locale management. Supports URL prefixes, subdomains, and multi-country localization strategies.
Requires
- php: ^8.0
- illuminate/contracts: ^9.0||^10.0||^11.0||^12.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^2.9||^3.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.1.1||^7.10.0||^6.4
- orchestra/testbench: ^10.0.0||^9.0.0||^8.22.0||^7.0
- pestphp/pest: ^3.0||^2.0
- pestphp/pest-plugin-arch: ^3.0||^2.0
- pestphp/pest-plugin-laravel: ^3.0||^2.0
- phpstan/extension-installer: ^1.3||^2.0
- phpstan/phpstan-deprecation-rules: ^1.1||^2.0
- phpstan/phpstan-phpunit: ^1.3||^2.0
- spatie/laravel-ray: ^1.35
This package is auto-updated.
Last update: 2025-06-30 22:28:49 UTC
README
A powerful Laravel package for creating localized routes with advanced features including subdomain support, country-specific routing, and automatic locale management.
Features
🌍 Multiple Localization Strategies
- URL prefix-based localization (
/en/page
,/hu/page
) - Subdomain-based localization (
en.example.com
,hu.example.com
) - Country-specific routing (
/en-us/page
,/en-ca/page
)
🚀 Framework Integration
- Seamless Laravel Route integration
- Automatic middleware registration
- Resource route support
- Custom route model binding
⚙️ Flexible Configuration
- Whitelist/blacklist locales
- Configurable separators
- Multiple domains per locale
- Default locale customization
🔧 Developer-Friendly
- Intuitive API design
- Helper methods for URL generation
- Comprehensive testing
- Full IDE support
Installation
Install the package via Composer:
composer require larasofthu/localized-routes-plus
Publish the configuration file:
php artisan vendor:publish --tag="localized-routes-plus-config"
Quick Start
Basic Usage
Create localized routes by chaining the localized()
method:
use Illuminate\Support\Facades\Route; // Creates routes for all configured locales Route::get('about', function () { return view('about'); })->name('about')->localized(); // Results in: // /about (default locale) // /hu/about (Hungarian) // /de/about (German)
Resource Routes
Route::resource('posts', PostController::class) ->names('posts') ->localized(); // Creates localized versions of all resource routes: // en.posts.index, en.posts.create, en.posts.store, etc. // hu.posts.index, hu.posts.create, hu.posts.store, etc.
Configuration
The configuration file (config/localized-routes-plus.php
) contains all available options:
return [ // Available locales 'locales' => ['en', 'hu', 'de'], // Default locale 'default_locale' => 'en', // Include prefix for default locale in URLs 'use_route_prefix_in_default_locale' => false, // Subdomain configuration 'use_subdomains_instead_of_prefixes' => false, 'domains' => [ 'en' => 'example.com', 'hu' => 'example.hu', 'de' => 'de.example.com', ], // Country-specific routing 'use_countries' => false, 'country_path_separator' => 'dash', // 'dash' or 'slash' 'countries' => [ 'en' => 'us', 'hu' => 'hu', 'de' => 'de', ], ];
URL Prefix Localization
Basic Configuration
// config/localized-routes-plus.php 'locales' => ['en', 'hu', 'de'], 'default_locale' => 'en', 'use_route_prefix_in_default_locale' => false,
Route Examples
Route::get('products', [ProductController::class, 'index']) ->name('products.index') ->localized(); // Generated routes: // GET /products -> en.products.index (default locale, no prefix) // GET /hu/products -> hu.products.index // GET /de/products -> de.products.index
Including Default Locale in URLs
// config/localized-routes-plus.php 'use_route_prefix_in_default_locale' => true, // Results in: // GET /en/products -> en.products.index // GET /hu/products -> hu.products.index // GET /de/products -> de.products.index
Subdomain Localization
Configuration
// config/localized-routes-plus.php 'use_subdomains_instead_of_prefixes' => true, 'domains' => [ 'en' => 'example.com', 'hu' => 'example.hu', 'de' => 'de.example.com', ],
Multiple Domains per Locale
'domains' => [ 'en' => 'example.com', 'hu' => 'example.hu', 'de' => [ 'de.example.com', 'de2.example.com', 'example.de' ], ],
Route Examples
Route::get('products', [ProductController::class, 'index']) ->name('products.index') ->localized(); // Generated routes: // example.com/products -> en.products.index // example.hu/products -> hu.products.index // de.example.com/products -> de.products.index // de2.example.com/products -> de.products.index (additional domain) // example.de/products -> de.products.index (additional domain)
Country-Specific Routing
Configuration
// config/localized-routes-plus.php 'use_countries' => true, 'country_path_separator' => 'dash', 'countries' => [ 'en' => 'us', 'hu' => 'hu', 'de' => 'de', ],
Multiple Countries per Locale
'countries' => [ 'en' => ['us', 'ca', 'gb'], 'hu' => 'hu', 'de' => 'de', ],
Path Separators
// Dash separator (default) 'country_path_separator' => 'dash', // Results in: /en-us/products, /en-ca/products // Slash separator 'country_path_separator' => 'slash', // Results in: /en/us/products, /en/ca/products
Route Examples
Route::get('products', [ProductController::class, 'index']) ->name('products.index') ->localized(); // With dash separator: // GET /en-us/products -> en-us.products.index // GET /en-ca/products -> en-ca.products.index
Selective Localization
Whitelist Specific Locales
// Only create routes for English and Hungarian Route::get('admin', [AdminController::class, 'index']) ->name('admin.index') ->localized(['en', 'hu']); // Single locale Route::get('terms', function () { return view('terms'); })->name('terms')->localized('en');
Blacklist Specific Locales
// Create routes for all locales except German Route::get('news', [NewsController::class, 'index']) ->name('news.index') ->localizedExcept('de'); // Multiple locales Route::get('blog', [BlogController::class, 'index']) ->name('blog.index') ->localizedExcept(['de', 'fr']);
Advanced Usage
Route Model Binding
Route::get('posts/{post}', [PostController::class, 'show']) ->name('posts.show') ->localized(); // Works with model binding: // /posts/my-post-slug -> en.posts.show // /hu/posts/my-post-slug -> hu.posts.show
Route Parameters
Route::get('categories/{category}/products/{product}', [ProductController::class, 'show']) ->name('products.show') ->localized(); // Generated routes handle parameters correctly: // /categories/electronics/products/laptop -> en.products.show // /hu/categories/electronics/products/laptop -> hu.products.show
Middleware Integration
The package automatically registers middleware to set the application locale:
// Automatically applied to localized routes SetLocaleFromRoute::class // Sets App::setLocale() SetCountryFromRoute::class // Sets App::setCountry() (when countries enabled)
Helper Methods
Route Switching
// In your views or controllers $currentRoute = request()->route(); // Get route for different locale $germanRoute = $currentRoute->locale('de'); $germanUrl = $germanRoute->getUrl(); // With countries $usRoute = $currentRoute->locale('en', 'us'); $canadaRoute = $currentRoute->locale('en', 'ca');
URL Generation
// Standard Laravel route() helper works $url = route('products.index'); // Current locale $hungarianUrl = route('hu.products.index'); // Specific locale // With countries $usUrl = route('en-us.products.index'); $canadaUrl = route('en-ca.products.index'); // Using helper methods $route = request()->route(); $germanUrl = $route->getUrl('de'); $usUrl = $route->getUrl('en', 'us'); // With country
Route Information
$route = request()->route(); // Get locale and country $locale = $route->getLocale(); // 'en' $country = $route->getCountry(); // 'us' (if countries enabled) // Get route name without locale prefix $safeName = $route->getSafeName(); // 'products.index' // Check if route matches a name if ($route->is('products.index')) { // Current route is products.index (any locale) }
Blade Helpers
Language Switcher
{{-- Basic language switcher --}} <div class="language-switcher"> @foreach(config('localized-routes-plus.locales') as $locale) @if($locale !== app()->getLocale()) <a href="{{ request()->route()->getUrl($locale) }}"> {{ strtoupper($locale) }} </a> @endif @endforeach </div> {{-- With countries --}} <div class="country-switcher"> <a href="{{ request()->route()->getUrl('en', 'us') }}">🇺🇸 US</a> <a href="{{ request()->route()->getUrl('en', 'ca') }}">🇨🇦 Canada</a> <a href="{{ request()->route()->getUrl('en', 'gb') }}">🇬🇧 UK</a> </div>
Current Locale Detection
{{-- Check current locale --}} @if(app()->getLocale() === 'hu') <p>Hungarian content</p> @endif {{-- Check if route is localized --}} @if(request()->route() instanceof \LarasoftHU\LocalizedRoutesPlus\LocalizedRoute) <p>This is a localized route</p> @endif
Testing
The package includes comprehensive tests covering all functionality:
# Run all tests composer test # Run specific test suites ./vendor/bin/pest tests/Feature/LocalizedRoutesTest.php ./vendor/bin/pest tests/Feature/CountryRoutesTest.php ./vendor/bin/pest tests/Feature/DomainRoutesTest.php # Run with coverage composer test-coverage
API Reference
LocalizedRoute Methods
Method | Description | Example |
---|---|---|
localized($locales = []) |
Create localized versions | ->localized(['en', 'hu']) |
localizedExcept($locales = []) |
Exclude specific locales | ->localizedExcept('de') |
locale($locale, $country = null) |
Get route for locale | $route->locale('hu') |
getUrl($locale = null, $country = null) |
Generate URL for locale | $route->getUrl('de') |
getLocale() |
Get route locale | $route->getLocale() |
getCountry() |
Get route country | $route->getCountry() |
getSafeName() |
Get name without locale prefix | $route->getSafeName() |
is($name) |
Check route name (locale-agnostic) | $route->is('products.index') |
Configuration Options
Option | Type | Default | Description |
---|---|---|---|
locales |
array |
['en'] |
Available locales |
default_locale |
string |
'en' |
Default application locale |
use_route_prefix_in_default_locale |
bool |
false |
Include prefix for default locale |
use_subdomains_instead_of_prefixes |
bool |
false |
Use subdomains instead of prefixes |
domains |
array |
[] |
Domain mapping for locales |
use_countries |
bool |
false |
Enable country-specific routing |
country_path_separator |
string |
'dash' |
Separator between locale and country |
countries |
array |
[] |
Country mapping for locales |
Middleware
Class | Description | Auto-Applied |
---|---|---|
SetLocaleFromRoute |
Sets App::setLocale() from route |
✅ Yes |
SetCountryFromRoute |
Sets App::setCountry() from route |
✅ When countries enabled |
Examples
E-commerce Site
// Product routes with localization Route::prefix('shop')->group(function () { Route::get('/', [ShopController::class, 'index']) ->name('shop.index') ->localized(); Route::get('categories/{category}', [CategoryController::class, 'show']) ->name('shop.categories.show') ->localized(); Route::resource('products', ProductController::class) ->only(['index', 'show']) ->names('shop.products') ->localized(); }); // Admin routes (English only) Route::prefix('admin')->group(function () { Route::resource('products', AdminProductController::class) ->names('admin.products') ->localized('en'); });
Multi-Country Site
// config/localized-routes-plus.php 'use_countries' => true, 'countries' => [ 'en' => ['us', 'ca', 'gb', 'au'], 'es' => ['es', 'mx', 'ar'], 'fr' => ['fr', 'ca'], ], // Routes Route::get('pricing', [PricingController::class, 'index']) ->name('pricing') ->localized(); // Results in routes like: // /en-us/pricing, /en-ca/pricing, /en-gb/pricing, /en-au/pricing // /es-es/pricing, /es-mx/pricing, /es-ar/pricing // /fr-fr/pricing, /fr-ca/pricing
Subdomain Setup
// config/localized-routes-plus.php 'use_subdomains_instead_of_prefixes' => true, 'domains' => [ 'en' => 'example.com', 'de' => ['de.example.com', 'example.de'], 'fr' => 'fr.example.com', ], // All routes automatically work on their respective domains Route::get('/', [HomeController::class, 'index']) ->name('home') ->localized(); // example.com/ -> en.home // de.example.com/ -> de.home // example.de/ -> de.home (additional domain) // fr.example.com/ -> fr.home
Troubleshooting
Common Issues
Route names must be set before localized()
// ❌ Wrong Route::get('products', ProductController::class)->localized()->name('products'); // ✅ Correct Route::get('products', ProductController::class)->name('products')->localized();
Missing domain configuration for subdomains
// Make sure all locales have corresponding domains when using subdomains 'domains' => [ 'en' => 'example.com', 'hu' => 'example.hu', // Don't forget this! ],
Country parameter required when countries enabled
// ❌ Wrong (when use_countries is true) $route->locale('en'); // ✅ Correct $route->locale('en', 'us');
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for details.
Security
If you discover any security vulnerabilities, please email fulopkapasi@gmail.com instead of using the issue tracker.
Credits
License
The MIT License (MIT). Please see License File for more information.