【Laravel】サブドメインで言語を切り替える

こんにちは。フリーランス・コンサルタント&エンジニアの 九保すこひ です。

さてさて、昨今の話題はコロナウィルスの影響を受けた制限ばかりでしたが、先日のニュースで「2022年6月から入国検査がさらに緩和される」と聞き、以前のインバウンド旅行客が少しずつ帰ってくるのでは、感じています。

そして、この流れもあって「Laravel + DeepL で自動翻訳機能をつくる」という記事を公開したのですが、実はもうひとつ多言語化に必要なのものがあります。

それが・・・・・・

言語の切り替え

です。

そして、同じURLで言語ごとに内容を切り替えてもいいのですが、「うーん、それって SEO 的に OK なのかな…🤔」という疑問があったので、今回は、

サブドメインごとに言語を割り振る

という実装をしてみます。

つまり、以下のような形ですね。

  • example.com: メイン(日本語)
  • en.example.com: 英語
  • zh.example.com: 中国語
  • ko.example.com: 韓国語
  • vi.example.com: ベトナム語

ぜひ何かのお役に立てたら嬉しいです。😄✨

「姪っ子の英語力に感動😄」

開発環境: Laravel 9.x、nginx 1.18.0、Ubuntu 20.04

前提として

今回は便宜上、l9x.testというドメインを使って開発を進めていきます。
皆さんはご自身のお好きなドメインに置き換えてくださいね。

nginx の設定

では、まずはウェブサーバーのnginxの設定です。
server_nameを正規表現で以下すべてのドメインに対応させます。

  • l9x.test
  • en.l9x.test
  • zh.l9x.test
  • ko.l9x.test
  • vi.l9x.test
server {
    listen 80;
    listen [::]:80;
    server_name "~^((en|zh|ko|vi)\.)?l9x\.test$";
    
    # 省略
}

※ テストなので「HTTP」接続のみをご紹介していますが、本来は443ポートの「HTTPS」バージョンも設定してください。
※ もしLet's encryptcertbot)でHTTPS対応させる場合、正規表現には対応できていないようでした。その際はめんどうですが、ひとつずつサブドメインをセットしてください。

そして、設定を保存したら以下のコマンドでnginxを再起動します。

sudo systemctl restart nginx

ドメイン名とIPアドレスを一致させる(hosts)

次に、(開発環境の場合)ブラウザで「l9x.test」などのドメインにアクセスできるようドメイン名を一致させる必要があります。これは、OSごとに設定方法が違うので以下の「Google検索」を参照してください。

※ 本番環境の場合はドメインのDNS設定になります。

なお、私が使っている(少数派の)Ubuntuの場合は、以下のコマンドでhostsファイルを開きます。

sudo gedit /etc/hosts

そして、中身を以下のようにして保存します。

127.0.1.1  l9x.test
127.0.1.1  en.l9x.test
127.0.1.1  zh.l9x.test
127.0.1.1  ko.l9x.test
127.0.1.1  vi.l9x.test

これで、ブラウザから「l9x.test」などのドメインにアクセスができるようになりました。

ドメイン名で言語を自動的に切り替える

では、ここからはLaravel側の実装になります。
アクセスしてきたドメインごとに言語をセットできるようにしていきましょう。

app/Providers/AppServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Support\Arr;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    // 省略

    public function boot()
    {
        $host_name = request()->getHost();
        $locales = [
            'l9x.test' => 'ja',
            'en.l9x.test' => 'en',
            'zh.l9x.test' => 'zh',
            'ko.l9x.test' => 'ko',
            'vi.l9x.test' => 'vi',
        ];
        $locale = Arr::get($locales, $host_name, 'ja'); // 👈 見つからないときは日本語
        app()->setLocale($locale);
    }
}

※ なお、本来は今後のことを考えて$localesはどこか別の場所(Enumやモデルの定数として一元管理しておいた方がいいでしょう)

コードを見ていただくと分かるとおり、ドメイン名を使って2文字の言語コードを取得し、それをセットしているだけです。

なお、もし言語が見つからない場合は日本語(ja)になるようにしています。

では、これでドメインごとに言語設定ができるようになりました!

各言語ファイルをつくる

続いて、各言語の翻訳ファイルの作成です。
Laravelに標準で搭載されている言語機能を使ってlangフォルダ以下にそれぞれファイルを作成しましょう。

lang/ja/message.php

<?php

return [
    'greeting' => 'こんにちは!',
];

lang/en/message.php

<?php

return [
    'greeting' => 'Hello!',
];

lang/zh/message.php

<?php

return [
    'greeting' => '你好!',
];

lang/ko/message.php

<?php

return [
    'greeting' => '안녕하세요!',
];

lang/vi/message.php

<?php

return [
    'greeting' => 'xin chào!',
];

はい❗
これで設定はすべて完了です。

お疲れ様でした。✨😄👍

