KAEDE Hack blog

JavaScript 中心に ライブラリなどの使い方を解説する技術ブログ。

React react-i18next 「1つの」 json file から各言語のテキストを展開する

map を使わず読み込む場合

en.json

{
  "welcome": "Welcome to i18next",
  "hello": "Hello",
  "changeLang": "Change Language"
}

ja.json

{
  "welcome": "i18next へようこそ!",
  "hello": "こんにちわ",
  "changeLang": "言語を切り替える"
}

App.js/import

import React, { useState, useEffect } from 'react';

import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
import { useTranslation } from 'react-i18next'

App.js/ヘッダー部分

i18n.use(initReactI18next).init({
  resources: {
    en: {
      translation: enJson,
    },
    ja: {
      translation: jaJson,
    },
  },
  lng: 'en',
  fallback: 'en',
  interpolation: {escapeValue: false},
});

App.js/ Function App 前半部分

function App() {
  const [t, i18n] = useTranslation();
  const [lang, setLang] = useState('en');

  useEffect( () => {
    i18n.changeLanguage(lang)
  } ,[lang, i18n]);

App.js/ Function App return 部分

  return (
    <div className="App">
      <p>{t('welcome')}</p> 
      <div>
        <button onClick={
          () => setLang('en')
        }>
          English
        </button>
        <button onClick={
          () => setLang('ja')
        }>
          Japanese
        </button>
      </div>
    </div>
  );
}

export default App;

result

f:id:kei_s_lifehack:20210309212752p:plain

これで切り替えが動く


json ファイルからまとめて map する場合

locale.json

[
  {
    "name": "en",
    "welcome": "Welcome to i18next",
    "hello": "Hello",
    "changeLang": "Change Language"
  },
  {
    "name": "zh",
    "welcome": "欢迎光临 i18next",
    "hello": "你好",
    "changeLang": "用中文"
  },
  {
    "name": "ja",
    "welcome": "i18next へようこそ!",
    "hello": "こんにちわ",
    "changeLang": "言語を切り替える"
  },
  {
    "name": "ko",
    "welcome": "i18next에 오신 것을 환영합니다",
    "hello": "안녕하세요",
    "changeLang": "한국어 변경"
  },
  {
    "name": "es",
    "welcome": "Bienvenida a i18next",
    "hello": "Hola",
    "changeLang": "usar español"
  }
]

App.js/ json の読み込み確認

import locales from './locales/lang.json'
console.log('Locales read:')
console.table(locales)

f:id:kei_s_lifehack:20210309213244p:plain

これで json ファイルの中に 一つの配列が入っていて、その中に

name: "en", welcome: "welcome to i..." と入っていることを読み取ることができた。

あとはこのデータをいかに react-i18next に渡すかになる。

init の失敗例

これで失敗して苦戦した。

i18n.use(initReactI18next).init({
  resources: {
    locales.map( (locale) => {
      console.log(locale.name);
      {locale.name}:{
        translation: {locale}
      }
    } )
  },
  lng: 'en',
  fallback: 'en',
  interpolation: {escapeValue: false},
});

これだと動かない。

i18n は object で受け取ることになってるのに、配列で渡しているからだ。


全体配列から1オブジェクト事に

object で json ファイル自体を書き直す手もあるが、ファイルはそのままでも、 取り出し方を工夫すれば object として渡せる。

array.reduce() を使って配列一つ一つに処理を施し、object として作る。

object はこれ { }

i18n.use(initReactI18next).init({
  resources: locales.reduce( (stack, locale) => {
    stack[locale.name] = { translation: locale }
    return stack
  }, {}),
  lng: 'en',
  fallback: 'en',
  interpolation: { escapeValue: false }
});

これで 前述の locale.json を当てる

[ { hoge: ua, }, { huu: gya, } , ]

の形ででかい配列にオブジェクトが5つ入ってるやつ。

stack[local.name] が en が入り

locale として今回使われている

f:id:kei_s_lifehack:20210321023445p:plain

配列の 0 こ目の object が当たる。

その object を i18next さんの translation に当ててひと言語分完了。

次に stack[local.name] に zh

locale として 1 こ目の json...

と当たっていき、全てが当てはめられる。

5 言語のボタンも map で書く

明日やる