Đa ngôn ngữ(Localization-L10n) trong ứng dụng web với Laravel

Chào các bạn chủ để tìm hiểu phần này của mình sẽ là về chủ đề đa ngôn ngữ trong xây dựng ứng dụng ứng dụng web với framwork Larvel hay còn gọi là L10N (Localization)

Web đa ngôn ngữ là gì?

Web đã ngôn ngữ là web mà mình có thể xem dưới nhiều loại ngôn ngữ khác nhau, phù hợp với bản ngữ của mình và ngôn ngữ hiển thị của trang web do người dùng lựa chọn. Đa ngôn ngữ ở đây theo mình hiểu không có nghĩa là tại mỗi thể hiện của trang web bạn hiển thị cho người dùng với đồng thời nhiều ngôn ngữ khác nhau mà với tùy chọn ngôn ngữ của người dùng thì trang web sẽ phải hiện thị với đúng ngữ ngôn ngữ đó.

Mình có thể ví dụ ra một website đa ngôn ngữ ví dụ như wiki khá nổi tiếng rồi chắc chúng ta đều rõ hay như viblo.asia nơi bạn có thể tùy chọn 3 ngôn ngữ khác nhau như tiếng Anh, Nhật hay Việt.

Ví dụ trực quan việc chuyển đổi giữa các ngôn ngữ như sau

1.png

Với cùng nội dung thể hiện cần chuyển đổi giữa các ngôn ngữ hợp lý theo tùy chọn của người dùng.

Việc xây dựng web đa ngôn ngữ làm cho ứng dụng web của chúng ta sẽ không giới hạn phạm vi của người dùng với một ngôn ngữ cụ thể nào cả, làm cho ứng dụng trở nên chuyên nghiệp và thuận tiện cho người dùng

Localization trong laravel

Vấn Localization trong Laravel được giải quyết bằng việc là mình sẽ sử dụng array files được lưu trữ bên trong đường dẫn app/lang.

Files ngôn ngữ cho từng ngôn ngữ cụ thể sẽ được lưu trong thư mục con của nó ví dụ

    /app
        /lang
            /en
                label.php
            /vn
               /label.php

Một file ngôn sẽ trả về một mảng key-value

