How to use a dynamic variable in Laravel group routes? - laravel

for example:
Route::group(['prefix' => '{lang?}'], function () {
//..
});
create a variable in middleware:
public function handle($request, Closure $next){
$lang = session('locale');
App::setLocale($lang);
return $next($request);
});
also tried to get the data in the prefix, but got null
Route::group(['prefix' => config('app.locale')], function () {
//..
});
or
Route::group(['prefix' => session('locale')], function () {
//..
});
change language separate route through session
Route::get('setlocale/{locale}', function ($locale) {
session(['locale' => $locale]);
return redirect()->back();
})->name('setlocale');
Thank you in advance for your help.

You don't need to group your routes under a lang route, you can create your own middleware and group your routes under that middleware, and use this code inside of it:
public function handle($request, Closure $next){
$lang = request->get('locale');
$currentLang = App::getLocale();
//If locale exists in the url and it's changed
if($lang && $lang != $currentLang) App::setLocale($lang);
//If locale doesn't exist in the url, fallback to default locale
if(!$lang) App::setLocale('en');
return $next($request);
});
The url should be data/store?locale=en, ur url should always append locale after ? in the url, otherwise, your default locale will be used
EDIT: Since your routes include locale as a part of the url, and not as a request param, then I suggest you use a library to handle this for you, because you will have a lot of stuff to do before you will be able to have a working example: Laravel localization mcmara

Related

Laravel localization middleware causing ERR_TOO_MANY_REDIRECTS

I am trying to set up localization in a laravel app but my middleware seems to be causing an ERR_TOO_MANY_REDIRECTS error. The site is going to differ per region slightly and I am using the laravel lang/ files to swap out phone numbers etc. This works okay. When I change my locale I get the correct numbers from the lang files. And when I just have the middleware to check if the cookie has been set to set the locale that works too.
I also have middleware to forget the prefix on localised routes so I don't have to update my methods with a locale parameter.
My issue is setting a url prefix if a cookie has been set with an allowed locale in my middleware. I also want to ignore the prefix if the locale is 'en' as it will be default.
Below is the code for everything
routes/web.php
Route::get('/', 'HomeController#index')->name('home');
Route::get('/reader/{identifier}', 'ReaderController#show')->name('reader');
Route::get('/locale/{locale}', SetLocaleController::class)->name('set.locale');
Route::group(['prefix' => '{locale?}', 'where' => ['locale' => implode('|', array_keys(config('app.allowed_locales')))], 'middleware' => 'redirect.localization'], function () {
Route::get('/', 'HomeController#index')->name('home');
Route::middleware('forget.prefix')->get('/reader/{identifier}', 'ReaderController#show')->name('reader');
});
Middleware/Localization.php - This is added to the web middleware group in Kernel.php. It checks for a cookie and sets the locale and should redirect to the proper locale prefix. If no cookie is set then we get the users geo and set it.
public function handle($request, Closure $next)
{
// Get requested url
$segments = collect($request->segments());
if ($segments->count() && Arr::exists(config('app.allowed_locales'), $segments[0])) {
$segments->shift();
}
$locale = Cookie::get('locale');
if ($locale ?? false) {
// Set the app locale.
App::setLocale($locale);
if ($locale != 'en') {
$segments->prepend($locale);
return redirect()->to($segments->implode('/'));
}
} else {
// Check for geo here and set it.
}
return $next($request);
}
Middleware/RedirectLocalization.php - This middleware is only set on the route group for the prefix. It checks if the locale passed is allowed and the sets the locale and cookie. This is set in the $routeMiddleware array in Kernel.php
public function handle($request, Closure $next)
{
$locale = $request->segment(1);
if (Arr::exists(config('app.allowed_locales'), $locale)) {
// Set the app locale.
App::setLocale($locale);
// Save app locale in a Cookie.
Cookie::queue(Cookie::make('locale', $locale, 525600));
}
return $next($request);
}
Controllers/SetLocaleController.php - This is where the locale can be manually set from a menu on the site.
public function __invoke($locale, Request $request)
{
$redirectUrl = parse_url(url()->previous());
$segments = Str::of($redirectUrl['path'])->trim('/')->explode('/');
if (Arr::exists(config('app.allowed_locales'), $segments[0])) {
$segments->shift();
}
if (Arr::exists(config('app.allowed_locales'), $locale)) {
// Set the app locale.
App::setLocale($locale);
// Save app locale in a Cookie.
Cookie::queue(Cookie::make('locale', $locale, 525600));
// Add locale to segments for redirect.
if ($locale != 'en') {
$segments->prepend($locale);
}
} else {
// Set locale to current locale.
App::setLocale(config('app.fallback_locale'));
}
// Redirect back
return redirect()->to($segments->implode('/'));
}
Controllers/ReaderController.php - Nothing out of the ordinary here but I wanted to add it to explain the forget.prefix middleware. If I don't add the forget.prefix middleware then the $reader param becomes the locale.
public function show($reader, Request $request)
{
$reader = Reader::find($reader);
return view('readers.show', [
'reader' => $reader
]);
}
Middleware/ForgetPrefix.php - This middleware removes the prefix so we can access parameter in controller methods without having to add a $locale param to the method in the controller.
public function handle($request, Closure $next)
{
$request->route()->forgetParameter('locale');
return $next($request);
}
So my question is how can I set the URL prefix if the locale has been set in a cookie without getting the too many redirects error?
So I found that my issue was coming from having the Middleware/Localization.php added to the web middleware group. I added this middleware to $routeMiddleware in Kernel.php. Instead I only added the Middleware/Localization.php to the default locale routes.
My updated web/routes.php
Route::group(['middleware' => 'localization'], function () {
Route::get('/', 'HomeController#index')->name('home');
Route::get('/reader/{identifier}', 'ReaderController#show')->name('reader');
});
Route::group(['prefix' => '{locale?}', 'where' => ['locale' => implode('|', array_keys(config('app.allowed_locales')))], 'middleware' => 'redirect.localization'], function () {
Route::get('/', 'HomeController#index')->name('home');
Route::group(['middleware' => 'forget.prefix'], function () {
Route::get('/reader/{identifier}', 'ReaderController#show')->name('reader');
});
});
// Switch Locale route
Route::get('/locale/{locale}', SetLocaleController::class)->name('set.locale');

