🌊

Inertia.js + breeze + react環境での日本語化したものを作った

2025/01/31に公開

以下。セットアップは一般的なlaravelのdeployに従う。特に難しい事はしてない。

https://github.com/catatsumuri/laravel-inertia-stack-ja/releases/tag/inertiajs-breeze-react-ja-startup


ログイン画面

とか


Profile更新画面

とか

以下作成ログ

laravel-react-i18n

https://github.com/EugeneMeles/laravel-react-i18n

これはバックエンドのものでは無いため、composerというかpackagistには存在していない。githubのREADMEにあるようにnpmでinstallする。

npm i laravel-react-i18n

vite.config.jsの変更

@@ -1,6 +1,7 @@
 import { defineConfig, loadEnv } from 'vite';
 import laravel from 'laravel-vite-plugin';
 import react from '@vitejs/plugin-react';
+import i18n from 'laravel-react-i18n/vite';

 export default defineConfig(({ mode }) => {
     const env = loadEnv(mode, process.cwd(), '');
@@ -20,6 +21,7 @@ export default defineConfig(({ mode }) => {
                 refresh: true,
             }),
             react(),
+            i18n(),
         ],
     };
 });

こんな感じでpluginにてi18n()をcallしてロード。

resources/js/app.jsxの変更

これは素のreactであった場合

breezeで展開されただけのresources/js/app.jsx
resources/js/app.jsx
import '../css/app.css';
import './bootstrap';

import { createInertiaApp } from '@inertiajs/react';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
import { createRoot } from 'react-dom/client';

const appName = import.meta.env.VITE_APP_NAME || 'Laravel';

createInertiaApp({
  title: (title) => `${title} - ${appName}`,
  resolve: (name) =>
    resolvePageComponent(
      `./Pages/${name}.jsx`,
      import.meta.glob('./Pages/**/*.jsx')
    ),
  setup({ el, App, props }) {
    const root = createRoot(el);

    root.render(<App {...props} />);
  },
  progress: {
    color: '#4B5563',
  },
});

変更

 import './bootstrap';

 import { createInertiaApp } from '@inertiajs/react';
+import { LaravelReactI18nProvider } from 'laravel-react-i18n';
 import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
 import { createRoot } from 'react-dom/client';

@@ -17,7 +18,15 @@ createInertiaApp({
   setup({ el, App, props }) {
     const root = createRoot(el);

-    root.render(<App {...props} />);
+    root.render(
+      <LaravelReactI18nProvider
+        locale={'ja'}
+        fallbackLocale={'en'}
+        files={import.meta.glob('/lang/*.json')}
+      >
+        <App {...props} />
+      </LaravelReactI18nProvider>
+    );
   },
   progress: {
     color: '#4B5563',

まあ用するに

<LaravelReactI18nProvider>
  <App />
</LaravelReactI18nProvider>

のようにwrapするという事になりますね。

ログインページを書き換えてみる

breezeで展開した場合 resources/js/Pages/Auth/Login.jsx に存在しているはずだ。
これを以下のように書き換えていく。

--- a/resources/js/Pages/Auth/Login.jsx
+++ b/resources/js/Pages/Auth/Login.jsx
@@ -5,8 +5,10 @@ import PrimaryButton from '@/Components/PrimaryButton';
 import TextInput from '@/Components/TextInput';
 import GuestLayout from '@/Layouts/GuestLayout';
 import { Head, Link, useForm } from '@inertiajs/react';
+import { useLaravelReactI18n } from 'laravel-react-i18n';

 export default function Login({ status, canResetPassword }) {
+  const { t } = useLaravelReactI18n();
   const { data, setData, post, processing, errors, reset } = useForm({
     email: '',
     password: '',
@@ -23,7 +25,7 @@ export default function Login({ status, canResetPassword }) {

   return (
     <GuestLayout>
-      <Head title='Log in' />
+      <Head title={t('Log in')} />

       {status && (
         <div className='mb-4 text-sm font-medium text-green-600'>{status}</div>
@@ -31,7 +33,7 @@ export default function Login({ status, canResetPassword }) {

       <form onSubmit={submit}>
         <div>
-          <InputLabel htmlFor='email' value='Email' />
+          <InputLabel htmlFor='email' value={t('Email')} />

           <TextInput
             id='email'
@@ -48,7 +50,7 @@ export default function Login({ status, canResetPassword }) {
         </div>

         <div className='mt-4'>
-          <InputLabel htmlFor='password' value='Password' />
+          <InputLabel htmlFor='password' value={t('Password')} />

           <TextInput
             id='password'
@@ -70,7 +72,9 @@ export default function Login({ status, canResetPassword }) {
               checked={data.remember}
               onChange={(e) => setData('remember', e.target.checked)}
             />
-            <span className='ms-2 text-sm text-gray-600'>Remember me</span>
+            <span className='ms-2 text-sm text-gray-600'>
+              {t('Remember me')}
+            </span>
           </label>
         </div>

@@ -80,12 +84,12 @@ export default function Login({ status, canResetPassword }) {
               href={route('password.request')}
               className='rounded-md text-sm text-gray-600 underline hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2'
             >
-              Forgot your password?
+              {t('Forgot your password?')}
             </Link>
           )}

           <PrimaryButton className='ms-4' disabled={processing}>
-            Log in
+            {t('Log in')}
           </PrimaryButton>
         </div>
       </form>

laravel-langを導入する

言語ファイルを自力で展開してもいいんだけど、面倒なのでここはlaravel-langを使う

https://packagist.org/packages/laravel-lang/lang

composer require laravel-lang/lang
php artisan lang:add ja

LaravelReactI18nProviderをハードコードしない場合

実は、ドキュメントもあるように

      <LaravelReactI18nProvider
        // locale={'ja'}
        fallbackLocale={'en'}
        files={import.meta.glob('/lang/*.json')}
      >

こんな具合にすると<html lang="">の値から読みこまれる。これは resources/views/app.blade.php という雛形から読みこまれるが

<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">

このように定義されているので、結局 .env

APP_LOCALE=ja
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_PORT=8000

この辺をしっかりセットしておけばハードコードする必要はない(fallbackは不明だけどまあenになるんじゃないかな...興味あれば調べてみてください)

なお

const { t, currentLocale } = useLaravelReactI18n();

として、適当なところで

<strong>{currentLocale()}</strong>

とかすれば現在のlocaleの設定は見られます(もちろんconsoleに送ってもok)

その他のページ

イチイチ全部書き換えていくのは相当面倒なので冒頭のソースを参考にしてください。まあ、半分以上AIにやらせてます。

Discussion