label.php

    <?php

     return array(
        'login' => 'Login',
        'register' => 'Register,
      );
  • Việc lựa chọn key ở đây cần phải cẩn thận do nếu mình sử dụng value tương ứng với key mà key trong files ngôn ngữ không tồn tại thì giá trị này vẫn được trả về. Mà laravel sẽ không tự động chuyển sang ngôn ngữ dự phòng (fallback language)

  • Default language sẽ được được set trong app/config/app.php. Ngôn ngữ sẽ được ứng dụng web của mình hiển thị được định nghĩa thông qua

      App::setLocale('vn');
    
  • Ví dụ mình muốn ngôn ngữ web của mình là tiếng Việt mình đặt tùy chọn là vn
  • Giờ mình có thể truy vấn từng dòng dữ liệu trong language files thông qua sử dụng hàm get của class Lang được cung cấp bởi Laravel. Tham số truyền vào hàm get này gồm có 2 phần: thứ nhất đó là tên của files lang ví dụ như label mà mình đã ví dụ ở trên, phần thứ hai có thể coi là phần mở rộng đó là key tại dòng được truy vấn tới

      echo Lang::get('label.login);
    

Kết quả mình sẽ thu được đó là: Login

  • Mình cũng có thể sử dụng trực tiếp phương thức trans để get giá trị của key trong language file. trans() thương đương Lang::get()

  • key có thể chỉ truy vấn tới một value hoặc cũng có thể là một mảng và lúc này trong mảng lại chứa các keyvalue ở cấp thấp hơn cho mình truy vấn. Ví dụ trong file label.php

      'title' => [
          'login' => 'Login page',
          'register' => 'Register page',
      ]
    
    • Giờ mình sẽ truy vấn thằng 'Login page' như sau

        trans('label.title.login');
      
  • Mình có thể linh đông nội dung được truy vấn ra thay vì chỉ là những đoạn text cố định bằng cách truyền vào các giá trị thay thế của riêng mình

      //messages.php
      <?php
          return array(
              'welcome' => 'Welcome, :username'
          );
      ?>
    
    • Giá trị thay thê này sẽ được truyền vào trong hàm get hoặc trans với tham số thứ 2 dưới dạng mảng. Với key là tên của giá trị cần thay thế và value là nội dung mình muốn thay thế vào đó

        trans('messages.welcome', ['username' => $username]);
      
    • Ví dụ biến $usename = 'Duy' thì mình sẽ nhận được 'Welcome, Duy'

  • Mình có một vấn đề nho nhỏ khi gặp phải đó là khi hiển thị đếm số lượng comment cho một bài viết thì mình có thể dùng

          trans(comment, ['num' => $num]);
    
    • Tuy nhiên dù là giá trị $num của mình có là 1 hay 10 thì nội dung mình thu được cũng chỉ là 1 comment hay 10 comment. Vấn đề này sẽ được giải quyết khi mình sử dụng "pipe" character:

        <?php
            return array(
                'comment' => ':num comment|:num comments'
            );
        ?>
      
    • Mình sẽ sử dụng Lang::choice() để truy vấn với dạng dữ liệu này

         echo Lang::choice('messages.comment', $numComment, ['num' => $numComment]);
      
    • Mình sẽ nhận được kết quá là '1 comment' nếu $numComment = 1 và comment sẽ được replace bởi comments nếu $numComment khác 1.

    • Mình cũng có thể tùy chỉnh hiển thị theo giá trị mà mình truyền vào thay vì chỉ có 1 và khác 1 như sau

        'comment' => '{0} There are none|[1,19] There are some|[20,Inf] There are many'
      

Mình đã có được cái nhìn tổng quát và các hàm cơ bản mà thường làm việc với Translator trong Laravel rồi. Giờ chúng ta sẽ cùng đi vào một bài toán cụ thể

Mình muốn tạo 1 trang đơn giản với các thành phần nội dung mang tính giới thiệu ví dụ như login, register, login form đơn giản mà mình có thể chuyển đổi qua lại giữa ngôn ngữ ví dụ như tiếng Việt(đăng nhập) hoặc tiếng Anh(Login) chẳng hạn

  • Mình sẽ tạo một app laravel mới.

  • Trong resources/lang tạo 2 folder là vnen

  • Trong envn cùng tạo 2 files với tên label.php

      //en/label.php
      <?php
    
      return [
          'laravel' => 'Laravel',
          'login' => 'Login',
          'register' => 'Register',
          'doc' => 'Documentation',
          'new' => 'News',
          'email' => 'E-Mail Address',
          'password' => 'Password',
          'remember_me' => 'Remember Me',
          'forgot_password' => 'Forgot Your Password?',
          'lang' => [
              'en' => 'English',
              'vi' => 'Tiếng Việt',
              'jp' => '日本語',
          ],
      ];
    
    •   //vn/label.php
        <?php
      
        return [
            'laravel' => 'Laravel',
            'login' => 'Đăng nhập',
            'register' => 'Đăng ký',
            'doc' => 'Tài liệu tham khảo',
            'new' => 'Tin tức',
            'email' => 'Địa chỉ email',
            'password' => 'Mật khẩu',
            'remember_me' => 'Ghi nhớ đăng nhập',
            'forgot_password' => 'Quên mật khẩu',
            'lang' => [
                'en' => 'English',
                'vi' => 'Tiếng Việt',
                'jp' => '日本語',
            ],
        ];
      
  • Mình sử dụng php artisan make:auth được hỗ trợ bởi Laravel để tạo ra các route và control, view đơn giản với việc đăng nhập đăng ký

  • Đây sẽ là view trong trang home:

      <body>
          <div class="flex-center position-ref full-height">
              @if (Route::has('login'))
                  <div class="top-right links">
                      <form action="{{ route('switchLang') }}" class="form-lang" method="post">
                          <select name="locale" onchange='this.form.submit();'>
                              <option value="en">{{ trans('label.lang.en') }}</option>
                              <option value="vi"{{ Lang::locale() === 'vi' ? 'selected' : '' }}>{{ trans('label.lang.vi') }}</option>
                              <option value="jp"{{ Lang::locale() === 'jp' ? 'selected' : '' }}>{{ trans('label.lang.jp') }}</option>
                          </select>
                          {{ csrf_field() }}
                      </form>
                      <a href="{{ url('/login') }}">{{ Lang::get('label.login') }}</a>
                      <a href="{{ url('/register') }}">{{ Lang::get('label.register') }}</a>
                  </div>
              @endif
    
              <div class="content">
                  <div class="title m-b-md">
                      {{ trans('label.laravel') }}
                  </div>
                  <div class="links">
                      <a href="https://laravel.com/docs">{{ trans('label.doc') }}</a>
                      <a href="https://laravel-news.com">{{ trans('label.new') }}</a>
                  </div>
              </div>
          </div>
      </body>
    
  • Phần form select được sử dụng để người dùng có thể tùy chọn switch ngôn ngữ hiển thị

  • Ý tưởng ở đây là mình sẽ sử dụng middleware để handle các request đến với ứng dụng của mình trước khi được đưa vào xử lý trong controller.

    • Mình sẽ tạo một class middlware là Locale.php

        <?php
      
        namespace App\Http\Middleware;
      
        use Closure;
        use Lang;
        use Session;
      
        class Locale
        {
            public function handle($request, Closure $next)
            {
                if (!Session::has('locale')) {
                    Session::put('locale', config('app.locale'));
                }
      
                Lang::setLocale(Session::get('locale'));
      
                return $next($request);
            }
        }
      
    • Khai báo middleware trong app/Http/Kernel.php

        // protected $routeMiddleware
      
            protected $routeMiddleware = [
            //...
            'localization' => \App\Http\Middleware\Locale::class,
        ];
      
    • Khi một request được gửi tới mình sẽ kiểm tra giá trị locale có được set trong Session chưa? Nếu có rồi thì sử dụng hàm setLocale của class Lang để lựa chọn ngôn ngữ mà mình sẽ sử dụng. Ở đây ngôn ngữ mặc định của mình là tiếng Anh (en) được khai báo trong config/app.php

  • Bây giờ mình sẽ sử dụng tới phần tùy chọn ngôn ngữ mà đã nói trong view phía trên. Tạo class LangController để xử lý phần này

      // LangController.php
      <?php
    
      namespace App\Http\Controllers;
    
      use Illuminate\Http\Request;
      use Session;
    
      class LangController extends Controller
      {
          public function postLang(Request $request)
          {
              Session::set('locale', $request->locale);
              return redirect()->back();
          }
      }
    
  • Tạo route

      //route.php
    
      Route::group(['middleware' => 'localization', 'prefix' => Session::get('locale')], function() {
    
          Auth::routes();
    
          Route::get('/home', '[email protected]');
    
          Route::post('/lang', [
              'as' => 'switchLang',
              'uses' => '[email protected]',
          ]);
    
          Route::get('/', function () {
              return view('welcome');
          });
      });
    
    • Ở đây mình sử dụng middleware là localization, các request tới nhẫn url trong group này sẽ được xử lý với middlewear mà mình đã viết ở phần trên

Kết quả thu được

3.png

4.png

Kết luận

  • Mong là bài viết của mình có hữu ích với mọi người

  • Cảm ơn vì đã dành thời gian đọc hết bài viết của mình. Mọi chia sẻ và góp ý các bạn bình luận cho mình tìm hiểu với nhé.