Laravel更改区域设置无效。

30

我正在使用下拉列表来选择语言,包括英语和荷兰语。

<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>

这是我的routes.php文件内容:

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

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

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

它没有起作用。

在我的项目中,路径是这样的

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

从荷兰语(du)的导航.php

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

并且对于英文(en)中的 'navigation.php'

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

2
setLocale不是持久的,所以更改它然后重定向会使其保持默认值。请考虑使用会话(或数据库)和中间件使此更改对用户永久生效。需要注意的是,如果这是一个公共面向网站,则应为不同的语言使用不同的URL。 - Devon
为什么要使用2个路由?很多网站没有使用也可以正常运行。 - mercury
5个回答

45

App::setLocale() 不具有持久性,仅在当前请求(运行时)中设置语言环境。您可以通过多种方式实现持久性(例如2个示例):

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

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

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

这将为当前用户从请求中获取的lang值设置会话密钥。接下来创建一个中间件,根据用户会话语言设置区域设置。

<?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);
    }

}

这将获取当前会话,如果为空,则回退到应用配置中设置的默认语言环境。

app\Http\Kernel.php中添加之前创建的Language中间件:

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

作为全球中间件或仅用于Web(根据您的需求)。

场景2 - 基于URL路径的语言设置:在应用程序配置中创建一个包含所有可用区域设置的数组

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

在中间件中,我们将首先检查URL的第一个部分en, fr, gr, cy,如果此部分存在于available_locale中,则设置语言

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);
}

你需要修改 `app\Providers\RouteServiceProvider` 文件中的路由前缀,这样你就可以通过 `example.com` 或 `example.com/fr/` 访问所有路由,而且带有 `法语` 语言。
找到:`mapWebRoutes`
在其之前添加以下内容(在添加 `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');
        });
    }

这将在你的所有路由前面添加国家字母,如“fr gr cy”(除了“en”以外),以避免重复内容,因此最好不要将其添加到available_locales_array中。


1
更新了我的答案。 - SergkeiM
4
$middlewareGroups 中,将 \App\Http\Middleware\Language::class 加到 \Illuminate\Session\Middleware\StartSession::class 之后。注意不要改变原意,使翻译通俗易懂。 - Vivek Pipaliya
@Froxz,我正在使用您的本地化方法。在laravel 5.7中运行良好。但问题是当用户注销时它会重置为默认值。(注意,我只按照VivekPipaliya的建议更改了您的方法)你能帮我吗? - Alauddin Ahmed
@AlauddinAhmed,你好,这是因为在5.7 Laravel中,注销方法会重定向到“/”。 return $this->loggedOut($request) ?: redirect('/');所以你可以这样做:在你的 LoginController 中添加以下代码:protected function loggedOut(Request $request) { return redirect(route('{name}')); //其中{name}是你想要在注销后重定向用户的路由; } - SergkeiM
@AlauddinAhmed 是的,因为区域设置是基于运行时的URL(前缀:例如fr)设置的。当您注销时,它会重定向到路由“/”,因此区域设置为英语,您需要重定向“/fr”以将区域设置为FR。使用route('home')(例如)将根据当前区域设置生成URL。 - SergkeiM
显示剩余6条评论

13

18
你应该在回答中引用文章中相关的部分。 - Emile Bergeron

5

App::setLocale()不是持久的。 我之前遇到过类似的问题,所以我创建了一个中间件:

<?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);
    }
}

我在 app\Http\Kernel 中注册了这个中间件:

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

这个脚本适用于两个域名:http://example.org(英语)和http://fr.example.org(法语)。作为中间件,在每个请求上被调用,因此区域设置始终根据URL设置为正确的区域设置。
我的路由如下:
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');
    // ...
}

因此,它会对以下内容响应正确的语言环境: 我使用同一个控制器和视图,只有两个不同的路由+全局中间件。
希望这能帮助你,但我不确定它是否是最好的解决方案。此解决方案在没有会话的情况下匹配域名和/或路由。与基于会话的解决方案相比,它具有一些优点:
  • 没有由于使用会话而产生的可能的错误(“幻术”语言切换)
  • 您可以重写您的路由。法国用户可能想看到他们URL中的“/mon-panier”,英国用户则想看到“/my-cart”。
  • 更好的谷歌索引(SEO),因为您可以通过国家进行实际内容索引。
  • 我正在生产中使用它!
它也可能有缺点。

1
@Froxz,是的,但为了SEO的目的,你不希望从同一个URL为公开面向的网站提供不同的语言。你不一定需要不同的子域名,一个不同的前缀或其他东西也可以起到同样的作用。 - Devon
@Froxz 它可以轻松地适应没有子域的简单路由,只需更改中间件逻辑即可。 - rap-2-h
如果我们谈论SEO,“domain.com”和“fr.domain.com”是两个不同的域名,因此最好保留一个域名,并使“domain.com”和其他语言的“domain.com/fr”等。无论如何,问题是关于如何设置未来请求的语言,而不是关于更好的SEO等方面。 - SergkeiM
1
@Froxz,是的,但这也是Laravel 5改用非持久化区域设置更改的主要原因之一。因此,我认为展示正确的方式非常重要,否则你几乎就是在放置他们剥离的Laravel 4代码。 - Devon
我并不是说@rap-2-h的答案是错误的,我已经在我的回答中发布了方案2的解决方法。 - SergkeiM

3
当用户从下拉列表中选择语言时,请调用以下路由将语言保存在会话中。
使用Vue.js
LocalizationComponent.vue更改语言。
<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');

//将语言环境保存到会话中

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

//创建中间件以设置语言环境
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
    ],
    //...
];

完成...


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接