概要

React.js の話をしようぜ!

いやいや、そりゃあ勿論、 Python こそが母国語であり、至高だよ? (再び)

でも前回 React.js をセットアップしてて楽しくなってきちゃったので、前回のスターターキットに i18n (多言語対応) を追加しようぜ!

今回は、

  • 翻訳拡張 i18next react-i18next を導入、
  • 翻訳ファイル作成作業効率化のための i18next-parser を導入、

ってところまでを揃えるぜ。

 

翻訳拡張を導入

自分のノートだけで作業が完結するように、今回も、以下に設定ファイルを書く……けれど、ちゃんとやりたいときはこちら↓の公式ドキュメントの Quick start 通りにやるといいぜ。

# i18next: 国際化ライブラリ。エンジン側。ふつうの JavaScript でも使える。
# react-i18next: これ↑を React へ統合するためのライブラリ。
yarn add i18next react-i18next

翻訳 json ファイルを用意しておく。

mkdir -p ./src/locales/en
mkdir -p ./src/locales/ja

echo '{}' > ./src/locales/en/translation.json
echo '{}' > ./src/locales/ja/translation.json

翻訳 json ファイルを読み込んで、翻訳の設定をするファイルを用意しておく。

mkdir -p ./src/i18n

echo "
import EN_TRANSLATION from '@/locales/en/translation.json'
import JA_TRANSLATION from '@/locales/ja/translation.json'
import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'

const resources = {
  ja: {
    translation: JA_TRANSLATION,
  },
  en: {
    translation: EN_TRANSLATION,
  },
}

i18n
  .use(initReactI18next)
  .init({
    resources,
    lng: 'en',
    interpolation: {
      escapeValue: false
    }
  })

export default i18n
" > ./src/i18n/index.ts

React アプリのエントリーポイントである main.tsx で、設定ファイルを読み込む。

echo "
import App from '@/App'
import '@/i18n'
import '@/index.css'
import React from 'react'
import ReactDOM from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <BrowserRouter basename='/my-react'>
      <App />
    </BrowserRouter>
  </React.StrictMode>
)
" > ./src/main.tsx

 

翻訳拡張を使う

こんな風に useTranslationt を使う。まだ翻訳 json ファイルは空っぽだけれど、そのときはキーがそのまま表示される。問題なし。なんか、参考サイトではよく一意のキー文字列が使われているよな。でも、 json ファイルでは日本語をキーに持てるから、べつに日本語でもいいんじゃないか? って思うよ。そのほうがコンポーネントが読みやすいからさ。

echo "
import Button from '@/components/Button'
import formatDate from '@/utils/formatDate'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'

function HomePage() {
  const { t } = useTranslation()
  const [formattedDate, setFormattedDate] = useState<string>('')

  useEffect(() => {
    const currentDate = new Date()
    const dateStr = formatDate(currentDate)
    setFormattedDate(dateStr)
  }, [])

  const handleClick = () => {
    alert(t('Button がクリックされた!'))
  }

  return (
    <div>
      <h1>{t('Welcome to the Home Page')} - {formattedDate}</h1>
      <Button label={t('Click Me')} onClick={handleClick} />
    </div>
  )
}

export default HomePage
" > ./src/pages/HomePage.tsx

 

翻訳 json ファイルを自動で埋めるツールを導入

このツールは、コンポーネントの t をスキャンして、翻訳 json ファイルを自動で埋めてくれる効率化ツールだ。これを使えば、こんな風↓に作業できるってわけだ。

  • コンポーネントに文字列を書くときは t で片っ端から実装する
  • ツールを使う
  • 翻訳すべき文章: 初期値 の組み合わせで json ファイルが埋まる
  • json ファイルを開いて、 “翻訳すべき文章” を片っ端から翻訳すればよい

ラクでいいね。

yarn add --dev i18next-parser

設定ファイルを追加↓する。公式 README に詳しい設定があるから、それを貼り付けたほうが後々よいかも。

echo "
export default {
  contextSeparator: '_',
  createOldCatalogs: true,
  defaultNamespace: 'translation',
  defaultValue: (locale, namespace, key) => {
    return \`\${key}\` // 規定と変更したもの。
  },
  indentation: 2,
  keepRemoved: false,
  keySeparator: '.',
  lexers: {
    hbs: ['HandlebarsLexer'],
    handlebars: ['HandlebarsLexer'],
    htm: ['HTMLLexer'],
    html: ['HTMLLexer'],
    mjs: ['JavascriptLexer'],
    js: ['JavascriptLexer'],
    ts: ['JavascriptLexer'],
    jsx: ['JsxLexer'],
    tsx: ['JsxLexer'],
    default: ['JavascriptLexer'],
  },
  lineEnding: 'auto',
  locales: ['ja', 'en'], // 規定と変更したもの。
  namespaceSeparator: ':',
  output: 'src/locales/\$LOCALE/\$NAMESPACE.json', // 規定と変更したもの。
  pluralSeparator: '_',
  input: undefined,
  sort: false,
  verbose: false,
  failOnWarnings: false,
  failOnUpdate: false,
  customValueTemplate: null,
  resetDefaultValueLocale: null,
  i18nextOptions: null,
  yamlOptions: null,
}
" > ./i18next-parser.config.js

こちらのコマンドで実行する。

yarn run i18next "./src/App.tsx" "./src/**/*.tsx"

# config が読み込まれているかどうかが心配だったらこう↓
yarn run i18next "./src/App.tsx" "./src/**/*.tsx" --config "./i18next-parser.config.js"

そうすると、こんな風にモリモリと翻訳 json ファイルが埋まっていくわけだぜ。ハァー、効率的!

{
  "Welcome to the Home Page": "Welcome to the Home Page",
  "Click Me": "Click Me",
  "Button がクリックされた!": "Button がクリックされた!"
}

以下の記事からリンクされています