加入切換語系的機制,加入紀錄網站狀態的全域物件及相關設定機制

This commit is contained in:
kroutony 2020-02-22 15:18:54 +08:00
parent deecddd2ec
commit 675fb36dc8
15 changed files with 318 additions and 18 deletions

View File

@ -35,11 +35,14 @@ class Kernel extends HttpKernel
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\SetAppLanguage::class,
\App\Http\Middleware\SetSiteStates::class,
],
'api' => [
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\SetSiteStates::class,
],
];
@ -79,5 +82,6 @@ class Kernel extends HttpKernel
\Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Illuminate\Auth\Middleware\Authorize::class,
\App\Http\Middleware\SetAppLanguage::class,
];
}

View File

@ -0,0 +1,106 @@
<?php
namespace App\Http\Middleware;
use Closure;
/**
* 設定網站語系
*
* Class SetAppLanguage
* @package App\Http\Middleware
*/
class SetAppLanguage
{
/**
* Cookie及Session存放Locale資訊的Key
*
* @var string
*/
private $cookieAndSessionKey = 'appLocale';
/**
* 設定Locale時的參數Key
*
* @var string
*/
private $localeRequestKey = 'locale';
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
//取得所有語言清單
$availableLocales = (array)config('languages');
//要被回傳的Cookie
$setLocaleCookie = false;
//要被設定的語言
$setLocale = false;
//如果有Locale參數
if($request->get($this->localeRequestKey) !== null) {
//取得傳入的Locale值
$requestLocale = $request->get($this->localeRequestKey);
//如果有在語言清單內
if(array_key_exists($requestLocale, $availableLocales)) {
//設定session
$request->session()->put($this->cookieAndSessionKey, $requestLocale);
//設定要被回傳的Cookie及目標Locale
$setLocale = $setLocaleCookie = $requestLocale;
}
}
//假如沒有目標Locale再讀取Session、Cookie、瀏覽器的Locale
if(!$setLocale) {
$cookieLocale = $request->cookies->get($this->cookieAndSessionKey);
$sessionLocale = $request->session()->get($this->cookieAndSessionKey);
if(array_key_exists($sessionLocale, $availableLocales)) {
//設定要被回傳的Cookie及目標Locale
$setLocale = $sessionLocale;
if(!$cookieLocale) {
$setLocaleCookie = $sessionLocale;
}
} elseif(array_key_exists($cookieLocale, $availableLocales)) {
//設定Session及目標Locale
$request->session()->put($this->cookieAndSessionKey, $cookieLocale);
$setLocale = $cookieLocale;
} else {
//Session及Cookie都沒有Locale值時讀取HTTP Header的資訊
$browserLocales = $request->server('HTTP_ACCEPT_LANGUAGE');
$browserLocales = preg_replace('/;q=0\.\d+/', '', $browserLocales);
$browserLocales = explode(',', $browserLocales);
$browserLocales = array_map(function($lang){
return strtolower($lang);
}, $browserLocales);
//與語言清單比對
$validBrowserLocales = array_intersect($browserLocales, array_keys($availableLocales));
if(!empty($validBrowserLocales)) {
//取得第一個瀏覽器Locale
$validBrowserLocale = head($validBrowserLocales);
//設定Session及目標Locale
$request->session()->put($this->cookieAndSessionKey, $validBrowserLocale);
$setLocale = $setLocaleCookie = $validBrowserLocale;
}
}
}
if($setLocale) {
//如果有目標Locale
app()->setLocale($setLocale);
} else {
//如果沒有目標Locale則設定成預設語言
app()->setLocale(config('app.fallback_locale'));
}
//如果有Cookie則帶入request
if($setLocaleCookie) {
return $next($request)->withCookie(cookie()->forever($this->cookieAndSessionKey, $setLocaleCookie));
} else {
return $next($request);
}
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace App\Http\Middleware;
use App\Services\SiteStateService;
use Closure;
/**
* 設定SiteStateService中的資料
*
* Class SetSiteStates
* @package App\Http\Middleware
*/
class SetSiteStates
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$siteState = app(SiteStateService::class);
//是否網站後台判斷route prefix
if($request->is(config('admin.route') . '/*')) {
$siteState->isAdminArea = true;
}
//所有可用語系及其原文名稱
$siteState->languagesWithLabel = config('languages');
//所有可用語系
$siteState->languages = array_keys($siteState->languagesWithLabel);
//所有其他可用語系,排除現在語系
$siteState->otherLanguages = collect($siteState->languages)->filter(function($locale){
return $locale !== app()->getLocale();
})->all();
//所有語系的當前語言翻譯
foreach ($siteState->languages as $locale) {
$siteState->languageTranslations[$locale] = trans('languages.' . $locale);
}
//預設語系
$siteState->defaultLanguage = config('app.fallback_locale');
return $next($request);
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace App\Presenters;
use Illuminate\Http\Request;
class UrlPresenter
{
private $request;
public function __construct(Request $request)
{
$this->request = $request;
}
public function appendLocale($locale)
{
$url = $this->request->fullUrlWithQuery(['locale' => $locale]);
return $url;
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace App\Providers;
use App\Option;
use Illuminate\Support\ServiceProvider;
/**
* 初始化所有singleton class instance
*
* Class SingletonServiceProvider
* @package App\Providers
*/
class SingletonServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register()
{
// Services
$this->app->alias(\App\Services\SiteStateService::class, 'SiteState');
$this->app->singleton(\App\Services\SiteStateService::class, function($app){
return new \App\Services\SiteStateService;
});
}
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
//
}
}

View File

@ -0,0 +1,64 @@
<?php
namespace App\Services;
/**
* 紀錄全域的網站狀態
*
* Class SiteStateService
* @package App\Services
*/
class SiteStateService
{
/**
* 是否為後台
*
* @var bool
*/
public $isAdminArea = false;
/**
* 所有可用語系
*
* @var array
*/
public $languages = [];
/**
* 所有其他可用語系,排除現在語系
*
* @var array
*/
public $otherLanguages = [];
/**
* 所有可用語系及其原文名稱
*
* @var array
*/
public $languagesWithLabel = [];
/**
* 所有語系的當前語言翻譯
*
* @var array
*/
public $languageTranslations = [];
/**
* 預設語系
*
* @var string
*/
public $defaultLanguage = '';
/**
* 取得所有變數
*
* @return array
*/
public function getAll()
{
return get_object_vars($this);
}
}

View File

@ -175,7 +175,7 @@ return [
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
App\Providers\SingletonServiceProvider::class,
],
/*

8
config/languages.php Normal file
View File

@ -0,0 +1,8 @@
<?php
return [
'en' => 'English',
'zh-tw' => '繁體中文',
'zh-cn' => '简体中文',
'ja' => '日本語',
'ko' => '한국어'
];

View File

@ -1 +1,2 @@
import './lib';
import '../app-common'

4
resources/js/app-common.js vendored Normal file
View File

@ -0,0 +1,4 @@
$('.logout-btn').on('click', e => {
e.preventDefault()
$('#logout-form').submit()
})

3
resources/js/app.js vendored
View File

@ -1 +1,2 @@
require('./bootstrap');
import './lib'
import './app-common'

View File

@ -31,6 +31,7 @@
</li>
</ul>
<ul class="nav navbar-nav ml-auto">
@component('components.languageDropdown')@endcomponent
</ul>
<button class="navbar-toggler aside-menu-toggler d-md-down-none" type="button" data-toggle="aside-menu-lg-show">

View File

@ -0,0 +1,9 @@
@inject('url', App\Presenters\UrlPresenter)
<li class="nav-item dropdown">
<a href="#" class="nav-link btn-icon dropdown-toggle switch-language" data-toggle="dropdown" role="button" aria-expanded="false" aria-haspopup="true">{{ app('SiteState')->languagesWithLabel[app()->getLocale()] }}</a>
<div class="dropdown-menu dropdown-menu-right">
@foreach (app('SiteState')->otherLanguages as $locale)
<a href="{{ $url->appendLocale($locale) }}" class="dropdown-item">{{ app('SiteState')->languagesWithLabel[$locale] }}</a>
@endforeach
</div>
</li>

View File

@ -26,24 +26,14 @@
</li>
@endif
@else
<li class="nav-item dropdown">
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
{{ Auth::user()->name }} <span class="caret"></span>
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
{{ __('Logout') }}
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
@csrf
</form>
</div>
<li class="nav-item">
<a class="nav-link logout-btn" href="#">{{ __('Logout') }}</a>
<form id="logout-form" action="{{ route('logout') }}" method="post" style="display: none;">
@csrf
</form>
</li>
@endguest
@include('components.languageDropdown')
</ul>
</div>
</div>