0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

laravel9.x + inertiajs(vue3) + typescript 多言語化(i18n)の設定備忘録

Posted at

実行環境

・osバージョン
rocky linux 8.7

・composerパッケージのバージョン
laravel 9.48.0
inertiajs/inertia-laravel v0.5.4
laravel-lang/lang 12.11.5
laravel-lang/publisher v14.5.0

・node(npm)パッケージで関連しそうなバージョン
typescript@4.9.4
@inertiajs/inertia-vue3@0.6.0
@inertiajs/inertia@0.11.0
@inertiajs/progress@0.2.7
@vue/compiler-sfc@3.2.45
laravel-mix-vue3@0.7.0
vue-i18n@9.2.2
vue-loader@17.0.1
vue-template-compiler@2.7.14
vue@3.2.45

各ファイルの設定

webpack.mix.js

webpack.mix.js
const mix = require('laravel-mix');
require("laravel-mix-vue3");

/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel applications. By default, we are compiling the CSS
 | file for the application as well as bundling up all the JS files.
 |
 */

mix.ts('resources/js/app.ts', 'public/js')
    .postCss('resources/css/tailwind.css', 'public/css', [require('tailwindcss'), require('autoprefixer')])
    .sass('resources/scss/bootstrap.scss', 'public/css')
    .alias({
        '@': 'resources/js',
    })
    .babelConfig({
        plugins: ['@babel/plugin-syntax-dynamic-import'],
    })
    .webpackConfig({
        stats: {
            children: true
        }
    })
    .sourceMaps()
    .vue();
if (mix.inProduction()) {
    mix.version();
}

tsconfig.json

有効な設定の未抜粋。

tsconfig.json
{
  "compilerOptions": {
    "target": "es2020",
    "module": "commonjs",
    "baseUrl": "./",
    "typeRoots": [
        "./resources/js/@types"
    ],
    "types": [
        "node"
    ],
    "resolveJsonModule": true,
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "noImplicitAny": false,
    "skipLibCheck": true
  },
  "include": [
    "./resources/js/**/*"
  ]
}

package.json

scriptsのみ抜粋

package.json
{
    "scripts": {
        "dev": "npm run development",
        "development": "mix",
        "watch": "mix watch",
        "watch-poll": "mix watch -- --watch-options-poll=1000",
        "hot": "mix watch --hot",
        "prod": "npm run production",
        "production": "mix --production"
    },
}

多言語化(i18n)

laravel側の設定

$ composer require laravel-lang/lang laravel-lang/lang-publisher --dev
$ composer require laravel-lang/attributes laravel-lang/http-statuses --dev
$ php artisan lang:add en ja

laravel9からlangはプロジェクトの直下に置かれる。

./lang
+ en
 +- auth.php
 +- http-statuses.php
 +- pagination.php
 +- passwords.php
 +- validation.php
+ ja
 +- auth.php
 +- http-statuses.php
 +- pagination.php
 +- passwords.php
 +- validation.php

デフォルトでないものは各言語ファイルに追加する。

vue3のフォルダ構成

resources配下にjs,css,scss,viewsを用意。

resources/scssとresources/css

前述のwebpack.mix.jsで指定したように
tailwindとbootstrapで別々のcssファイルに生成するようにする。
scssにはbootstrap.scssファイルを格納
cssにはtailwind.cssファイルを格納、
さらにアプリ独自のcssファイルの設定があれば
app.cssなどを用意する。

resources/scss/bootstrap.scss

./scss/bootstrap.scss
@import "~bootstrap/scss/bootstrap";

resources/css/tailwind.css

resources/css/tailwind.css
@tailwind base;
@tailwind components;
@tailwind utilities;

resources/views

既存のテンプレートシステム(*.blade.php)が格納されているが
vue3ではjs配下に画面+処理のファイル(*.vue)を格納するので
app.blade.phpのみ使用する。

resources/views/app.blade.php

resources/views/app.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="csrf-token" content="{{ csrf_token() }}">

        <title inertia>{{ config('app.name', 'Laravel') }}</title>

        <!-- Fonts -->
        <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap">

        <!-- Styles -->
<!--        <link rel="stylesheet" href="{{ asset('css/app.css') }}"> -->
        <link rel="stylesheet" href="{{ asset('css/tailwind.css') }}">

        <!-- Scripts -->
        @routes
        <script src="{{ mix('js/app.js') }}" defer></script>
        @inertiaHead
    </head>
    <body class="font-sans antialiased">
        @inertia

        @env ('local')
<!--            <script src="http://localhost:8080/js/bundle.js"></script> -->
        @endenv
    </body>
</html>

@routesはvueファイルのscript内やtemplate内でlaravelのroute関数を使えるようにする指定
@env('local')はローカル環境で動作させるとき
内部のサーバ経由にするらしいが余計なお世話なのでコメント化。

