Laravel по-русски

Русское сообщество разработки на PHP-фреймворке Laravel.

Ты не вошёл. Вход тут.

#1 17.02.2015 17:43:31

Как я локализацию прикручивал. Весёлые истории.

Привет!
Решил накатать статейку с кодом, как прикрутил локализации к L5.

ТЗ:

1) Адреса вида site.com/en/any-segment?any=argument определяют локализацию
2) Адрес без указания локализации уводит на ссылку с указанием локали, это СЕО-затычка, чтобы небыло двух одинаковых страниц по разным языкам.
3) Смена длкали через ссылку site.com/jp/set-language (language вместо locale понятней обывателю)

Побочный (не сильно приятный) эффект:
Все ссылки приходится генерить в шаблонах и в коде рукаами вида Request::secure( '/'.$locale )
(Возможно когда-нить поправлю код ядра, либо напишу какой-нить враппер, но я ленивый smile)

Реализация:

1. Установить пакет

"mcamara/laravel-localization": "1.0.*",

2. Исправить существующие файлы:
Публикуем конфиг нового пакета

/config/laravellocalization.php
Исправляем конфиг таким обазом (раскомментировать), чтобы включить язык 'en' и 'ru'
Отключаем:

'useAcceptLanguageHeader' => false, // мы используем наше значение конфига /config/app.php: locale=>en)
'useSessionLocale' => false, // мы берём локаль из адреса
'useCookieLocale' => false, // - отключаем чтобы не перебивало, когда меняем локаль (пояснение в конце)

/config/app.php

'locale' => 'en'

/app/Http/routes.php

Route::group(
    [
        'prefix' => LaravelLocalization::setLocale(),
        'middleware' => 'setlanguage'
    ],
    function()
    {
        // HomeController
        Route::get( 'home',  [ 'as' => 'home',   'uses' => 'HomeController@getIndex' ] );
        Route::get( '/',         [ 'as' => 'home',   'uses' => 'HomeController@getIndex' ] );
        Route::get( 'about',  [ 'as' => 'about',   'uses' => 'HomeController@getAbout' ] );

        // SettingsController
        Route::get( 'set-language', [ 'as' => 'set-language', 'uses' => 'SettingsController@setLocale' ] );

        // AuthController
        Route::controllers([
            'auth'      => 'Auth\AuthController',
            'password'  => 'Auth\PasswordController',
        ]);
    }
);

/app/Http/Kernel.php

