🍀

Laravel Jetstream+Vueの多言語化(i18n)

2021/09/17に公開

はじめに

この記事を読むと、Laravel Jetstream+Vue環境をja.json等のファイルによって多言語化(i18n)できるようになります。
多言語化にはvue-i18nを使用します。
Jetstreamではフロントエンドとして、livewireまたはVueを選択できます。
livewireバージョンは既に多言語化対応されています。
しかし、現状、Vueバージョンの方は多言語化対応されていません。
この記事ではフロントエンドとしてvueを選択した場合の多言語化(i18n)について解説します。

(2021年10月12日追記)
Laravel+Jetstream(Vue)+Vite環境化での多言語化(i18n)に関する記事を追加しました。
Vite環境化で多言語化する場合は以下の記事をご覧ください。
https://zenn.dev/yamabiko/articles/laravel-jetstream-vite-i18n

バージョン情報

  • PHP 8.0.9
  • laravel/framework: v8.62.0
  • vue@3.2.11
  • @inertiajs/inertia@0.10.1
  • @inertiajs/inertia-vue3@0.5.2
  • @intlify/vue-i18n-loader@3.0.0

多言語化(i18n)

1.ja.jsonファイル作成

以下のコマンドを実行し、ja.jsonファイルを自動生成します。

$ sail composer require laravel-lang/lang:~8.1.3 --dev
$ cp vendor/laravel-lang/lang/json/ja.json resources/lang/

2.多言語化に関するソースコード修正

vue-i18nをインストールします。

NPM

$ sail npm install vue-i18n@next

Yarn

$ sail yarn add vue-i18n@next

ルートテンプレートにlocaleを定義します。

resources/views/app.blade.php
        <!-- Scripts -->
        @routes
        <script>
            var __locale = '{{ app()->getLocale() }}'
        </script>
        <script src="{{ mix('js/app.js') }}" defer></script>

app.jsに多言語化(i18n)を追加します。
legacy: false、globalInjection: trueを指定することでグローバルに、どのvueファイルからでもメッセージ翻訳関数の使用が可能になります。
また、loadLocaleMessages関数により多言語化ファイル(ja.json等)を読み込みます。
詳細はvue-i18nのドキュメントをご確認ください。

resources/js/app.js
    require('./bootstrap');

    import { createApp, h } from 'vue';
    import { createInertiaApp } from '@inertiajs/inertia-vue3';
    import { InertiaProgress } from '@inertiajs/progress';
    import { createI18n } from 'vue-i18n'

    const appName = window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel';

    async function loadLocaleMessages(i18n, locale){
        const messages = await import(`../lang/${locale}.json`)
        i18n.global.setLocaleMessage(locale, messages.default)
    }

    createInertiaApp({
        title: (title) => `${title} - ${appName}`,
        resolve: (name) => require(`./Pages/${name}.vue`),
        async setup({ el, app, props, plugin }) {

            const i18n = createI18n({
                legacy: false,
                globalInjection: true,
                locale: __locale
            })

            await loadLocaleMessages(i18n, __locale)

            return createApp({ render: () => h(app, props) })
                .use(plugin)
                .use(i18n)
                .mixin({ methods: { route } })
                .mount(el);
        },
    });

    InertiaProgress.init({ color: '#4B5563' });

以上で、多言語化に関するソースコード修正は完了です。

vueファイルの修正

日本語化する箇所について以下のように修正します。
主な修正箇所は以下です。

  • 文字列、タイトル、ボタン名、リンク名
  • ラベル
  • プレースホルダ

(livewireバージョンは既に同じような修正が入っています。)
フォーマットについてはvue-i18nのドキュメントをご確認ください。

resources/js/Pages/Auth/Login.vue
<template>
    <Head :title="$t('Log in')" />

    <jet-authentication-card>
        <template #logo>
            <jet-authentication-card-logo />
        </template>

        <jet-validation-errors class="mb-4" />

        <div v-if="status" class="mb-4 font-medium text-sm text-green-600">
            {{ status }}
        </div>

        <form @submit.prevent="submit">
            <div>
                <jet-label for="email" :value="$t('Email')" />
                <jet-input id="email" type="email" class="mt-1 block w-full" v-model="form.email" required autofocus />
            </div>

            <div class="mt-4">
                <jet-label for="password" :value="$t('Password')" />
                <jet-input id="password" type="password" class="mt-1 block w-full" v-model="form.password" required autocomplete="current-password" />
            </div>

            <div class="block mt-4">
                <label class="flex items-center">
                    <jet-checkbox name="remember" v-model:checked="form.remember" />
                    <span class="ml-2 text-sm text-gray-600">{{ $t('Remember me') }}</span>
                </label>
            </div>

            <div class="flex items-center justify-end mt-4">
                <Link v-if="canResetPassword" :href="route('password.request')" class="underline text-sm text-gray-600 hover:text-gray-900">
                    {{ $t('Forgot your password?') }}
                </Link>

                <jet-button class="ml-4" :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
                    {{ $t('Log in') }}
                </jet-button>
            </div>
        </form>
    </jet-authentication-card>

</template>

その他のvueファイルについても同様の修正が必要です。

注意点

Register.vueの以下の箇所だけは、今回の対応では日本語化できません。
直接修正する必要があります。
その他のvueファイルについては対応可能です。

resources/js/Pages/Auth/Register.vue
I agree to the <a target="_blank" :href="route('terms.show')" class="underline text-sm text-gray-600 hover:text-gray-900">Terms of Service</a> and <a target="_blank" :href="route('policy.show')" class="underline text-sm text-gray-600 hover:text-gray-900">Privacy Policy</a>

まとめ

全てのvueファイルを日本語化するのは骨が折れますね(汗)
でも、1時間もあれば十分に全箇所修正可能です(笑)
近い将来、Vueの多言語化も正式にサポートされたら嬉しいですね。

Discussion