اكتشف إطار PHP Laravel


الدرس: تحديد الموقع localisation


الصفحة السابقة
عند إنشاء موقع ، غالبًا ما يكون من منظور متعدد اللغات. نتحدث بعد ذلك عن التدويل ( i18n) والتوطين ( L10n) . التدويل يتكون من إعداد تطبيق لجعله مناسبًا للغات المختلفة.  Localisation هي إضافة مكون خاص بلغة ما.
إنه موضوع معقد إلى حد ما ولا يقتصر على ترجمة النصوص ولكنه يؤثر أيضًا على تمثيل التواريخ وإدارة التعددية وأحيانًا التخطيط ...
ماذا لدى Laravel لتقدمه لنا في هذا المجال؟ سنرى ذلك من خلال إتاحة مدونتنا الصغيرة باللغتين الفرنسية والإنجليزية.
i18n  نظرًا لوجود 18 حرفًا بين "i" و "n" للتدويل و L10n نظرًا لوجود 10 أحرف بين "L" و "n" للموقع.

المبدا


الواجهة Lang والفئة Translator
تم تجهيز Laravel بالواجهة الأمامية Lang  لإنشاء سلاسل أحرف بعدة لغات. لكنك تعلم الآن أن الواجهة تخفي فئةً. أي واحد في حالتنا؟
جميع الفئات المعنية بالترجمة موجودة في الملف Illuminate\Translation  :
framework Laravel MVC
المجلد للترجمات
نرى انه منطقي للغاية مزود للقيام بجميع التهيئات.  ملف composer.json  للتبعيات . فئة FileLoader  مع واجهة LoaderInterface  لإدارة ملفات الترجمة.  فئة ArrayLoader  لتحميل الرسائل.  وأخيرا الفئة الرئيسية Translator  التي يتم تنفيذها من قبل الواجهة.
ملفات اللغة
تحدثنا بالفعل عن ملفات اللغة. عندما يتم تثبيت Laravel لدينا هذه الهيكل:
framework Laravel MVC
ملفات اللغة
في البداية يوجد فقط اللغة الإنجليزية ( en) مع 4 ملفات. لإضافة لغة ، قم فقط بإنشاء مجلد جديد ، على سبيل المثال fr  للغة الفرنسية. و بالطبع يجب أن يكون لديك نفس المحتوى.
لن تضطر إلى القيام بكل هذه الترجمة بنفسك! البعض قد فعل ذلك بالفعل مع هذه الحزمة . لقد استخدمناها بالفعل في هذه الدورة. قم بتنزيله ونسخ الملف الفرنسي هنا إذا لم تقم بذلك بالفعل أو إذا قمت بحذفه:
framework Laravel MVC
الملف الفرنسي
لديك بعد ذلك نسختان لغويتان من رسائل Laravel الأساسية.
الآن دعونا نرى كيف تتكون هذه الملفات. خذ الأخف ( pagination.php) :

<?php

return [

    'previous' => '« Previous',
    'next'     => 'Next »',

];
نرى أننا راضون عن إرجاع مجموعة تحتوي على مفاتيح وقيم. لا يمكننا أن نكون أكثر بساطة! إذا أخذنا نفس الملف باللغة الفرنسية:

<?php

return [

    'previous' => '« Précédent',
    'next'     => 'Suivant »',

];
من الواضح أننا نجد نفس المفاتيح مع القيم المكيفة للغة.
طريقة العمل
لدينا بعض الطرق لاختبار واستعادة هذه القيم.
مع

<?php
Lang::get('pagination.previous');
سوف أحصل على:

« Previous
لذلك النسخة الإنجليزية. كيف أحصل على النسخة الفرنسية؟ انظر في الملف config/app.php :

<?php
'locale' => 'en',
هذا هو المكان الذي تم إصلاح اللغة الحالية. تغيير القيمة:

<?php
'locale' => 'fr',
الآن بنفس الكود سوف تحصل على:

