Jednoduchá lokalizace Laravel aplikace


Autor Pavel Zaněk

20. 11. 2021

Níže uvedený návod popisuje jednoduchou lokalizaci Laravel aplikace (tedy překlady pouze částí aplikace, nikoliv překlad obsahu stránek uloženého v databázi či lokalizace URL adres). Návod se tedy věnuje, jak nastavit překlady řetězců objevujících se v šabloně Laravel aplikace.

Testováno ve verzi: Laravel 8
Stack: Jetstream / Livewire + Blade/Tailwind CSS, ale prakticky se změnami na straně front endu lze použít kdekoliv
Dokumentace: https://laravel.com/docs/8.x/localization

Logika

Logika je poměrně jednoduchá, není potřeba žádného zásahu do databáze.

Nastavení jazyků

...
'locales' => [
        'en' => 'English',
        'cs' => 'Czech',
 ],
...

Middleware Language

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\URL;

class Language
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {        
        App::setLocale($request->language);
        URL::defaults(['language' => $request->language]);
        $request->route()->forgetParameter('language');
        return $next($request);
    }
}

Přidání middlewaru do kernelu

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

        ...
];
...

Routy ve web.php

...
Route::group([
    'prefix' => "{language}", 
    'where' => ['language' => '[a-zA-Z]{2}']
    ], function () {

    ...

});
...

Kontroler LanguageController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\URL;

class LanguageController extends Controller
{
    public function switchLang(Request $request, $lang)
    {
        // $urlParts[1] == current lang
        $urlParts = explode('/', str_ireplace(array('http://', 'https://'), '', url()->previous()));

        $newUrl = str_replace(
            '/' . $urlParts[1] . '/', 
            '/' . $lang . '/',
            url()->previous() 
        );

        App::setLocale($lang);
        URL::defaults(['language' => $lang]);
        return redirect()->to($newUrl);
    }
}

Přidání routy kontroleru do web.php

...
Route::get('lang/{lang}', [App\Http\Controllers\LanguageController::class, 'switchLang'])->name('langSwitcher');
...

RouteServiceProvider.php

...
public const DASHBOARD = '/en/dashboard';
...

Vytvoření Language switcheru

Příklad pro použití ve stacku: Laravel, Jetstream – Livewire + Blade, Tailwindcss

Desktop

...
<!-- Language Dropdown -->
<div class="ml-3 relative">
    <x-jet-dropdown align="right" width="48">
        <x-slot name="trigger">
            <span class="inline-flex rounded-md">
                <button type="button" class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 bg-white hover:text-gray-700 focus:outline-none transition">
                    @lang(config('app.locales')[App::getLocale()])

                    <svg class="ml-2 -mr-0.5 h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
                        <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
                    </svg>
                </button>
            </span>
        </x-slot>

        <x-slot name="content">
            <!-- Language Switcher -->
            <div class="block px-4 py-2 text-xs text-gray-400">
                {{ __('Select language') }}
            </div>

            @foreach (config('app.locales') as $lang => $language)
                @if ($lang !== App::getLocale())
                    <x-jet-dropdown-link href="{{ route('langSwitcher', $lang) }}">
                        @lang($language)
                    </x-jet-dropdown-link>
                @endif
            @endforeach

        </x-slot>
    </x-jet-dropdown>
</div>
...

Responsive

...
<div class="mt-3 space-y-1">
    <div class="border-t border-gray-200"></div>

    <div class="block px-4 py-2 text-xs text-gray-400">
        {{ __('Select language') }}
    </div>
    
    @foreach (config('app.locales') as $lang => $language)
        @if ($lang !== App::getLocale())
            <x-jet-responsive-nav-link href="{{ route('langSwitcher', $lang) }}">
                @lang($language)
            </x-jet-responsive-nav-link>
        @endif
    @endforeach

    <div class="border-t border-gray-200"></div>
</div>
...

Doplnění překladů

Například resources/lang/cs.json

{
    "Dashboard": "Nástěnka",
    "Select language": "Vyberte jazyk",
    "Czech": "Čeština",
    ...
}

Výsledek

Česká verze

Anglická verze

Tip

