Laravel5.7: 多言語対応

グローバルナビのドロップダウンメニューで、日本語と英語を切り替えられるようにします。

![001.png](https://qiita-image-store.s3.amazonaws.com/0/27816/5ad1a950-152b-fc3b-04be-3fa837a849de.png)

# 親記事
[Laravel 5.7で基本的なCRUDを作る - Qiita](https://qiita.com/sutara79/items/ef30fcdfb7afcb2188ea)


# 翻訳文字列を用意する
:link: readouble.com: [翻訳文字列の定義](https://readouble.com/laravel/5.7/ja/localization.html#defining-translation-strings)

## 短縮キー
ログイン関連の英語メッセージは初めから用意されているので、それを元に日本語メッセージを作ります。
量が多い`validation.php`は、[先人が作ったv5.1のもの](https://gist.github.com/syokunin/b37725686b5baf09255b)に加筆しました。
下記のリンク先にある4つのファイルを作ってください。
:page_facing_up: [resources/lang/ja](https://github.com/sutara79/demo-laravel-crud/tree/5.7/resources/lang/ja)

## 翻訳文字列のキー
今回のアプリではUI部分だけを多言語対応すればいいので、仰々しい短縮キーを使うやり方ではなく、より簡単な翻訳文字列をキーとして使うやり方を主に使います。
下記のファイルを作ってください。
:page_facing_up: [resources/lang/ja.json](https://github.com/sutara79/demo-laravel-crud/blob/5.7/resources/lang/ja.json)

```diff:ビューでの使い方
-   Edit
+   {{ __('Edit') }}
```


# 言語切替の方針
初めてのアクセスでは、リクエストヘッダの`Accept-Language`に指定された言語で表示します。
その情報はセッションに保存し、以後はセッションを元に表示言語を切り替えます。

ユーザーが手動で言語を切り替えられるようにするために、今回のアプリではGETパラメータを使います。

- `http://foo.com?lang=ja`: 日本語表示を強制
- `http://foo.com?lang=en`: 英語表示を強制

下記の記事によれば、多言語対応はGETパラメータよりもサブディレクトリの方が良さそうです。
しかし、今回のアプリはどの言語でもコンテンツは共通で、多言語対応はUI部分のみなので、お手軽なGETパラメータを使うことにします。
:link: [海外サイト構築で注目するべき3つのポイント : ビジネスとIT活用に役立つ情報](http://www.asobou.co.jp/blog/web/overseas)


# 多言語対応はミドルウェアで行う
:link: readouble.com: [ミドルウェア](https://readouble.com/laravel/5.7/ja/middleware.html)

表示言語の判断は、全てのページで実行したいです。
そのためにミドルウェアとしてまとめます。

## ミドルウェアを作る
`CheckLocale`という名前のミドルウェアを作ります。

```text:PowerShell
# ミドルウェアを作る。
# app/Http/Middleware/CheckLocale.php が生成される。
> php artisan make:middleware CheckLocale
```

生成したファイルの内容を下記のようにします。
:page_facing_up: [app/Http/Middleware/CheckLocale.php](https://github.com/sutara79/demo-laravel-crud/blob/5.7/app/Http/Middleware/CheckLocale.php)

### intlの有効化
PHPの[locale_accept_from_http()](http://php.net/manual/ja/locale.acceptfromhttp.php)関数を使うには、国際化用拡張モジュール(intl)を有効にしなければなりません。
WindowsのXAMPPの場合は下記のようにアンコメントしてApacheを再起動します。

```diff:xampp\php\php.ini(892行目)
- ;extension=intl
+ extension=intl
```

### セッションはファイルに保存する
:link: readouble.com: [HTTPセッション](https://readouble.com/laravel/5.7/ja/session.html)

公式解説ではmemcachedやredisを勧めていますが、とりあえずデフォルトのファイルに保存するようにします。
以前はクッキーに保存していました。
しかしバリデーションにおいて、バリデーションが通らずに前のページに戻って入力値を復元する際、セッションをクッキーに保存していると、入力値の文字数が多すぎるとクッキーの4KB制限を超えてしまい、入力値を復元できなくなるというトラブルに遭遇しました。
下記の通り、[readouble.com](https://readouble.com)管理人の川瀬さんから助言をいただきました。

> 日本語（UTF−８）をクッキーに保存する場合にエンコードされます。そのエンコードされた値が更に暗号化されるため、短いメッセージのつもりでもクッキーの最大長である４KBを超えてしまいます。そのため、セッションのデフォルト保存先をファイルにしているフレームワークが多いです。

:link: https://twitter.com/HiroKws/status/887597436587220992


## webミドルウェアグループに追加する
:link: readouble.com: [ミドルウェアグループ](https://readouble.com/laravel/5.7/ja/middleware.html#middleware-groups)

下記のようにすることで、WebのUIの全てのルートで`CheckLocale`を実行してくれます。

```diff:app/Http/Kernel.php
     protected $middlewareGroups = [
         'web' => [
             // 注意! 末尾に追加すること。少なくともStartSessionよりも後に。
+            \App\Http\Middleware\CheckLocale::class,
         ],
```


# 言語切り替えのUIを用意する
以上の設定で、GETパラメータを手動入力することで言語を切り替えられるようになりましたが、面倒なので切り替え用のリンクをグローバルナビに設置します。

```html
<a href="http://foo.com?lang=ja">日本語</a>
```

上の`http://foo.com?lang=ja`ようなURLを生成する処理は少し複雑なので、ヘルパー関数で行うようにします。
ヘルパー関数は[以前の記事](https://qiita.com/sutara79/items/9309824806d99f0fce6a#%E3%83%98%E3%83%AB%E3%83%91%E3%83%BC%E9%96%A2%E6%95%B0%E3%81%AB%E3%81%99%E3%82%8B)で既に作っています。

## ヘルパー関数を作る
下記のように`my_locale_url()`関数を`app/helpers.php`に追加してください。

:page_facing_up: [app/helpers.php(16行目以降)](https://github.com/sutara79/demo-laravel-crud/blob/5.7/app/helpers.php#L16)

:link: Laravel API: [Request::fullUrl](https://laravel.com/api/5.7/Illuminate/Http/Request.html#method_fullUrl)
:link: [[PHP] URLをパースしていろいろ取得する方法 | 制作メモ | 560DESIGNS](http://www.560designs.com/memo/734.html)

## ビューでヘルパー関数を使う
ドロップダウンメニューをレイアウトに追加します。

```diff:resources/views/layouts/my.blade.php
                         @endguest
+                        <li class="nav-item dropdown">
+                            <a class="nav-link dropdown-toggle" href="javascript:void(0)" id="dropdown-lang" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                                {{ __('locale.'.App::getLocale()) }}
+                            </a>
+                            <div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdown-lang">
+                                @if (!App::isLocale('en'))
+                                    <a class="dropdown-item" href="{{ my_locale_url('en') }}">
+                                        {{ __('locale.en') }}
+                                    </a>
+                                @endif
+                                @if (!App::isLocale('ja'))
+                                    <a class="dropdown-item" href="{{ my_locale_url('ja') }}">
+                                        {{ __('locale.ja') }}
+                                    </a>
+                                @endif
+                            </div>
+                        </li>
                     </ul>
```

最後に、`locale.en`と`locale.ja`の翻訳を収めたファイルを作ります。
`ja`ではなく`en`フォルダ内に設置することに注意してください。

```php:resources/lang/en/locale.php
<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Localization Language Lines
    |--------------------------------------------------------------------------
    |
    */

    'en' => 'English',
    'ja' => '日本語',

];
```

これで、手軽に言語を切り替えられるようになりました。

![001.png](https://qiita-image-store.s3.amazonaws.com/0/27816/5ad1a950-152b-fc3b-04be-3fa837a849de.png)