Laravel10 + Inertia + vue3で動的な多言語機能の実装

# 概要
laravelで動的な言語切り替えを実装しようとした際に情報があまり出ていなかったので備忘録も含めて実装のマニュアルを残そうと思いました

# 必要なものをインストール

### コマンド一覧
**多言語対応初期設定**
バックエンド側
`composer require laravel-lang/common --dev`

`php artisan lang:publish`

vue側
`npm i laravel-vue-i18n`

**対応言語一覧**
https://github.com/drobee/Laravel-lang/tree/master/json

# 実装
### Laravel側
```php:/app/config/languages.php
<?php
return [
    'en' => 'English',
    'ja' => '日本語',
    'vi' => 'Tiếng Việt',
    'zh-CN' => '汉语',
    'tr' => 'Türkçe',
    'ar' => 'اللغة العربية'
    //動的に選択したい言語を入れておく
];
```
```php:/routes/web.php
Route::get('/setlocale/{locale}', function ($locale) {
    // セッションにデータを保存
    session(['locale' => $locale]);
    $locale = session('locale');
    return ['lang' => $locale];
})->where(['locale' => 'ja|en|vi|tr|ar|zh-CN'])->name('locale');
```
```php:/app/Http/Middleware/SetLocale.php
    public function handle(Request $request, Closure $next): Response
    {

        $locale = session('locale');
        if ($locale) {
            App::setLocale($locale);
        }
        return $next($request);
    }
```
```php:/app/Http/Kernel.php
    protected $middlewareGroups = [
        'web' => [
            //追加
            \App\Http\Middleware\SetLocale::class,
        ],
    ];
```

### vue側
```js:/resources/app.js
import { i18nVue } from 'laravel-vue-i18n';

...

createInertiaApp({
    title: (title) => `${title} - ${appName}`,
    resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')),
    setup({ el, App, props, plugin }) {
        return createApp({ render: () => h(App, props) })
            .use(plugin)
            .use(ZiggyVue, Ziggy)
            //ここから
            .use(i18nVue,{
                resolve: lang => import(`../../lang/${lang}.json`),
            })
            //ここまで追加
            .use(VCalendar, {})
            .mount(el);
    },
```
```vue:/resources/js/Components/Language.vue
<script setup>
import { ref } from 'vue'
import Dropdown from '@/Components/Dropdown.vue';
import { loadLanguageAsync, getActiveLanguage } from 'laravel-vue-i18n';
import axios from "axios";

const props = defineProps({
    align: {
        type: String,
        default: 'left'
    },
});

const thisLang = ref(getActiveLanguage().replace('_', '-'));
const url = "/setlocale";
const changeLang = async (lang) => {
    await axios.get(url + '/' + lang);
    loadLanguageAsync(lang);
    thisLang.value = lang;
};
</script>

<template>
    <Dropdown :align="align" width="48">
        <template #trigger>
            <span class="inline-flex rounded-md">
                <button type="button"
                    class="inline-flex items-center px-3 py-2 text-sm leading-4 font-medium focus:outline-none transition ease-in-out duration-150">
                    <div class="w-full">
                        {{ $page.props.languages[thisLang] }}
                    </div>
                </button>
            </span>
        </template>

        <template #content>
            <template v-for="(languages, lang) in $page.props.languages" :key="lang">
                <div v-on:click="changeLang(lang)"
                    class="block w-full px-4 py-2 text-left text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out">
                    {{ languages }}
                </div>
            </template>
        </template>
    </Dropdown>
</template>
  
```

```vue:/resources/Pages/ExamplePage.vue
  //{{ $t('') }}で翻訳したいものを囲む
  <InputLabel for="email" :value="$t('Email')" />
  
```

# 最後に
今回はバリデーションのエラーメッセージの翻訳は省略して、自分が良く忘れてしまうvue側の翻訳についてをメモ程度に残しました
「laravel-vue-i18n」のドキュメントをよく読めばloadLanguageAsync(lang: string)とgetActiveLanguage()を使えば簡単に実装できることに気付けるのですが、今後多言語化を実装される人の助けに少しでもなればと思います。

もしかしたらMiddlewareの箇所は必要ないかも・・・？
ずいぶん前に実装したのでもし間違いやより良い実装などあればコメントでご指摘くださると幸いです。