I managed to get some routes working with and without a prefix. Having routes that do not have the prefix work properly is important as it's too much work to go back and change all get/post links to the correct localized ones.
For example with the code below the URL localhost/blog redirects to localhost/en/blog (or any other language stored in session).
However, I noticed that URLs with parameters don't work, so /blog/read/article-name will result in a 404 instead of redirecting to /en/blog/read/article-name.
Routes:
Route::group([
'prefix' => '{locale}',
'middleware' => 'locale'],
function() {
Route::get('blog', 'BlogController#index');
Route::get('blog/read/{article_permalink}', 'BlogController#view');
}
);
Middleware is responsible for the redirects which don't seem to fire at all for some routes as if the route group isn't matching the URL.
public function handle($request, Closure $next)
{
if ($request->method() === 'GET') {
$segment = $request->segment(1);
if (!in_array($segment, config('app.locales'))) {
$segments = $request->segments();
$fallback = session('locale') ?: config('app.fallback_locale');
$segments = array_prepend($segments, $fallback);
return redirect()->to(implode('/', $segments));
}
session(['locale' => $segment]);
app()->setLocale($segment);
}
return $next($request);
}
Related
I have a Laravel app in 9 difference languages:
/resources
-- /lang
-- -- /en_US.json
-- -- /fr_FR.json
-- -- /...
I'm trying to set up localized URL's like:
Route::get(__("link.projects"), 'GuestController#projects');
All content on the website is displayed in the correct language but routing returns 404 pages except for default fallback language.
My web.php
Route::group(['middleware' => ['web', 'locale']], function () {
/* GUEST */
Route::get('/', 'GuestController#index');
Route::get(__("link.projects"), 'GuestController#projects');
Route::get(__("link.participants"), 'GuestController#participants');
Route::get(__("link.participant")."/{public_id}", 'GuestController#participant');
Route::get(__("link.about"), 'GuestController#about');
Route::get(__("link.contact"), 'GuestController#contact');
});
My locale middleware:
<?php
namespace App\Http\Middleware;
use Closure;
class SetLocale
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if($request->getHost() == "domain.com") {
$locale = 'en_US';
} elseif($request->getHost() == "domain.dk") {
$locale = 'da_DK';
} elseif($request->getHost() == "domain.de") {
$locale = 'de_DE';
} elseif($request->getHost() == "domain.se") {
$locale = 'sv_SE';
} elseif($request->getHost() == "domain.fi") {
$locale = 'fi_FI';
} elseif($request->getHost() == "domain.es") {
$locale = 'es_ES';
} elseif($request->getHost() == "domain.fr") {
$locale = 'fr_FR';
} elseif($request->getHost() == "domain.it") {
$locale = 'it_IT';
} elseif($request->getHost() == "domain.nl") {
$locale = 'nl_NL';
} else {
$locale = 'en_US';
}
if (in_array($locale, [
"en_US",
"da_DK",
"de_DE",
"sv_SE",
"fi_FI",
"es_ES",
"fr_FR",
"it_IT",
"nl_NL"
]
)) {
session()->put('locale', $locale);
} else {
session()->put('locale', 'en_US');
}
if(session()->has('locale')) {
app()->setLocale(session('locale'));
}
return $next($request);
}
}
Using the fallback language URLs in production i'm able to get the content localized but URL's in the web.php won't allow translation.
I don't want to have prefix in URL's since the different locales are set by domain instead.
Okay so i took a small break and figured out a possible solution which seems to be working perfectly.
$locales = array("en_US","de_DE","es_ES","it_IT","fr_FR","da_DK","sv_SE","fi_FI","nl_NL");
foreach($locales as $locale) {
/* GUEST */
Route::get(__("link.projects", [], $locale), 'GuestController#projects');
Route::get(__("link.participants", [], $locale), 'GuestController#participants');
Route::get(__("link.participant", [], $locale)."/{public_id}", 'GuestController#participant');
Route::get(__("link.about", [], $locale), 'GuestController#about');
Route::get(__("link.contact", [], $locale), 'GuestController#contact');
Route::get(__("link.cookies", [], $locale), 'GuestController#cookies');
Route::get(__("link.terms_of_use", [], $locale), 'GuestController#terms_of_use');
Route::get(__("link.privacy_policy", [], $locale), 'GuestController#privacy_policy');
Route::get(__("link.disclaimer", [], $locale), 'GuestController#disclaimer');
}
Let me know if this has any potential breaks, but for me it's working.
The way I handle this is to use two routes, one for the home page (which generally contains more complex logic like news, pick up articles, banners, etc), and a catch all for any other page.
Routes
// Home page
Route::get('/', [
'as' => 'home',
'uses' => 'PageController#index'
]);
// Catch all page controller (place at the very bottom)
Route::get('{slug}', [
'uses' => 'PageController#getPage'
])->where('slug', '([A-Za-z0-9\-\/]+)');
The important part to note in the above is the ->where() method chained on the end of the route. This allows you to declare regex pattern matching for the route parameters. In this case I am allowing alphanumeric characters, hyphens and forward slashes for the {slug} parameter.
This will match slugs like
test-page
test-page/sub-page
another-page/sub-page
you can set your own pattern.
I think i found a perfect solution for you.
After a quick google search I found the following:
Check it out!
https://github.com/codezero-be/laravel-localized-routes
Currently, my system is using 2 languages which is English and German. my goal is to browse following routes by switching between the mentioned language -
base-url/contact - for english
base-url/kontakte - for german
Currently, I have required routes file in the resource folder, where I have put the necessary translated words.
resources/lang/en/routes.php
resources/lang/de/routes.php
In web.php I have currently -
Route::get(r('contact'), 'TestController#index')->name('contact');
by r() helper function I am getting the active translated word.
On my user table, I have locale column where I am storing the active language when I am updating the language from user profile -
\Session::put('locale', $request->input('locale'));
I have created a middleware Localization where I have currently -
public function handle($request, Closure $next)
{
if ( \Session::has('locale')) {
\App::setLocale(\Session::get('locale'));
Carbon::setLocale(\Session::get('locale'));
}
return $next($request);
}
Currently, the code is working fine for blade translated word. but the translated routes are not working. whenever I switch and visit any route, it gives me 404 error. but if I restart the server by PHP artisan serve, it works with changed language.
So how fix the issue?
:) Try to put \App::setLocale(\Session::get('locale') in the beginning of the routes file (routes.php, or web.php/api.php)
With PHP 8.1.0, Laravel 9.x
resources/lang/en/routes.php:
return [
'post' => '/post/',
'product' => '/product/',
'contact' => '/contact/',
'aboutUs' => '/about-us/'
];
Create LanguageType enum:
enum LanguageType: string
{
case EN = 'en';
case DE = 'de';
public function label(): string
{
return trans(match ($this) {
self::EN => 'English',
self::DE => 'German',
});
}
}
In web.php:
Route::get('{contactTranslation}', [ContactController::class, 'index']);
Route::get('{aboutUsTranslation}', [AboutUsController::class, 'index']);
Route::get('{productTranslation}', [ProductController::class, 'index']);
Route::get('{postTranslation}', [PostController::class, 'index']);
//...
In RouteServiceProvider.php/boot(), add:
foreach (trans('routes') as $key => $value) {
Route::pattern($key . 'Translation', $this->getTranslation($key));
}
And function:
private function getTranslation($slug): string
{
$slugList = collect();
foreach (LanguageType::cases() as $language) {
$slugList = $slugList->merge(trim(trans('routes.' . $slug, [], $language->value), '/'));
}
return $slugList->implode('|');
}
I am having issue getting the group route parameter in middleware here's what i am doing
I am using [PHP - SLIM 3 Framework]
route.php
$app->group('/{lang}', function() use ($container){
//routes ... (ignore the other routes)
})->add(new Middleware($container));
Middleware.php
//middleware.php
class Middleware {
public function __invoke($req, $res, $next)
{
//here is the part which is confusing about how can i get
// {lang} parameter
$req->getAttribute('route')
}
}
You can do this with the getArguments()-method
public function __invoke($req, $res, $next)
{
$route = $req->getAttribute('route');
$args = $route->getArguments();
$lang = $args['lang'];
return $res;
}
Note: you also need to set the slim setting determineRouteBeforeAppMiddleware to true. Otherwise the argument is not set in the middleware.
$container = [
'settings' => [
'determineRouteBeforeAppMiddleware' => true
]
]
$app = new \Slim\App($container);
Lets assume I have a site with cars: cars.com on Laravel 5.
I want to set up my routes.php so a user could type in a browser ford.cars.com/somethingOrnothing and get to the controller responsible for Ford™ cars (FordController).
Of course I could use something like this code:
Route::group(['middleware' => 'web'], function () {
Route::group(['domain' => 'ford.cars.com'], function(\Illuminate\Routing\Router $router) {
return $router->resource('/', 'FordController');
});
});
But I am not happy about writing and maintaining routes for hundreds of car brands.
I would like to write something like this:
Route::group(['domain' => '{brand}.cars.com'], function(\Illuminate\Routing\Router $router) {
return $router->get('/', function($brand) {
return Route::resource('/', $brand.'Controller');
});
});
So the question is: Is it possible to dynamically set routes for sub-domains and how to achieve this?
upd:
the desirable outcome is to have subdomains that completely repeat controllers structure. Like Route::controller() did (but it is now deprecated)
To emulate Route::controller() behaviour you could do this:
Route::group(['domain' => '{carbrand}.your.domain'], function () {
foreach (['get', 'post'] as $request_method) {
Route::$request_method(
'{action}/{one?}/{two?}/{three?}/{four?}',
function ($carbrand, $action, $one = null, $two = null, $three = null, $four = null) use ($request_method) {
$controller_classname = '\\App\\Http\\Controllers\\' . Str::title($carbrand).'Controller';
$action_name = $request_method . Str::title($action);
if ( ! class_exists($controller_classname) || ! method_exists($controller_classname, $action_name)) {
abort(404);
}
return App::make($controller_classname)->{$action_name}($one, $two, $three, $four);
}
);
}
});
This route group should go after all other routes, as it raises 404 Not found exception.
Probably this is what you need:
Sub-Domain Routing
Route groups may also be used to route wildcard sub-domains.
Sub-domains may be assigned route parameters just like route URIs,
allowing you to capture a portion of the sub-domain for usage in your
route or controller. The sub-domain may be specified using the domain
key on the group attribute array:
Route::group(['domain' => '{account}.myapp.com'], function () {
Route::get('user/{id}', function ($account, $id) {
//
});
});
From:
Laravel 5.2 Documentation
upd.
If you want to call your controller method you can do it like this:
Route::group(['domain' => '{account}.myapp.com'], function () {
Route::get('user/{id}', function ($account, $id) {
$controllerName = $account . 'Controller' //...or any other Controller Name resolving logic goes here
app('App\Http\Controllers\\' . $controllerName)->controllerMethod($id);
});
});
How to I redirect following urls to lower case url?
http://domain.com/City/really-long-slug-from-db/photos
http://domain.com/city/Really-Long-Slug-From-Db/photos
http://domain.com/City/really-long-slug-from-db/Photos
to
http://domain.com/city/really-long-slug-from-db/photos
This is my route:
Route::any('/{city}/{slug}/{page?}',
array(
'as' => 'slug-page',
function($city, $slug, $page="info"){
return View::make('default.template.'.$page)
->with('city', $city)
->with('page',$page)
->with('slug', $slug);
}
))
->where(
array(
'city' => '[a-z ]+',
'page' => '[a-z-]+',
'slug' => '(about|photos|videos)'
));
Currently I used regex [a-z-]+ to match only smaller case strings and that throws NotFoundHttpException for obvious reasons.
How do I accept all these parameters in case insensitive strings and 301 redirect(to avoid duplicate urls) to smaller case urls in Laravel 5.1?
You could easily do that with a route middleware. The middleware should check if there are any uppercase characters in the path and redirect to lowercased version.
First, define the middleware class:
class RedirectToLowercase
{
public function handle($request, Closure $next) {
$path = $request->path();
$pathLowercase = strtolower($path); // convert to lowercase
if ($path !== $pathLowercase) {
// redirect if lowercased path differs from original path
return redirect($pathLowercase);
}
return $next($request);
}
}
Then register the new middleware in your Kernel.php:
protected $routeMiddleware = array(
// ... some other middleware classes ...
'lowercase' => 'App\Http\Middleware\RedirectToLowercase'
);
Finally, apply the middleware to your route:
Route::any('/{city}/{slug}/{page?}', array(
'as' => 'slug-page',
'middleware' => 'lowercase',
function() {
// your code
})
);