Laravel change locale not working
Asked Answered
P

5

30

I am using a dropdown lists for the languages, consisting of english and dutch.

<form class="" action="{{url('/locale')}}" method="post">
            Locale:
              <select class="" name="locale" onchange="this.form.submit()">

                <option value="en" >English</option>
                <option value="du" >Dutch</option>
              </select>
          </form>

Then this is my routes.php,

Route::post('/locale', function(){

     \App::setLocale(Request::Input('locale'));

     return redirect()->back();
});

And it is not working.

In my project, the path is like this

resources/
 /du
   navigation.php
 /en
  /navigation.php

From the Dutch(du) 'navigation.php'

<?php
return [
  "home" => 'Home-test-dutch',  
];

and for the English(en) 'navigation.php'

<?php
return [
  "home" => 'Home',  
];
Paphian answered 21/12, 2016 at 15:5 Comment(2)
setLocale is not persistent, so changing it then redirecting will leave it the default. Look to using sessions (or the database) and middleware to make this change permanent for the user. I should note, if this is a public facing web site, you should use different urls for different languages.Internalize
why should use 2 routes , many sites are ok without that .Osugi
P
14

I solved my problem from this article https://mydnic.be/post/laravel-5-and-his-fcking-non-persistent-app-setlocale

Thanks to the people who contributed the word 'non persistent'

Paphian answered 22/12, 2016 at 3:4 Comment(1)
You should quote the relevant parts of the article in your answer.Pandybat
M
46

App::setLocale() is not persistent, and sets locale only for current request(runtime). You can achieve persistent in multiple ways (example of 2):

Route::post('/locale', function(){

     session(['my_locale' => app('request')->input('locale')]);

     return redirect()->back();
});

This will set session key with lang value from request for current user. Next create a Middleware to set locale based on user session language

<?php namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Foundation\Application;

class Language {

    public function __construct(Application $app, Request $request) {
        $this->app = $app;
        $this->request = $request;
    }

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $this->app->setLocale(session('my_locale', config('app.locale')));

        return $next($request);
    }

}

This will get current session and if is empty will fallback to default locale, which is set in your app config.

In app\Http\Kernel.php add previously created Language middleware:

protected $middleware = [
   \App\Http\Middleware\Language::class,
];

As global middlware or just for web (based on your needs).

Scenario №2 - Lang based on URL path Create an array with all available locales on your app inside app config

'available_locale' => ['fr', 'gr', 'ja'],

Inside the Middleware we will check the URL first segment en, fr, gr, cy if this segment is in available_locale, set language

public function handle($request, Closure $next)
{
      if(in_array($request->segment(1), config('app.available_locale'))){
            $this->app->setLocale($request->segment(1));
      }else{
            $this->app->setLocale(config('app.locale'));
      }

      return $next($request);
}

You will need to modify app\Providers\RouteServiceProvider for setting prefix to all your routes. so you can access them example.com or example.com/fr/ with French language Find: mapWebRoutes And add this to it: (before add use Illuminate\Http\Request;)

public function map(Request $request)
    {
        $this->mapApiRoutes();
        $this->mapWebRoutes($request);
    }
    protected function mapWebRoutes(Request $request)
    {
        $locale = null;
        if(in_array($request->segment(1), config('app.available_locale'))){
          $locale = $request->segment(1);
        }

        Route::group([
           'middleware' => 'web',
           'namespace' => $this->namespace,
           'prefix' => $locale
        ], function ($router) {
             require base_path('routes/web.php');
        });
    }

This will prefix all your routes with country letter like 'fr gr cy' except en for non-duplicate content, so is better to not add into available_locales_array

Marlanamarlane answered 21/12, 2016 at 15:17 Comment(11)
Undefined variable: request in RouteServiceProvider Oppilate
@MuhammadShahzad what is your laravel version?Marlanamarlane
Hi, 5.4 versionOppilate
@upated my answerMarlanamarlane
You need to add your middleware \App\Http\Middleware\Language::class, after \Illuminate\Session\Middleware\StartSession::class in $middlewareGroups .Pietrek
@Froxz , i using your method for localization. Which is working good in laravel 5.7. But the problem is it is resetting to default one when logging out from a user. (note, i only changed in your method as VivekPipaliya mentioned) Can you help me out?Contreras
@AlauddinAhmed, Hi this is because in 5.7 laravel, the logout methos redirects to "/" return $this->loggedOut($request) ?: redirect('/'); So what you can do: In your LoginController add protected function loggedOut(Request $request) { return redirect(route('{name}')); //where {name} is the route where you want to redirect user after logout; }Marlanamarlane
@Froxz, i was talking about localization. I have 2 language, english and french. My default english. then i changed it to french through localization. Then login to that site. when i logged out from the site, it is reseting to english again. not staying in french language.Contreras
@AlauddinAhmed yes, because locale is set on Runtime based on URL (prefix: eg fr) When you logout it redirects to route "/", so locale is set to english, you need to redirect "/fr", in order to set locale to FR. using route('home') (for examle) wil generate URL based on current lcoaleMarlanamarlane
@Froxz. But you are not setting locale based on url, aren't you using session and middleware? sorry but i kinda beginner at laravel, so not understanding easily..Contreras
@AlauddinAhmed no Locale is Set based on segment 1 if(in_array($request->segment(1), config('app.available_locale'))){ $this->app->setLocale($request->segment(1)); }Marlanamarlane
P
14

I solved my problem from this article https://mydnic.be/post/laravel-5-and-his-fcking-non-persistent-app-setlocale

Thanks to the people who contributed the word 'non persistent'

Paphian answered 22/12, 2016 at 3:4 Comment(1)
You should quote the relevant parts of the article in your answer.Pandybat
C
5

App::setLocale() is not persistent. I had a similar problem before so I created a 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 (strpos($request->getHttpHost(), 'fr.') === 0) {
            \App::setLocale('fr');
        } else {
            \App::setLocale('en');
        }
        return $next($request);
    }
}