« Précédent
من الواضح أننا سنتمكن من تغيير هذه القيمة في الكود بالطريقة setLocale :

<?php
App::setLocale('fr');
حيث سيكون لديك أماكن متعددة في الكود حيث ستسترجع القيم ، فهناك مساعد:

<?php
trans('pagination.previous');
يمكننا أيضًا التحقق من وجود مفتاح مع الطريقة has :

<?php
Lang::has('pagination.previous');
هذه هي الطرق الأساسية التي ستكون كافية لهذا الفصل ، يمكنك العثور على مزيد من المعلومات في الوثائق .

الوسيطة


عندما يختار المستخدم لغة يجب تذكرها ، لذلك سنستخدم الجلسة session لحفظ خياره. عندما يصل المستخدم دون أن يختار بعد ، يجب تقديمه بلغة ، ولكن أي لغة؟ من الممكن الحصول على معلومات من اللغة المستخدمة على مستوى الطلب.
والسؤال الآن هو: أين سنضع الكود للتعامل مع كل هذا؟ انها الوسيطة التي سوف تعتني بها منطقيا. هنا رسم بياني:
framework Laravel MVC
الإدارة المحلية
عندما يصل الطلب ، تتم معالجته بواسطة الوسيطة. سنرى هنا ما إذا كان المستخدم لديه جلسة نشطة مع موقع ، وإذا كان هذا هو الحال ، فسنطبق هذه اللغة ونرسل الطلب إلى الخطوة التالية. إذا لم يكن الأمر كذلك ، فإننا ننظر في رأس الطلب إلى اللغة التي أرسلها المتصفح ، فنحن نحفظها في الجلسة ونطبقها. ثم نرسل الطلب إلى الخطوة التالية.
سننشئ هذه الوسيطة مع Artisan :

php artisan make:middleware Locale
يمكن العثور عليها في مجلد الوسيطة:
framework Laravel MVC
الوسيطة الجديدة
أنا عينته Locale  لتمثيل وظيفته. إليك الكود الذي تم إنشاؤه تلقائيا:

<?php

namespace App\Http\Middleware;

use Closure;

class Locale
{
    /**
     * Handle an incoming request.
     *
     * @param    \Illuminate\Http\Request  $request
     * @param    \Closure  $next
     * @return  mixed
     */
    public function handle($request, Closure $next)
    {
        return $next($request);
    }
}
لذلك دعونا نضيف كودنا:

<?php

namespace App\Http\Middleware;

use Closure;

class Locale
{
    protected $languages = ['en','fr'];
    
    public function handle($request, Closure $next)
    {
        if(!session()->has('locale'))
        {
            session()->put('locale', $request->getPreferredLanguage($this->languages));
        }

        app()->setLocale(session('locale'));

        return $next($request);
    }
}
يتبع الكود بصرامة المخطط الموضح أعلاه.
يبقى لنا فقط أن نطلب من Laravel أن يأخذ هذه الوسيطة في الاعتبار في app/Http/Kernel.php :

<?php
protected $middlewareGroups = [
    'web' => [
        ...
        \App\Http\Middleware\Locale::class,
    ],

    ...
];

التواريخ


ربما لاحظت أن التواريخ لا يتم تقديمها بنفس الطريقة اعتمادًا على اللغات المستخدمة. خاصة بين الفرنسية والإنجليزية. في فرنسا يستخدمون التنسيق jj/mm/aaaa  بينما يستخدم الأمريكيون التنسيق mm/jj/aaaa . لذلك يجب القيام بشيء ما بحيث تظهر تواريخ إنشاء المقالات بالتنسيق الصحيح في كل حالة.
نظرًا لأنه يجب تطبيق هذا التنسيق على جميع التواريخ ، فإن الطريقة الأسهل للقيام بذلك هي إنشاء "ملحقaccessor " على الخاصية. لذلك في كل مرة نقوم باستخراج تاريخ من النموذج ، سنقوم بتنسيقه بشكل عابر. يمكننا القيام بذلك مباشرة في نموذجنا Post . ولكن لتعميم الشيء الذي سنتخيل أننا قد نحتاج إليه في الاساليب الأخرى.
في مثل هذا الموقف ، يكون إنشاء خط مناسبًا لتقديم وظيفة دون المرور بالميراث. سوف نستخدم نسخة مبسطة من نمط تصميم الديكور . في Laravel سوف نسمي هذا "مقدم العرضModel Presenter" .
سنقوم بإنشاء مجلد للعارضين وإنشاء المجلد الذي نحتاجه:
framework Laravel MVC
تقديمه للتواريخ
مع هذا الكود:

