Avoiding Multiple Routes for Different Languages - laravel

I'm translating my site into different languages. By the default, the language will be English, which I'm using the following route to return the "welcome" view:
Route::get('welcome', function ()
{
return view('welcome');
});
For my other languages, I'm using this other route:
Route::get('welcome/{locale}', function ($locale)
{
App::setLocale($locale);
return view('welcome');
});
Is there any way I can combine those two routes into one? For example, if the route is "welcome" or "welcome/en", return the "welcome" view in English, the default language.
However, if the route is "welcome/fr", the "welcome" view should be returned in French.
I'm going to have hundreds of routes so I would love being able to combine my routes.

The default language for your application is stored in the config/app.php configuration file. You may modify this value to suit the needs of your application. You may also change the active language at runtime using the setLocale method on the App facade:
Route::get('welcome/{locale}', function ($locale) {
if (! in_array($locale, ['en', 'es', 'fr'])) {
abort(400);
}
App::setLocale($locale);
//
});
You may configure a "fallback language", which will be used when the active language does not contain a given translation string. Like the default language, the fallback language is also configured in the config/app.php configuration file:
'fallback_locale' => 'en',
Occasionally you may need to specify a route parameter, but make the presence of that route parameter optional. You may do so by placing a ? mark after the parameter name. Make sure to give the route's corresponding variable a default value:
Route::get('welcome/{locale?}', function ($locale = null) {
//
});

Related

Laravel - Add Route Wildcard to every Route