テストしてみる

では、実際にうまくいっているかPHPUnitを使ってテストしていきましょう!
まずは次のルートを追加してください。

routes/web.php

Route::get('locale_via_domain', fn() => trans('message.greeting'));

これは、例えば「http://l9x.test/locale_via_domain」へアクセスすると、「こんにちは!」と表示されるものです。

もちろん、ドメインごとに以下のように表示が切り替わります。

  • l9x.test: こんにちは!
  • en.l9x.test: Hello!
  • zh.l9x.test: 你好!
  • ko.l9x.test: 안녕하세요!
  • vi.l9x.test: xin chào!

続いて、以下のコマンドでPHPUnitのテストファイルを作成してください。

php artisan make:test LocaleTest

すると、ファイルが作成されるので中身を次のように変更します。

tests/Feature/LocaleTest.php

<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class LocaleTest extends TestCase
{
    public function test_can_see_greetings()
    {
        $locales = [
            'l9x.test' => 'ja',
            'en.l9x.test' => 'en',
            'zh.l9x.test' => 'zh',
            'ko.l9x.test' => 'ko',
            'vi.l9x.test' => 'vi',
        ];

        foreach ($locales as $domain => $locale) {

            $url = 'http://'. $domain .'/locale_via_domain';
            $content = file_get_contents($url);
            $greeting = trans('message.greeting', [], $locale);
            $this->assertEquals($content, $greeting);

        }
    }
}

※ ちなみにLaravelが用意してくれている$response = $this->get($url);というコードを使ってしまうと、実行側の言語の影響を受けてしまうようだったので、file_get_contents()を使っています。

では、実際にチェックしてみましょう❗

php artisan test --filter LocaleTest

どうなったでしょうか・・・・・・

はい❗
うまくテストが通りました。

成功です😄✨

実際の使用例

私が個人的に運営している「街角コレクション」を「英語」「中国語」に対応させました。

興味のある方はぜひご覧ください❗

ちなみに:日本語だけにしたいページがあるとき

ちなみに、「このページだけは日本語だけにしたいな(翻訳を提供できないな)」という場合は、ミドルウェアで対応できます。

例えば、「en.example.com」や「zh.example.com」でアクセスしてきたときは、通常の「example.com」へ強制的にリダイレクトするわけですね。

このような場合の手順は次のとおりです。
まず以下のコマンドでミドルウェアを作成します。

php artisan make:middleware OnlyJapaneseMiddleware

するとファイルが作成されるので中身を次のように変更します。

app/Http/Middleware/OnlyJapaneseMiddleware.php

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class OnlyJapaneseMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {
        $locale = app()->getLocale();

        if($locale !== 'ja') {

            $baseUrl = config('app.url');
            $uri = $request->getRequestUri();
            $url = $baseUrl . $uri;

            return redirect($url);

        }

        return $next($request);
    }
}

そして、ミドルウェアをLaravelへ登録します。

app/Http/Kernel.php

// 省略

protected $routeMiddleware = [

    // 省略

    'only_japanese' => OnlyJapaneseMiddleware::class, // 👈 ここ
];

そして、ルートやコントローラーで「only_japanese」をセットしてやればOKです。

routes/web.php

Route::get('test', [TestController::class, 'index'])
    ->middleware('only_japanese') // 👈 ここ
    ->name('test.index');

企業様へのご提案

今回の機能を使うと、「サイトはひとつで言語ごとにサイトを用意する」ことができるようになります。

さらにサブドメインを使って言語の切り替えをしているので、閲覧する側にも分かりやすく、Laravel側での対応コードを極力減らすことができます(つまり、開発スピードを上げることができます)

また、サブドメインを作成するだけで言語の増減をさせることもでき、よりフレキシブルな体制を準備することもできます。

もしサブドメインを利用した多言語化をご希望でしたら、いつでもお問い合わせからご連絡ください。

お待ちしております。😄✨

開発のご依頼お待ちしております
開発のご依頼はこちらから: お問い合わせ
どうぞよろしくお願いいたします! by 九保すこひ

おわりに

ということで、今回は「サブドメインを使って多言語化」を実装してみました。

近年は、私の地元にも中国や韓国料理だけでなくベトナム料理のお店を目にすることも増えました。つまり、よりグローバル化が進んでいるのは間違いないので、サイトの多言語化はより重要になっていくんじゃないかと感じています。(スーパー翻訳機ができたら話は別ですが…まだ完璧とまではいかないですよね)

さらに、日本は人口減少時代を迎えていますので、徐々に「モノを売るのは海外しかない」という状況にもなってきていると思います。

ぜひ皆さんのサイトも多言語化を考えてみてくださいね。

ではでは〜❗

「久しぶりにチャプチェが
食べたい!
あまりおいてないんですよね…😂」

 

このエントリーをはてなブックマークに追加       follow us in feedly