<?php

namespace App\Presenters;

use Carbon\Carbon;

trait DatePresenter
{

    public function getCreatedAtAttribute($date)
    {
        return $this->getDateFormated($date);
    }

    public function getUpdatedAtAttribute($date)
    {
        return $this->getDateFormated($date);
    }

    private function getDateFormated($date)
    {
        return Carbon::parse($date)->format(config('app.locale') == 'fr' ? 'd/m/Y' : 'm/d/Y');
    }

}
نرى أنه وفقًا لقيمة اللغة ، سنطبق التنسيق المعتمد للتاريخ المستخرج.
علينا فقط استخدام هذه السمة في النموذج Post  :

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use App\Presenters\DatePresenter;

class Post extends Model
{
    use DatePresenter;

    protected $fillable = ['titre','contenu','user_id'];

    public function user() 
    {
        return $this->belongsTo('App\User');
    }

    public function tags()
    {
        return $this->belongsToMany('App\Tag');
    } 

}

الطريق ووحدة التحكم


يجب أن يكون المستخدم قادرًا على تغيير اللغة عند الرغبة. سنضيف بالتالي الطريق:

<?php
Route::get('language', 'PostController@language');
وهنا هو تعديل وحدة التحكم:

<?php

namespace App\Http\Controllers;

use App\Repositories\PostRepository;
use App\Repositories\TagRepository;
use App\Http\Requests\PostRequest;

class PostController extends Controller
{

    protected $postRepository;

    protected $nbrPerPage = 4;

    public function __construct(PostRepository $postRepository)
    {
        $this->middleware('auth', ['except' => ['index', 'indexTag', 'language']]);
        $this->middleware('admin', ['only' => 'destroy']);

        $this->postRepository = $postRepository;
    }

    public function index()
    {
        $posts = $this->postRepository->getWithUserAndTagsPaginate($this->nbrPerPage);
        $links = $posts->render();

        return view('posts.liste', compact('posts', 'links'));
    }

    public function create()
    {
        return view('posts.add');
    }

    public function store(PostRequest $request, TagRepository $tagRepository)
    {
        $inputs = array_merge($request->all(), ['user_id' => $request->user()->id]);

        $post = $this->postRepository->store($inputs);

        if(isset($inputs['tags'])) 
        {
            $tagRepository->store($post, $inputs['tags']);
        }

        return redirect(route('post.index'));
    }

    public function destroy($id)
    {
        $this->postRepository->destroy($id);

        return redirect()->back();
    }

    public function indexTag($tag)
    {
        $posts = $this->postRepository->getWithUserAndTagsForTagPaginate($tag, $this->nbrPerPage);
        $links = $posts->render();

        return view('posts.liste', compact('posts', 'links'))
        ->with('info', trans('blog.search') . $tag);
    }

    public function language()
    {
        session()->set('locale', session('locale') == 'fr' ? 'en' : 'fr');

        return redirect()->back();
    }

}
نرى وظيفة جديدة language . من ناحية أخرى indexTag  ، تم تعديل الوظيفة لتأخذ في الاعتبار اللغات.
لذلك مع رابط

.../language
سنتمكن من التبديل من لغة إلى أخرى.

تحديد الموقع Localisation


نحتاج أيضًا إلى إنشاء ملفات الترجمة:
framework Laravel MVC
ملفات الموقع
مع هذا المحتوى لـ resources/lang/en/blog.php  :