I have created a multilanguage application in laravel and for every route (because i want to see in the url what my language is) i need
www.example.com/{locale}/home
for example, whereas {locale} is the set language and home well, is home. but for every route i need to declare that locale wildcard. is there any way to get this done with middleware or something, to add this before route is executed?
Thanks!
You can use prefix for it.
Route::group(['prefix' => '{locale}'], function () {
Route::get('home','Controller#method');
Route::get('otherurl','Controller#method');
});
And here how you can access it now.
www.example.com/{locale}/home
www.example.com/{locale}/otherurl
For more info.
https://laravel.com/docs/5.8/routing#route-group-prefixes
Not sure if I am understanding your request right, but I believe this is the scope you are looking for:
A generalized route which can receive the "locale" based on which you can serve the page in the appropriate language.
If that's the case, I would define a route like this:
Route::get({locale}/home, 'HomeController#index');
and then in your HomeController#index, you will have $locale variable based on which you can implement your language logic:
class HomeController extends Controller
{
/**
* Show the application homepage.
*
* #return mixed (View or Redirect)
*/
public function index(Request $request, $locale)
{
switch ($locale) {
case 'en':
//do english logic
break;
so on...
}
}
I hope it helps

Laravel: Best way to implement dynamic routing in routes.php based on environment variable?

My aim is to roll out a big re-theming / re-skinning (including new URL routing) for a Laravel v5 project without touching the existing business logic (as much as possible that is).
This is my current approach:
I placed a APP_SKIN=v2 entry in my .env file
My app\Http\routes.php file has been changed as follows:
if (env('APP_SKIN') === "v2") {
# Point to the v2 controllers
Route::get('/', 'v2\GeneralController#home' );
... all other v2 controllers here ...
} else {
# Point to the original controllers
Route::get('/', 'GeneralController#home' );
... all other controllers
}
All v2 controllers have been placed in app/Http/Controllers/v2 and namespaced accordingly
All v2 blade templates have been placed in resources/views/v2
the rest of the business logic remains exactly the same and shared between the "skins".
My question: Is there a "better" way to achieve the above?. Please note that the idea here is to affect as few files as possible when doing the migration, as well as ensure that the admin can simply change an environment variable and "roll back" to the previous skin if there are problems.
Within app/Providers/RouteServiceProvider.php you can define your routes and namespaces, etc. This is where I would put the logic you talked about (rather than in the routes file):
protected function mapWebRoutes()
{
if (App::env('APP_SKIN') === 'v2') {
Route::group([
'middleware' => 'web',
'namespace' => $this->namespace,
], function ($router) {
require base_path('routes/web_v2.php');
});
} else {
// ...
}
}
This way, you can create separate route files to make it a bit cleaner.
Aside from that, I personally can't see a better solution for your situation than what you described as I'm guessing your templates want to vary in the data that they provide, which if that is the case then you will need new controllers - otherwise you could set a variable in a middleware which is then retrieved by your current controllers which could then determine which views, css and js are included. This would mean you would only need to update your existing controllers, but depending upon your current code - this could mean doing just as much work as your current solution.
Routes pass through Middleware. Thus you can achieve this by BeforeMiddleware as follows
public function handle($request, Closure $next)
{
// Get path and append v2 if env is v2
$path = $request->path();
$page = $str = str_replace('', '', $path); // You can replace if neccesary
// Before middleware
if (env('APP_SKIN') === "v2")
{
return $next($request);
}
else
{
}
}

Laravel 5.1 optional route params in the middle of the URL

Is there any way I can use optional route params in the middle of the URL in Laravel 5. Here is what I want
Route::get('api/{locale?}/my-url', 'MyController#myAction');
You can't have optional route parameters in the middle of the route path, because they make the definition ambiguous if they are omitted, and the route won't be matched.
You could have two route definitions one with and one without (as you've suggested in your comment):
Route::get('api/{locale}/my-url', 'MyController#myAction');
Route::get('api//my-url', 'MyController#myAction');
But if you have lots of routes you'll have a lot of duplicates just for this.
You could just leave one definition with the locale, since there's no big deal of passing the default locale as part of the URL path. So if your default locale is en it just gets passed via the path as other locales:
http://example.com/api/en/my-url
However, since I'm guessing the locale is used for language appropriate responses and is only used for GET/HEAD requests, the best solution that I see here and it makes the most sense, is to just pass the locale as a parameter, because it's essentially an option:
http://example.com/api/my-url?locale=en
That way the Laravel route definition doesn't need to worry about it. Then you can use a middleware to change the locale if it is passed along in the query string. Here's an example of a middleware class that sets the locale and checks if it's an allowed locale:
namespace App\Http\Middleware;
use Closure;
class SetLocale
{
public function handle($request, Closure $next)
{
if ($request->has('locale') && $this->isValidLocale()) {
app()->setLocale($request->input('locale'));
}
return $next($request);
}
protected function isValidLocale()
{
return in_array(request()->input('locale'), ['en', 'es', 'fr', 'de']);
}
}
Now in your controller action you can just use:
app()->getLocale();
And it will be set to the value passed in the query string.
If I understand well and you want the url to work both as api/my-url and api/en/my-url you can simply setup two routes:
Route::get('api/{locale}/my-url', 'MyController#myAction');
Route::get('api/my-url', 'MyController#myAction');

changing url, and locale on the fly with laravel

My code in laravel to handle multiple language is:
$languages = array('it-IT','en-GB','fr-FR');
$lingua = Request::segment(1);
if(in_array($lingua, $languages)){
\App::setLocale($lingua);
}else{
$lingua = 'it-IT';
}
Route::group(array('prefix' => $lingua), function()
{
Route::get('/', array('as' => 'home', 'uses' => 'ItemController#menu'));
Route::get('/{idcampo}','ItemController#show');
});
How can i:
1)Make the page start always with it-IT as default. (i need it because I use $lingua to fetch from a database) so i can't have that null. Should I use a redirect::to / to /it-IT?
2) change url and language(app:locale) on he fly with a link in the upper section of every pages. withouth returning to the home.
3) to link pages I learn to use:
URL::route('home')
but how to do it when the link change with the entry of a database (for example my link is {{ URL::to($lingua. '/'. $campo[1].'/') }}) I need to use
URL::action('ItemController#show', ($lingua. '/'. $campo[1].'/'))
EDIT:
OK at the top of my pages there is a link to change language on the fly.
Italian //
English //
French
I create a controller clled LanguageController
<?php
class LanguageController extends BaseController {
public function select($lingua)
{
// Store the current language in the session
Session::put('lingua', $lingua);
return Redirect::back(); // redirect to the same page, nothing changes, just the language
}
}
I create a route:
Route::get('lingua/{lingua}', 'LanguageController#select');
Route::get('/', array('as' => 'home', 'uses' => 'ItemController#menu'));
Route::get('/mondo/','ItemController#mondo');
Route::get('/{idcampo}','ItemController#show');
I have my ItemController#menu
public function menu()
{ $linguadefault='it-IT';
$lingua = Session::get('lingua',$linguadefault);
$data = DB::table('campo')->lists('id');
return View::make('index')->with('campo',$data)->with('lingua',$lingua);
}
1) I don't understand why i need to route at lingua/{lingua} if i never route there but i use a url:action to a controller directly.
2) now i need to add
$linguadefault='it-IT';
$lingua = Session::get('lingua',$linguadefault);
at the beginning of every function to have a lingua variable in my page right?
3) now my language seems stucked to french and i can't change it anymore.
I would not use the language in the URL all the time, you can just switch languages when you need and persist it:
1) Use Session to persist the language chosen:
// Set the default language to the current user language
// If user is not logged, defaults to Italian
$linguaDefault = Auth::check()
? Auth::user()->lingua
: 'it-IT';
/// If not stored in Session, current language will be the default one
\App::setLocale(Session::get('lingua', $linguaDefault));
To have the language always set in your application, you can put this code in your file
app/start/global.php
And you don't need to add this anywhere else. So it will use it in this order:
a) Language stored in Session (selected online)
b) Language user has in database
c) Italian
2) To change the language you create a route:
Route::get('lingua/{lang}', 'LanguageController#select');
Your links
URL::action('LanguageController#select', 'it-IT')
URL::action('LanguageController#select', 'en-GB')
URL::action('LanguageController#select', 'fr-FR');
And in your controller you just have to do:
public function select($lang)
{
// Store the current language in the session
Session::put('lingua', $lang);
return Redirect::back(); // redirect to the same page, nothing changes, just the language
}
3) This way you don't need your language in all your URLs, you don't have to deal with it in all your routes. If your user changes the language in database, you just:
$user->save();
Session::put('lingua', $user->lingua);
return Redirect::route('home'); // or anything else