And I registered this middleware in app\Http\Kernel:

protected $middlewareGroups = [
    'web' => [
        // ...
        \App\Http\Middleware\SetLocale::class,
        // ...
    ]
];

This script works for two domains: http://example.org (en) and http://fr.example.org (fr). As a middleware, it's called on every request, so the locale is always set as the right locale according to the url.

My routes looked like:

Route::group(['domain' => 'fr.' . config('app.root-domain')], function () {
    Route::get('a-propos', 'HomeController@about');
    // ...
}
Route::group(['domain' => config('app.root-domain')], function () {
    Route::get('about', 'HomeController@about');
    // ...
}

So it responds with the correct locale to:

And I use the same controller and same view, just 2 different routes + a global middleware.

Hope it will help, not sure it's the best solution BTW. This solution works without sessio, it matches with domain and/or routes. It has some advantages over session-based solutions:

  • No possible bugs due to session usage ("magic" language switch)
  • You can rewrite your routes. A french user may want to see "/mon-panier" and english user "/my-cart" in their url.
  • Better indexing in google (SEO), because you can have a real index by country with relevant content.
  • I use it in production!

It may have it's cons too.

Clouse answered 21/12, 2016 at 15:17 Comment(5)
@Froxz, yes, but for SEO purposes, you don't want to serve different languages from the same URL for public facing web sites. You don't necessarily need different subdomains, a different prefix or something else may work as well.Internalize
@Froxz It can easily be adapted to simple routes without subdomain, just change the middleware logic.Clouse
If we speak about SEO domain.com and fr.domain.com are 2 different domains, so it is better to keep one domain and make domain.com and other languages domain.com/fr an so on. Anyway the question was about how to set language for future requests not about better SEO an so on.Marlanamarlane
@Froxz, yes, but that is one of the primary drivers behind Laravel making the switch to non-persistent locale changes in Laravel 5. So I think it's important to show the right way, otherwise you're pretty much just putting in the Laravel 4 code that they stripped out.Internalize
I don't say that @Clouse answer is incorrect, I've posted Scenario 2 solution on my answerMarlanamarlane
G
3

When user selects language from dropdown, call below route to save language in session

Using Vue.js

LocalizationComponent.vue change language

<template>
<ul>
    <li @click="changeLanguage('en')">
        <a href="javascript:void(0);">
            <img src="/images/flag-1.png"  alt="image description">
            <span>ENG</span>
        </a>
    </li>
    <li @click="changeLanguage('vn')">
        <a href="javascript:void(0);">
            <img src="/images/flag-2.png"  alt="image description">
            <span>Việt</span>
        </a>
    </li>
</ul>

<script>
    export default{
        data(){
            return {
                selected_language:'en',
            }
        },
         methods:{
            changeLanguage(language){
                this.axios.post('/change-locale',{language:language}).then( 
                (response) => {window.location.reload();
            }).catch((error) => {
                console.log(error.response.data.errors)
            });
            localStorage.setItem('selected_language',language);
        }
    }
}
</script>

routes/web.php

Route::post('/change-locale', 'HomeController@changeLocale');

//save locale to session

public function changeLocale(Request $request)
{
    $language = $request->language ?? 'en';
    session(['selected_language' =>$language]);
}

//create middleware to set locale

class Localization
{
    public function handle($request, Closure $next)
    {
        App::setLocale(session()->get('selected_language') ?? 'en');
        return $next($request);
    }
}

In app/Http/Kernel.php

 protected $middlewareGroups = [
    'web' => [
        ...other middelwares
        Localization::class,  //add your localization middleware here
    ],
    //...
];

Done...

Ganglion answered 26/10, 2019 at 20:42 Comment(0)
K
0

setLocale will only change the language for the rest of the request from that point on.

So to persist it, you could look into setting the selected language in a session (https://laravel.com/docs/session). Then you can create a middeware (https://laravel.com/docs/middleware) where you can check if a language is set in the session and then apply it for the request :)

Knap answered 21/12, 2016 at 15:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.