Reputation: 417
I write a multilanguage application. I don't want to use locale prefix for the default language, so for example my URL structure should be like this:
https://example.com/about-us
https://example.com/contant
https://example.com/es/about-us
https://example.com/es/contant
https://example.com/de/about-us
https://example.com/de/contant
I use Route::group
to group my routes by locale
prefix:
Route::group([
'prefix' => '{locale}',
'where' => ['locale' => '[a-z]{2}'],
'middleware' => 'setlocale'
], function () {
Route::get('/{page}', [PageController::class, 'show'])->name('page');
});
in my middleware setlocale
I set locale and prefix for URLs:
public function handle(Request $request, Closure $next)
{
if (in_array($request->segment(1), ['es', 'de'])) {
URL::defaults(['locale' => $request->segment(1)]);
app()->setLocale($request->segment(1));
} else {
URL::defaults(['locale' => 'en']);
app()->setLocale('en');
}
return $next($request);
}
and then my controller prints the data:
class PageController extends Controller
{
public function show($lang, Page $page)
{
dd($page);
}
}
This works well with de
and es
prefixes, but not without them - for English lang. I tried to copy Route::get('/{page}', [PageController::class, 'show'])->name('page');
& paste it outside the Route::group
, but then just one argument is passed to PageController
and I get following error:
Too few arguments to function App\Http\Controllers\PageController::show(), 1 passed in /Users/mati/Sites/laravel-app/vendor/laravel/framework/src/Illuminate/Routing/Controller.php on line 54 and exactly 2 expected
Upvotes: 2
Views: 4884
Reputation: 533
This full solution:
https://example.com/
https://example.com/contact
https://example.com/en/
https://example.com/en/contact
file 'routes/web.php'
if(!function_exists('parseLocale')) {
function parseLocale()
{
$locale = request()->segment(1);
if (in_array($locale, ['js', 'css'])) // for speed up :)
return $locale;
if (array_key_exists($locale, config('languages'))) {
app()->setLocale($locale);
return $locale;
}
app()->setLocale('es'); // this default locale
return '/';
}
}
/** Here route */
Route::prefix(parseLocale())->group(function () {
Route::get('/', [App\Http\Controllers\PropertiesController::class, 'home']);
Route::get('/contact', function () {
return view('contact');
});
});
file 'config/languages.php'
<?php
return [
'en' => [
'display' => 'English',
],
'es' => [
'display' => 'Español',
],
];
files *.blade.php
<a href="{{ $init['lang'] }}">Home</a>
<a href="{{ $init['lang'] }}contact">Contact</a>
file 'app/Providers/AppServiceProvider.php'
public function boot()
{
View::composer('*', function ($view) {
$view->with('init', $this->loadTranslate());
});
}
private function loadTranslate()
{
$locale = app()->getLocale();
if ($locale == 'es') $lang = '';
else $lang = $locale . '/';
return [
'lang' => '/' . $lang,
];
}
Upvotes: 1
Reputation: 2683
You could do something like this.
Check that first slug is language prefix and is not default language then use it like prefix, else use nothing (null).
Route::prefix(parseLocale())->group(function () {
Route::get('/{page}', [PageController::class, 'show'])->name('page');
}
function parseLocale(){
$locale = request()->segment(1);
$locales = config('translatable.locales');
$default = config('app.fallback_locale');
if ($locale !== $default && in_array($locale, $locales)) {
app()->setLocale($locale);
return $locale;
}
}
In this example config('translatable.locales')
is array of locales.
Then drop useless $lang
param from controller:
class PageController extends Controller
{
public function show(Page $page)
{
dd($page);
}
}
Upvotes: 4