<?php

return [
    'site' => 'My nice site',
    'title' => 'My nice blog',
    'creation' => 'Create an article',
    'login' => 'Login',
    'logout' => 'Logout',
    'delete' => 'Delete this post',
    'confirm' => 'Really delete this post ?',
    'on' => 'on',
    'search' => 'Results for tag : '
];
وهذا واحد للموارد / lang / fr / blog.php :

<?php

return [
    'site' => 'Mon joli site',
    'title' => 'Mon joli blog',
    'creation' => 'Créer un article',
    'login' => 'Se connecter',
    'logout' => 'Déconnexion',
    'delete' => 'Supprimer cet article',
    'confirm' => 'Vraiment supprimer cet article ?',
    'on' => 'le',
    'search' => 'Résultats pour la recherche du mot-clé : '
];

العرض


علينا فقط تعديل العرض حتى تنجح. أولاً القالب ( views/template/php) :

<!DOCTYPE html>
<html lang="fr">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>{ { trans('blog.site') }}</title>
        {!! Html::style('https://netdna.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css') !!}
        {!! Html::style('https://netdna.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css') !!}
        <!--[if lt IE 9]>
            { { Html::style('https://oss.maxcdn.com/libs/html5shiv/3.7.2/html5shiv.js') }}
            { { Html::style('https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js') }}
        <![endif]-->
        <style> textarea { resize: none; } </style>
    </head>
    <body>
        <header class="jumbotron">
            <div class="container">
                <h1 class="page-header">{!! link_to_route('post.index', trans('blog.title')) !!}</h1>
                @yield('header')
            </div>
        </header>
        <div class="container">
            @yield('contenu')
        </div>
    </body>
</html>
تم تعديله لترجمة عنوان الموقع في العنوان:

<title>{ { trans('blog.site') }}</title>
والعنوان / الرابط على الصفحة:

<h1 class="page-header">{!! link_to_route('post.index', trans('blog.title')) !!}</h1>
وهنا المدونة المحولة ( views/posts/liste.blade.php) :

@extends('template')

@section('header')
    <div class="btn-group pull-right">
        {!! link_to('language', session('locale') == 'fr' ? 'English' : 'Français', ['class' => 'btn btn-primary']) !!}
        @if(Auth::check())
            {!! link_to_route('post.create', trans('blog.creation'), [], ['class' => 'btn btn-info']) !!}
            {!! link_to('logout', trans('blog.logout'), ['class' => 'btn btn-warning']) !!}
        @else
            {!! link_to('login', trans('blog.login'), ['class' => 'btn btn-info pull-right']) !!}
        @endif
    </div>
@endsection

@section('contenu')
    @if(isset($info))
        <div class="row alert alert-info">{ { $info }}</div>
    @endif
    {!! $links !!}
    @foreach($posts as $post)
        <article class="row bg-primary">
            <div class="col-md-12">
                <header>
                    <h1>{ { $post->titre }}
                        <div class="pull-right">
                            @foreach($post->tags as $tag)
                                {!! link_to('post/tag/' . $tag->tag_url, $tag->tag, ['class' => 'btn btn-xs btn-info']) !!}
                            @endforeach
                        </div>
                    </h1>
                </header>
                <hr>
                <section>
                    <p>{ { $post->contenu }}</p>
                    @if(Auth::check() and Auth::user()->admin)
                        {!! Form::open(['method' => 'DELETE', 'route' => ['post.destroy', $post->id]]) !!}
                            {!! Form::submit(trans('blog.delete'), ['class' => 'btn btn-danger btn-xs ', 'onclick' => 'return confirm(\'' . trans('blog.confirm') . '\')']) !!}
                        {!! Form::close() !!}
                    @endif
                    <em class="pull-right">
                        <span class="glyphicon glyphicon-pencil"></span> { { $post->user->name . ' ' . trans('blog.on') . ' ' . $post->created_at }}
                    </em>
                </section>
            </div>
        </article>
        <br>
    @endforeach
    {!! $links !!}