protected $routeMiddleware = [
    // существующий код...
    // лосле локализаций переопределяем локаль
    'setlanguage' => 'App\Http\Middleware\SetLanguage',

3. Добавить новые файлы

/app/Http/Middleware/SetLanguage.php

<?php namespace App\Http\Middleware;
use Closure;
use Auth;
use LaravelLocalization;
use Config;
use Redirect;
class SetLanguage
{
    public function handle($request, Closure $next)
    {
        $addrLocale = $request->segment(1);
        $locales = LaravelLocalization::getSupportedLocales();
        $locales = array_keys( $locales );
        $defaultLocale = Config::get('app.locale');
        if ( !Auth::guest() )
        {
            // ставим локаль из настроек пользователя, если она разрешена
            $userLocale = Auth::user()->locale;
            if ( in_array( $userLocale, $locales ) )
            {
                LaravelLocalization::setLocale($userLocale);
            }
        }
        else
        {
            // ставим локаль из адреса, если она разрешена
            if ( in_array( $addrLocale, $locales ) )
            {
                LaravelLocalization::setLocale($addrLocale);
            }
            else
            {
                // иначе отправляем на адрес с локалью по умолчанию
                return Redirect::secure( '/'.$defaultLocale );
            }
        }
        return $next($request);
    }
}

/app/Http/Controllers/SettingsController.php

<?php namespace App\Http\Controllers;

use View;
use Request;
use Config;
use Auth;
use Redirect;
use LaravelLocalization;
use URL;

class SettingsController extends Controller
{
    public function __construct()
    {
        // во всех конструкторах необходимо кинуть локаль и список поддерживаемых локалей в шаблонизатор, чтобы реализовать переключатель языка. Для примера бросил здесь, но лучше где-нить в Controller::constructor'е
        View::share( 'locale', Lang::getLocale() );
        View::share( 'locales', LaravelLocalization::getSupportedLocales() );
    }
    public function setLocale()
    {
        $addrLOCALE     = Request::segment(1);
        $cfgLOCALE        = Config::get('app.locale');
        $segments         = Request::segments();
        $locales             = LaravelLocalization::getSupportedLocales();
        $locales             = array_keys( $locales );
        $setLocale         = null;

        if ( in_array( $addrLOCALE, $locales ) )
        {
            // локаль в адресе действительна
            // ставим её
            $setLocale = $addrLOCALE;
        }
        else
        {
            // локаль в адресе НЕ действительна
            // ставим дефолтную
            $setLocale = $cfgLOCALE;
        }

        if ( Auth::check() )
        {
            // пользователь авторизован
            // сохраняем в пользователя
            $user = Auth::getUser();
            $user->locale = $setLocale;
            $user->save();
        }

        // ставим локаль в адрес
        if ( !empty( $segments ) && $segments[1] == 'set-language' )
        {
            // ПОПЫТКА (!) уйти туда же откуда и пришли, заменив язык в адресе
            // TODO: не совсем уместно, но на уровне REST'a проблем возникнуть не должно
            $lc = implode('|',$locales);
            $back = URL::previous();
            $back = preg_replace( "#/([".$lc."].)#iu", "/{$setLocale}", $back );
            return Redirect::secure( $back );
        }
        else
        {
            // простой переход на главную страницу
            return Redirect::secure( '/'.$setLocale );
        }
    }
}

/resources/lang/en/*
остаётся как есть
/resources/lang/ru/*
копируем 'en' и переводим руками

========================

Шаблон переключателя локали

@if(!empty( $locales ))
    <li class="dropdown">
        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
            <i class="fa fa-cog"></i>
            {{trans('nav.sitelanguage')}}
        </a>
        <ul class="dropdown-menu" role="menu">
@foreach($locales AS $lc=>$ln)
                <li class="@if($locale==$lc) active @endif">
                    <a href="/{{$lc}}/set-language">
                        <i class="fa @if($locale==$lc) fa-check-square-o @else fa-square-o @endif"></i>
                        <span class="">{{$ln['native']}}</span>
                    </a>
                </li>
@endforeach
            </ul>
        </li>
@endif

При этом если установите css-спрайт с флагами, то сможете заменить голову меню на [Флаг] [Имя Языка] чтобы было красиво

Как-то так...
Пробуйте !

ПС:
1) если включить определение хедеров браузера - можно отказаться от определения дефолтного языка, однако потеряете возможность менять язык -- оно перебивает конфиг (во всяком случае у меня)
2) по такому же принципу можно менять шкурки, но значения хранить например в куках

Отвечу на вопросы, приму достойную и добрую критику.

Изменено hzone (17.02.2015 17:44:19)

Не в сети

#2 26.04.2015 15:40:30

bakatov82

Re: Как я локализацию прикручивал. Весёлые истории.

а что мешает сделать папки ru, eng и т.д., и там писать текст в виде констант/переменных? так же проще))) и при выборе языка просто бери текст из той папки, откуда нужно))) ИМХО

#3 26.04.2015 15:43:08

bakatov82

Re: Как я локализацию прикручивал. Весёлые истории.

тем более, что в laravel это прекрасно реализовано)))

#4 05.05.2015 15:32:32

Re: Как я локализацию прикручивал. Весёлые истории.

bakatov82 пишет:

а что мешает сделать папки ru, eng и т.д., и там писать текст в виде констант/переменных? так же проще))) и при выборе языка просто бери текст из той папки, откуда нужно))) ИМХО

ничего не мешает.
вы видимо поторопились с выводами, - прочтите ещё раз "ТЗ" из первого поста.
если лень, то кратко, - цель определять и переключать, а не использовать, что является уже побочным действием определения и переключения локали.

Не в сети

#5 16.12.2015 17:41:16

Cukama

Re: Как я локализацию прикручивал. Весёлые истории.

А если вдруг мне надо еще и http://prntscr.com/9ev94r , то мне придется еще продублировать все руты без префикса?

#6 16.12.2015 20:30:36

Cukama

Re: Как я локализацию прикручивал. Весёлые истории.

Эту группу фронтовых рутов необходимо повесить в самый конец файла /app/Http/routes.php
Туда же и локализованные 404

Route::group(
    [
        'prefix' => {lang?},
        'middleware' => 'lang'
    ],
    function()
    {
        // HomeController
        Route::get( 'home',  [ 'as' => 'home',   'uses' => 'HomeController@getIndex' ] );
        Route::get( '/',         [ 'as' => 'home',   'uses' => 'HomeController@getIndex' ] );
        Route::get( 'about',  [ 'as' => 'about',   'uses' => 'HomeController@getAbout' ] );

        // SettingsController
        Route::get( 'set-language', [ 'as' => 'set-language', 'uses' => 'SettingsController@setLocale' ] );

        // AuthController
        Route::controllers([
            'auth'      => 'Auth\AuthController',
            'password'  => 'Auth\PasswordController',
        ]);
    }
);

/app/Http/Kernel.php

protected $routeMiddleware = [
    // существующий код...
    // лосле локализаций переопределяем локаль
    'lang' => \App\Http\Middleware\Locale::class,

/app/Http/Middleware/Locale.php

<?php

namespace App\Http\Middleware;

use Closure;
use App;
use Request;
use Illuminate\Contracts\Cookie\QueueingFactory as CookieJar;

class Locale
{
    protected $cookies;

    public function __construct(CookieJar $cookies)
    {
        $this->cookies = $cookies;
    }

    public function handle($request, Closure $next)
    {
        $server = $request->server;
        $lang = $request->segment(1);
        $cookie_lang = Request::cookie('lang');

        $response = $next($request);

        foreach ($this->cookies->getQueuedCookies() as $cookie) {
            $response->headers->setCookie($cookie);
        }

        if($lang === 'ru' || $lang === 'en' || $lang === 'ua'){
            $response->withCookie(cookie('lang', $lang));
            App::setLocale($lang);
            return $response;
        }

        if($cookie_lang){
            return redirect( url( $cookie_lang . $server->get('REQUEST_URI') ) );
        } else {
            return redirect( url( App::getLocale() . $server->get('REQUEST_URI') ) );
        }
    }
}

#7 23.02.2016 14:42:51

Yuri

Re: Как я локализацию прикручивал. Весёлые истории.

здравствуйте . я установил Mcamara 1.0 Laravel 5.1  ,как будто все работает нормально ,никакой ошибок нету просто язык  не изменяется..

#8 06.09.2016 13:26:29

ridiks10

Re: Как я локализацию прикручивал. Весёлые истории.

Я делал так и все работает!
/app/Http/Middleware/Locale.php

<?php

namespace App\Http\Middleware;
use App;
use Closure;
use Request;
use Illuminate\Contracts\Cookie\QueueingFactory as CookieJar;

class Locale
{
    protected $languages = ['ru', 'uk', 'en'];
    protected $cookies;

    public function __construct(CookieJar $cookies)
    {
        $this->cookies = $cookies;
    }

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $lang = $request->segment(1);
        if(in_array($lang, $this->languages)) {
            \App::setLocale($lang);
            $this->cookies->queue(cookie('lang', $lang));
            return $next($request);
        }
        $server = $request->server;
        $cookie_lang = Request::cookie('lang');
        if($cookie_lang){
            return redirect( url( $cookie_lang . $server->get('REQUEST_URI') ) );
        } else {
            return redirect( url( App::getLocale() . $server->get('REQUEST_URI') ) );
        }
    }
}

#9 06.09.2016 14:29:52

Re: Как я локализацию прикручивал. Весёлые истории.

зависит ещё от версии лары... первый пост заточен под 5.1...
скоро напишу пост для 5.3, когда потребуется на работе сделать подобный финт.
может будет попроще

Не в сети

Подвал раздела