Laravel route acts craizy

Hy i have this http://laravel.io/bin/jaPB
The problem is when i go to:
domain.com -> it serves the homepage (wich is ok)
domain.com/foo -> it serves a subpage (still ok)
but when i go from one of those to:
domain.com/en -> it gives an error (not ok)
But after hitting refresh its ok.
So again when i'm on domain.com/en first time error after refresh ok
Same goes to subpage like domain.com/en/contact first time error after refresh ok
I would point out that the error says first time it tries to go to PublicPageController#subpage
but this shouldn't happen when i go to domain.com/en it should need to go to PublicPageController#homepage
Any idea ?
Thank you all.
My guess here form looking at the way you set up the locale-based routes is that the Session::get('urilang) isn't set the first time you visit, hence the error, and is only set once you've been to a page first.
Now, I haven't yet had to deal with multilingual sites, but as far as I'm aware the way you're doing it is not the correct way. Instead think of the lang key as a URI parameter, and use the filter to validate and set it for the routes. Something a bit like the below code:
// Main and subpage - not default language
Route::group(array('prefix' => '{lang}', 'before' => 'detectLanguage'), function () {
Route::get('', 'PublicPage#homepage');
Route::get('{slug}', 'PublicPage#subpage');
});
// Main and subpage - default language
Route::group(array('before' => 'setDefaultLanguage'), function () {
Route::get('/', 'PublicPage#homepage');
Route::get('/{slug}', 'PublicPage#subpage');
});
Route::filter('detectLanguage', function($route, $request, $response, $value){
// hopefully we could do something here with our named route parameter "lang" - not really on sure the details though
// set default
$locale = 'hu';
$lang = '';
// The 'en' -> would come from db and if there is more i would of corse use in array
if (Request::segment(1) == 'en')
{
$lang = 'en';
$locale = 'en';
}
App::setLocale($locale);
Session::put('uriLang', $lang);
Session::put('locale', $locale);
});
Route::filter('setDefaultLanguage', function($route, $request, $response, $value){
App::setLocale('hu');
Session::put('uriLang', '');
Session::put('locale', 'hu');
});
I don't know if you can use a segment variable in a Route::group prefix, but you should certainly have a go at it as it'd be the most useful.
That said, I wouldn't advise setting up default language routes that mimic specific language routes but without the language segment. If I were you, I'd set up a special root route that will redirect to /{defaultlang}/ just so you have fewer routing issues.

Resources