laravel Localization per user

How do I change the language of each user? For example, some people don't change the language. Some people change the language.
Middleware :
use Closure;
use Auth;
class Localization
{
public function handle($request, Closure $next)
{
if(\Session::has('locale')) {
\App::setLocale(\Session::get('locale'));
}
return $next($request);
}
}
Save the locale for each user in database. This way you can override app's default locale to user's preferred locale in the middleware.
public function handle($request, Closure $next)
{
if($user = Auth::user()) {
App::setLocale($user->locale);
}
return $next($request);
}
If the your application doesn't require user to be authenticated, you can save locale in session for each user when the user change language.
Your method is right, no need to change the middleware. You can put the session on user with controller, like this below way.
Route :
Route::get('/lang',[
'uses' => 'HomeController#lang',
'as' => 'lang.index'
]);
Controller :
public function lang(Request $request)
{
$user = Auth::user()->id;
$locale = $request->language;
App::setLocale($locale);
session()->put('locale', $locale);
// if you want to save the locale on your user table, you can do it here
return redirect()->back();
}
Note : I added the GET method on route, so your URI will be like http://127.0.0.1:8000/lang?language=en, and o controller $request->language will catch the language parameter from your Request. You can modify and use POST method instead

how to send id in route and controller in laravel localization

In my Laravel project with localization I made middleware, route group and all parameters, language switch work correct but when I click to send id by
I get the error:
Missing required parameters for [Route: products] [URI:
{lang}/products/{id}]
My Routes:
Route::group(['prefix' => '{lang}'], function () {
Route::get('/', 'AppController#index')->name('home');
Route::get('/categories', 'AppController#categories')->name('categories');
Route::get('products/{id}', 'AppController#products')->name('products');
Auth::routes();
});
My Middleware:
public function handle($request, Closure $next)
{
\App::setLocale($request->lang);
return $next($request);
}
My AppController:
public function products($id)
{
$products = Category::with('products')->where('id', $id)->get();
return view('products', compact('products'));
}
this is the URL:
http://127.0.0.1:8000/fa/products/1
if I change the above URL manually it works and shows the page:
http://127.0.0.1:8000/1/products/1
But if I click on:
I receive the error.
Since you added a route prefix the first parameter of the products method in your controller will be lang and the second one id.
This should fix the controller:
public function products($lang, $id)
{
$products = Category::with('products')->where('id', $id)->get();
return view('products', compact('products', 'lang'));
}
You need to use a key-value array in route('products', ['lang'=>app()->getLocale(), 'id'=>$category->id]) or whatever your route parameters are named in the original route.
Ref. Laravel Named Routes
PS. as Remul notes, since you have a lang param (as route prefix) the first param in your controller will be $lang then $id
public function products($lang, $id)
{
$products = Category::with('products')->where('id', $id)->get();
return view('products', compact('products'));
}

Laravel redirect to correct path on missing routes

In my application when user enter this url as http://127.0.0.1:8000/showContent/aboutUs laravel show error as an
Sorry, the page you are looking for could not be found.
for that correct path is http://127.0.0.1:8000/en/showContent/aboutUs and I'm trying to manage that and adding missing segment for that for example:
Route:
Route::get('{lang?}/showContent/aboutUs', array('as' => 'lang', 'uses' => 'HomeController#showAboutUsPage'))->where('lang', '.+');
mapping routes:
protected function mapWebRoutes()
{
$locale = request()->segment(1);
if ($locale != 'fa' && $locale != 'en') {
$locale = 'fa';
}
app()->setLocale($locale);
Route::middleware('web')
->namespace($this->namespace)
->prefix($locale)
->group(base_path('routes/web.php'));
}
on mapWebRoutes function i'm using multi language and manage routes, and for missing parameters as language key on segment(1) I get error, now how can I add missing segment or manage routes to redirect to correct path?
sorry for duplicate comment!
it is related to your web server. you can use .htaccess file for that.
another way is to use exception handler. you can check the exception type and also the request route. then redirect to appropriate url if it is necessary.
My problem solved:
i change kernel.php to :
protected $middleware = [
\App\Http\Middleware\Language::class,
...
];
and Language class to:
public function handle($request, Closure $next)
{
$locale = $request->segment(1);
if (!array_key_exists($locale, config('app.locales'))) {
$segments = $request->segments();
array_unshift($segments,config('app.fallback_locale'));
return redirect(implode('/', $segments));
}
app()->setLocale($locale);
return $next($request);
}

Passing variables into middleware Laravel

I am using wildcard subdomains and want to pass the $element variable into the middleware whitelabel so I can check the subdomain and respond accordingly.
Route::group(['domain' => '{element}.website.co', 'middleware' => 'whitelabel'], function() {
Route::get('/', 'AuthController#getLogin');
Route::post('/', 'AuthController#postLogin');
});
How would I use the value of element within the middleware?
Firstly, (unless you already have done) you'll need to add the following to your:
Route::pattern('element', '[a-z0-9.]+');
You can add it to the boot() method of your AppServiceProvider.
Then to access it in your middleware you would have something like:
public function handle($request, Closure $next)
{
$domain = $request->route('element');
return $next($request);
}
Hope this helps!

Resources