resource/js

resource/js/bootstrap.js

larave-echopusher-jsはブロードキャスト機能の記述のため使わない場合はコメント化

resource/js/bootstrap.js
window._ = require('lodash');

try {
    require('bootstrap');
} catch (e) {}

/**
 * We'll load the axios HTTP library which allows us to easily issue requests
 * to our Laravel back-end. This library automatically handles sending the
 * CSRF token as a header based on the value of the "XSRF" token cookie.
 */

window.axios = require('axios');
/**
 * Echo exposes an expressive API for subscribing to channels and listening
 * for events that are broadcast by Laravel. Echo and event broadcasting
 * allows your team to easily build robust real-time web applications.
 */

import Echo from 'laravel-echo';

window.Pusher = require('pusher-js');
// import Pusher from 'pusher-js';

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: process.env.MIX_PUSHER_APP_KEY,
    cluster: process.env.MIX_PUSHER_APP_CLUSTER,
    forceTLS: true,
    wsHost: 'websocketのホスト名',
    wssPort: 443,
});

resource/js/app.ts

laravelの言語ファイル(php/json)をvue3で使うための設定も追記。
routeziggy-jsを使う。

resource/js/app.ts
import './bootstrap';

import { createApp, h } from 'vue';
import { createInertiaApp } from '@inertiajs/inertia-vue3';
import { InertiaProgress } from '@inertiajs/progress';
import { createI18n } from 'vue-i18n';
import route from 'ziggy-js';
import 'flowbite';
import * as jaJP from '../../lang/ja.json';
import * as enUS from '../../lang/en.json';

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

createInertiaApp({
    title: (title) => `${title} - ${appName}`,
    resolve: (name) => require(`./Pages/${name}`),
    setup({ el, app, props, plugin }) {
        const setupAsync = async ({ el, app, props, plugin }) => {
            const i18n = createI18n({
                legacy: false,
                globalInjection: true,
                locale: 'ja-JP',
                fallbackLocale: 'en-US',
                messages: {
                    'en-US': enUS,
                    'ja-JP': jaJP
                }
            });
            return createApp({ render: () => h(app, props) })
                .use(plugin)
                .use(i18n)
                .mixin({ methods: { route } })
                .mount(el);
        }
        setupAsync({ el, app, props, plugin });
    },
});

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

さらに以下のフォルダを配置する。

resource/js/@types

typescript用の型定義ファイルを置く。コンパイルやvscodeでのエラー対策。
tsconfig.jsonにて指定する。

resource/js/@types/shim-vue.d.ts
declare module '*.vue' {
    import type { DefineComponent } from "vue";
    const component: DefineComponent<{}, {}, any>;
    export default component;
}
resource/js/@types/global.d.ts
interface Window {
  Echo: any;
  Pusher: any;
}

declare var window: Window;

resource/js/Components

画面の各ボタンやロゴやInputなどの部品を格納初期である程度用意されている。
ApplicationLogo.vue,Checkbox.vue,DropdownLink.vue,InputError.vue,NavLink.vue,ResponsiveNavLink.vue,Button.vue,Dropdown.vue,Input.vue,Label.vue,NavMenu.vue,ValidationErrors.vue

resource/js/Layouts

ログインしていない状)用のレイアウトファイル(Guest.vue)とログイン済みユーザー用のレイアウトファイル(Authenticated.vue)
ヘッダー要素と各ページへの指定がされているだけ。

resource/js/Pages

各ページの内容を記載する。
初期はログイン前の画面(Welcome.vue)とログイン後の画面(Dashboard.vue)と
Pages/Authに認証に必要な画面(ログイン、アカウント作成、パスワードリセット)のみ。
{{ $t('Dashboard') }}の個所は後述の多言語化対応

resource/js/Pages/Dashboard.vue
<script setup lang="ts">
import BreezeAuthenticatedLayout from '@/Layouts/Authenticated.vue';
import { Head, Link } from '@inertiajs/inertia-vue3';
</script>

<template>
    <Head>
        <title>{{ $t('Dashboard') }}</title>
        <meta name="description" content="Dahsboad page description">
        <link rel="shortcut icon" href="/favicon.svg">
    </Head>

    <BreezeAuthenticatedLayout>
        <template #header>
            <h2 class="font-semibold text-xl text-gray-800 leading-tight">
                {{ $t('Dashboard') }}
            </h2>
        </template>

        <div class="py-12">
            <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
                <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
                    <div class="p-6 bg-white border-b border-gray-200">
                        {{ $t("You are logged in!") }}
                    </div>
                </div>
            </div>
        </div>
    </BreezeAuthenticatedLayout>
</template>

vueファイル内で多言語メッセージを使う場合は{{ $t('メッセージ') }}のように使用する。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?