enesthedev / translated-routes
Simple route translation for Laravel 11+. Just add ->translate() to your routes. Wildcard support, validation, export to JSON/JS/TS. Works with any locale management system.
Fund package maintenance!
Enes
Installs: 21
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 1
pkg:composer/enesthedev/translated-routes
Requires
- php: ^8.2
- illuminate/contracts: ^11.0||^12.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.8||^9.0
- orchestra/testbench: ^9.0.0||^10.0.0
- pestphp/pest: ^3.0||^4.0
- pestphp/pest-plugin-arch: ^3.0||^4.0
- pestphp/pest-plugin-laravel: ^3.0||^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
- spatie/laravel-ray: ^1.35
This package is auto-updated.
Last update: 2025-12-08 22:29:21 UTC
README
Simple, elegant route translation for Laravel 11+. Just add ->translate() to your routes.
Why This Package?
- Zero configuration - Works with Laravel's locale system
- Native feel - Uses
->translate()macro on routes - Cache-optimized - Built-in caching with configurable TTL
- Inertia-ready - Shares locale data automatically
- No magic - Simple translation lookup from
lang/{locale}/routes.php
Installation
composer require enesthedev/translated-routes
php artisan vendor:publish --tag="translated-routes-config"
php artisan translated-routes:install
Quick Start
1. Create Translation Files
You have two options for organizing your translation files:
Option A: Separate Files Per Locale (Recommended)
lang/en/routes.php
return [ 'about' => 'about-us', 'contact' => 'contact', 'blog' => 'blog/{slug}', 'products' => 'products/{category}/{id}', ];
lang/tr/routes.php
return [ 'about' => 'hakkimizda', 'contact' => 'iletisim', 'blog' => 'blog/{slug}', 'products' => 'urunler/{category}/{id}', ];
Option B: Single File with All Locales
lang/routes.php
return [ 'en' => [ 'about' => 'about-us', 'contact' => 'contact', 'blog' => 'blog/{slug}', 'products' => 'products/{category}/{id}', ], 'tr' => [ 'about' => 'hakkimizda', 'contact' => 'iletisim', 'blog' => 'blog/{slug}', 'products' => 'urunler/{category}/{id}', ], ];
Note: The package automatically detects which structure you're using. If
lang/routes.phpexists, it uses that. Otherwise, it looks forlang/{locale}/routes.php.
2. Use ->translate() on Your Routes
use Illuminate\Support\Facades\Route; // Single route translation Route::get('about', [PageController::class, 'about']) ->name('about') ->translate(); Route::get('contact', [PageController::class, 'contact']) ->name('contact') ->translate(); Route::get('blog/{slug}', [BlogController::class, 'show']) ->name('blog.show') ->translate(); // Group translation - Option 1: translateGroup helper Route::translateGroup(['middleware' => 'auth'], function () { Route::get('settings/profile', [ProfileController::class, 'edit'])->name('profile.edit'); Route::get('settings/password', [PasswordController::class, 'edit'])->name('password.edit'); }); // Group translation - Option 2: Individual translate calls Route::middleware('auth')->group(function () { Route::get('settings/profile', [ProfileController::class, 'edit']) ->name('profile.edit') ->translate(); Route::get('settings/password', [PasswordController::class, 'edit']) ->name('password.edit') ->translate(); });
3. Set Your App Locale
The package uses Laravel's App::getLocale(), so use any locale management package or middleware you prefer:
// In a middleware or wherever you set locale App::setLocale('tr');
4. Results
When App::getLocale() is 'en':
/about-us
/contact
/blog/hello-world
/products/electronics/123
When App::getLocale() is 'tr':
/hakkimizda
/iletisim
/blog/hello-world
/urunler/electronics/123
Parameters are automatically preserved!
Inertia.js Support
Backend
Add the middleware to share locale data:
Route::middleware(['web', 'share-inertia-locale'])->group(function () { Route::get('about', [PageController::class, 'about'])->translate(); });
Frontend (React/TypeScript)
types/index.d.ts
export interface LocaleData { code: string; name: string; native: string; active: boolean; } export interface PageProps { locale: { current: string; default: string; supported: Record<string, LocaleData>; }; }
LanguageSwitcher.tsx
import { usePage, router } from '@inertiajs/react'; import { PageProps } from '@/types'; export default function LanguageSwitcher() { const { locale } = usePage<PageProps>().props; const switchLocale = (code: string) => { // Use your preferred locale switching method router.post('/locale/switch', { locale: code }); }; return ( <div className="flex gap-2"> {Object.values(locale.supported).map((lang) => ( <button key={lang.code} onClick={() => switchLocale(lang.code)} className={lang.active ? 'font-bold' : ''} > {lang.native} </button> ))} </div> ); }
Current Locale Usage
const { locale } = usePage<PageProps>().props; console.log(locale.current); // 'en' or 'tr' console.log(locale.supported.tr.native); // 'Türkçe'
API
Route Macro
// Translate a single route Route::get('about', $action)->translate(); // Translate all routes in a group - Option 1 (Recommended) Route::translateGroup(['middleware' => 'auth'], function () { Route::get('settings/profile', [ProfileController::class, 'edit']); Route::get('settings/password', [PasswordController::class, 'edit']); }); // Translate all routes in a group - Option 2 Route::middleware('auth')->group(function () { Route::get('settings/profile', [ProfileController::class, 'edit'])->translate(); Route::get('settings/password', [PasswordController::class, 'edit'])->translate(); });
Facade
use Enes\TranslatedRoutes\Facades\TranslatedRoutes; // Get locale data for Inertia TranslatedRoutes::getLocaleData(); // Get supported locales TranslatedRoutes::getSupportedLocales(); // Clear cache TranslatedRoutes::clearCache(); TranslatedRoutes::clearCache('en');
Helper Function
// Remove locale from URL non_localized_url('/tr/about'); // Returns: '/about'
Configuration
// config/translated-routes.php return [ // Define your supported locales 'supported_locales' => [ 'en' => ['name' => 'English', 'native' => 'English'], 'tr' => ['name' => 'Turkish', 'native' => 'Türkçe'], ], // Cache settings 'cache_enabled' => env('TRANSLATED_ROUTES_CACHE', true), 'cache_ttl' => env('TRANSLATED_ROUTES_CACHE_TTL', 86400), ];
How It Works
- You define your routes with keys (e.g.,
'about','contact') - You add
->translate()to the route or group - The package looks up the translation in
lang/{App::getLocale()}/routes.php - Route parameters (
{slug},{id}) are automatically preserved - The translated route is set and cached
Locale Management
This package does not manage locale switching. Use any method you prefer:
Option 1: mcamara/laravel-localization
Route::group([ 'prefix' => LaravelLocalization::setLocale(), 'middleware' => ['localeSessionRedirect'] ], function () { Route::get('about', $action)->translate(); });
Option 2: Custom Middleware
class SetLocale { public function handle($request, $next) { if ($locale = $request->segment(1)) { if (in_array($locale, ['en', 'tr'])) { App::setLocale($locale); } } return $next($request); } }
Option 3: Session-based
Route::post('/locale/switch', function (Request $request) { session(['locale' => $request->locale]); App::setLocale($request->locale); return back(); }); // In a middleware App::setLocale(session('locale', 'en'));
Artisan Commands
# Create translation files php artisan translated-routes:install # Clear cache php artisan translated-routes:clear # All locales php artisan translated-routes:clear en # Specific locale # Validate translations php artisan translated-routes:validate # Check for missing/inconsistent translations # List translated routes php artisan translated-routes:list # All locales php artisan translated-routes:list --locale=en # Specific locale # Export translations php artisan translated-routes:export --format=json # Export to JSON php artisan translated-routes:export --format=js # Export to JavaScript php artisan translated-routes:export --format=ts # Export to TypeScript # Profile performance php artisan translated-routes:profile # Benchmark translation performance
Advanced Usage
Wildcard Translations
Support for dynamic route patterns:
// lang/en/routes.php return [ 'blog/*' => 'blog/*', 'user/*/profile' => 'user/*/profile', ]; // lang/tr/routes.php return [ 'blog/*' => 'blog/*', 'user/*/profile' => 'kullanici/*/profil', ];
This allows matching multiple routes with a single pattern, reducing repetition.
Translation Validation
Check your translations for consistency:
php artisan translated-routes:validate
Export for Frontend
Export translations for SPA/PWA applications:
php artisan translated-routes:export --format=ts
Then use in your TypeScript application:
import { getRoute, type RouteKey, type Locale } from '@/translations/routes'; const route = getRoute('about', 'tr'); // 'hakkimizda'
Named Routes with Parameters
Route::get('blog', [BlogController::class, 'show']) ->name('blog.show') ->translate(); // Generate URL route('blog.show', ['slug' => 'hello-world']); // Result: /blog/hello-world (en) or /blog/hello-world (tr)
API Routes
Route::prefix('api')->group(function () { Route::get('user', [UserController::class, 'show'])->translate(); });
Fallback
If a translation key doesn't exist, the original URI is used:
// lang/en/routes.php - 'missing-key' not defined Route::get('missing-key', $action)->translate(); // Result: /missing-key (uses original URI)
Performance
- First request: ~2ms (loads and caches translations)
- Cached requests: ~0.01ms (static memory cache)
- Cache TTL: Configurable (default 24 hours)
Real-World Example
// routes/web.php use Illuminate\Support\Facades\Route; use App\Http\Controllers\{HomeController, BlogController, ProductController}; Route::middleware(['web', 'share-inertia-locale'])->group(function () { // Home page Route::get('/', [HomeController::class, 'index'])->name('home'); // Translated routes Route::group([], function () { Route::get('about', [HomeController::class, 'about'])->name('about'); Route::get('contact', [HomeController::class, 'contact'])->name('contact'); Route::get('services', [HomeController::class, 'services'])->name('services'); Route::get('blog', [BlogController::class, 'index'])->name('blog.index'); Route::get('blog/{slug}', [BlogController::class, 'show'])->name('blog.show'); Route::get('products', [ProductController::class, 'index'])->name('products.index'); Route::get('products/{category}/{id}', [ProductController::class, 'show'])->name('products.show'); })->translate(); // Forms Route::post('contact', [HomeController::class, 'contactSubmit'])->name('contact.submit')->translate(); });
What This Package Does
✅ Translates route URIs based on App::getLocale()
✅ Preserves route parameters automatically
✅ Caches translations for performance
✅ Shares locale data with Inertia.js
✅ Provides non_localized_url() helper
What This Package Does NOT Do
❌ Locale detection/switching (use other packages)
❌ Content translation (use Laravel's trans())
❌ Session management (use Laravel's session)
❌ URL redirects (handle in your middleware)
Requirements
- PHP 8.4+
- Laravel 11.0+
Testing
composer test
Changelog
See CHANGELOG for more information.
License
MIT License. See License File for more information.