Pokud používáte Jetstream spolu s Livewire jako v příkladu a chcete defaultní routy lokalizovat, pak musíte udělat ještě několik menších úprav.

Ze složky /vendor/jetstream/routes/ překopírujte routy k Vašim routám. Soubor pak může vypadat následovně:

<?php

use Illuminate\Support\Facades\Route;
use Laravel\Jetstream\Http\Controllers\CurrentTeamController;
use Laravel\Jetstream\Http\Controllers\Livewire\ApiTokenController;
use Laravel\Jetstream\Http\Controllers\Livewire\PrivacyPolicyController;
use Laravel\Jetstream\Http\Controllers\Livewire\TeamController;
use Laravel\Jetstream\Http\Controllers\Livewire\TermsOfServiceController;
use Laravel\Jetstream\Http\Controllers\Livewire\UserProfileController;
use Laravel\Jetstream\Http\Controllers\TeamInvitationController;
use Laravel\Jetstream\Jetstream;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('lang/{lang}', [App\Http\Controllers\LanguageController::class, 'switchLang'])->name('langSwitcher');

Route::get('/', function () {
    return redirect()->route('login');
});

Route::group([
    'prefix' => "{language}", 
    'where' => ['language' => '[a-zA-Z]{2}']
    ], function () {

    Route::group(['middleware' => config('jetstream.middleware', ['web'])], function () {
        if (Jetstream::hasTermsAndPrivacyPolicyFeature()) {
            Route::get('/terms-of-service', [TermsOfServiceController::class, 'show'])->name('terms.show');
            Route::get('/privacy-policy', [PrivacyPolicyController::class, 'show'])->name('policy.show');
        }

        Route::group(['middleware' => ['auth:sanctum', 'verified']], function () {
            // User & Profile...
            Route::get('/user/profile', [UserProfileController::class, 'show'])
                        ->name('profile.show');

            // API...
            if (Jetstream::hasApiFeatures()) {
                Route::get('/user/api-tokens', [ApiTokenController::class, 'index'])->name('api-tokens.index');
            }

            // Teams...
            if (Jetstream::hasTeamFeatures()) {
                Route::get('/teams/create', [TeamController::class, 'create'])->name('teams.create');
                Route::get('/teams/{team}', [TeamController::class, 'show'])->name('teams.show');
                Route::put('/current-team', [CurrentTeamController::class, 'update'])->name('current-team.update');

                Route::get('/team-invitations/{invitation}', [TeamInvitationController::class, 'accept'])
                            ->middleware(['signed'])
                            ->name('team-invitations.accept');
            }
        });
    });

    Route::group(['middleware' => ['auth:sanctum', 'verified']], function () {
        
        // Dashboard
        Route::get('/dashboard', function () {
            return view('dashboard');
        })->name('dashboard');

    });

});

Pokud bychom upravili například routu pro zobrazení profilu, například takto:

...
// Old value: /user/profile
Route::get('/profile', [UserProfileController::class, 'show'])->name('profile.show');
...

Pokud uděláme výše uvedenou změnu, zobrazení profilu bude dostupné jak na naší routě /profile, tak i můžeme využít výchozí routu /user/profile (načítanou z package – laravel/jetstream/routes/livewire.php). Pokud chceme výchozí routy vypnout, pak musíme upravit JetsreamServiceProvider.php.

...
public function register()
{
    Jetstream::ignoreRoutes();
}
...

Další články

Psaní příspěvků ve WordPress

Psaní příspěvků ve WordPress

V dnešním článku se podíváme na vše potřebné pro tvorbu/psaní článků v redakčním systému WordPress. Konkrétně si...

Autor Pavel Zaněk

Tvořím webové stránky s velkou oblibou v online marketingu - především v odvětví optimalizace pro vyhledávače (SEO). Především se specializuji na tvorbu webových aplikací na míru (PHP framework Laravel s využitím dalších moderních technologií) a na tvorbu webových stránek s použitím redakčního systému Wordpress. Ačkoliv se více zaměřuji na logiku webových aplikací (back end), jsem schopný tvořit i moderní a responzivní šablony v rámci webové grafiky (front end).

20. 11. 2021

0 komentářů

Přidat komentář

Vaše e-mailová adresa nebude zveřejněna.