@endsection
التعديلات تتعلق بجميع النصوص. سوف اسمح لك بتحليل الكود. والنتيجة هي أنه في الفرنسية لا شيء يتغير مقارنة بما كان لدينا. عن طريق سلبيات عندما نتحول إلى اللغة الإنجليزية من خلال النقر على زر "اللغة الإنجليزية" :
framework Laravel MVC
زر تغيير اللغة
الأزرار تتغير بالفعل مظهرها:
framework Laravel MVC
أزرار الإنجليزية
عنوان الصفحة باللغة الإنجليزية:
framework Laravel MVC
عنوان الصفحة باللغة الإنجليزية
العنوان / الرابط باللغة الإنجليزية:
framework Laravel MVC
العنوان باللغة الإنجليزية
يتم تنسيق التواريخ بشكل صحيح:
framework Laravel MVC
تاريخ التنسيق
زر الحذف هو أيضا باللغة الفرنسية:
framework Laravel MVC
زر الحذف
وكذلك الرسالة:
framework Laravel MVC
رسالة التنبيه باللغة الإنجليزية
يتم تكييف رسالة البحث حسب العلامة:
framework Laravel MVC
رسالة البحث عن طريق العلامة
من الضروري في بعض الأحيان تفريغ المجلد storage/framework/views  لإزالة ذاكرة التخزين المؤقت لطرق العرض. ومع ذلك ، لا تقم بحذف الملف .gitignore .

أسماء الضوابط


سيكون من الضروري أيضًا التعامل مع طريقة عرض مقال ما ، لكن هذا هو نفس المبدأ تمامًا. ومع ذلك ، هناك عنصر يجب أخذه في الاعتبار ، هو اسم عنصر التحكم في الادخال. تتذكر أننا اسميناهم titre، contenu  و tags . هذه الأسماء غير مرئية طالما لم يكن هناك اهتمام بالتحقق من الصحة ، ثم يتم إرسالها في نص الخطأ. يصعب رؤية العنوان "حقل مطلوب" باللغة الإنجليزية. إذا كيف يمكنك أن تفعل ذلك؟ 
إذا نظرت إلى الملف resources/lang/fr/validation.php  ، ستجد هذا الجدول:

<?php
'attributes' => [
    "name" => "Nom",
    "username" => "Pseudo",
    ...
],
نحن هنا نطابق اسم السمة بإصدارها اللغوي للحصول على شيء يتوافق مع رسالة الخطأ. في وضعنا هذا ، نظرًا لأننا قمنا بفرز اسم عناصر التحكم ، يجب أن نضيف النسخة الإنجليزية في الملف  resources/lang/en/validation.php  :

<?php
'attributes' => [
    "titre" => "Title",
    "contenu" => "Content",
],
من الواضح إذا كنا قد خططنا لأسماء إنجليزية ، وهو ما نقوم به بشكل عام ، لكان علينا القيام بالعكس ، وتقديم تسميات فرنسية في الملف  resources/lang/fr/validation.php .
الغرض من هذا الفصل هو إظهار كيفية تحديد موقع واجهة صفحة الويب. إذا كان الأمر يتعلق بتكييف المحتوى وفقًا للغة ، فهذه قصة أخرى ومن الضروري التدخل على مستوى عنوان url . في الواقع ، هناك مبدأ أساسي في الويب وهو أنه بالنسبة لعنوان url معين ، نرجع دائمًا نفس المحتوى.

في الخلاصة


  • Laravel لديه الأدوات الأساسية للترجمة.
  • يجب عليك إنشاء العديد من ملفات الترجمة حيث توجد لغات للمعالجة.
  • يجب أن تتم الترجمة على مستوى البرامج الوسيطة.
  • تنسيق التاريخ يمكن أن يتم بسهولة مع middleware .
  • يجب توفير النسخة اللغوية من السمات.