KAEDE Hack blog

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

React react-i18next での言語切り替えを試した

why

kei-s-lifehack.hatenablog.com

Next.js と react-intl での言語切り替えがうまくいかなかった、react-18next があると聞いた

suzukalight.com

t, translate 関数での二言語の切り替えが react-intl より簡単そうに見えた

結果

f:id:kei_s_lifehack:20210306045346p:plain

f:id:kei_s_lifehack:20210306045359p:plain

Next.js ではこのコードは使えなかった

React.js 単体では動いた

実装

とりあえず1 からトップだけ作ってみることにする。

ボタンでの言語切り替え。

suzukalight.com

この「素振り」を参考にする

問題点

TS の React.FC の型がどうして参考記事で使われているのか理解できていない

 () => setLang(lang === 'en' ? 'ja' : 'en')      

のロジックは理解できたが、これを自分で思いつける自信がない

github.com

公式の GitHub の サンプルを見たが、どうして class component も使うのか理解できない

install

npm i  i18next react-i18next

いつも通り npm i

t('trans_key')と setLang でのトグル切り替えを仕込む

<p>{t('welcome')}</p> 
<div>                                               
  <button onClick={                                 
    () => setLang(lang === 'en' ? 'ja' : 'en')      
  }>                                                
    {t('changeLang')}                          
  </button>                                         
</div>                                              

最初に jsx に

{ t('translation_key') }

と t 関数と引数の key となる string で t 関数を用いた変換された key を書く

これはあくまで 変換の際に辞書ファイルを検索するための key であり、実際のメッセージではない。

次に btn で クリックされたときに

() => setLang(lang === 'en' ? 'ja' : 'en')

setLang が即時関数で発動し、

引数には lang という変数の中身が 初期値の ja から en になっていた場合であれば

en からの変更なので ja に

en じゃない場合は ja になるので、ja からの変更なので en に

変更する。


i18n の import と辞書の配列の作成

先ほどの index.js の前半 で i18n の import と 変換用の辞書のリソースを入れる

先ほど作った t(welcome), t(changLang) はここからとってくる。

json だし その技術があれば CMS で外部からも持って来れそうだな...

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

i18n と initReactI18next を import して初期値を入れる。

i18n.use(initReactI18next).init({
  resources: {
    en: {
      translation: {
        'welcome': 'Welcom to i18next',
        'changeLang': 'Change Language',
      },
    },
    ja: {
      translation: {
        'welcome': 'i18next にようこそ。'
        'changeLang': '言語を切り替える',
      },
    },
  },
  lng: 'en',
  fallback: 'en',
  interpolation: {escapeValue: false},
});

検索先のリソースとして

welcome, changeLang をそれぞれの言語で入れる。

lng で最初の言語、fallback が何かあったときの言語だろう

inter polation と escape value は不明

Hook を使って言語の切り替え関数を作る

index のさらに上部に 本命の useTranslation での 作成した辞書から引っ張ってくるライブラリの import と、react-hooks での state と副作用での切り替え機能を作る

記事では ts のため React.FC を使っているが、function のみで実装できた。

import { useTranslation } from 'react-i18next'
import React, { useState, useEffect } from 'react';

useTranslation と useState, useEffect を import

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

traslate, i18n, lang, setLang の hook を作成

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

lang か i18n が変化したときに、 i18n の changeLanguage を使って 現状の lang の値にする。

JS で type を使っていてエラーが出る

f:id:kei_s_lifehack:20210305115049p:plain

key を間違えていたが、React.FC がないと思って js に React.FC を使ったところ

react_devtools_backend.js:2430 ./pages/index.js:27:10
Syntax error: Const declarations require an initialization value

  25 | });
  26 | 
> 27 | const Home: React.FC = () => {
     |           ^
  28 |   const [t, i18n] = useTranslation();
  29 |   const [lang, setLang] = useState('en');
  30 | 

const Home: React.FC だと初期値がない?からだめとエラーが出た

react_devtools_backend.js:2430 ./pages/index.js:27:8
Syntax error: Unexpected token, expected ";"

  25 | });
  26 | 
> 27 | let Home: React.FC = () => {
     |         ^
  28 |   const [t, i18n] = useTranslation();
  29 |   const [lang, setLang] = useState('en');
  30 | 

let にしても : React.FC が受け入れられない

Next との食い合わせが悪いのか...???

Syntax error: Const declarations require an initialization value

  25 | });
  26 | 
> 27 | const Home:React.FC = (lang) => {
     |           ^
  28 |   const [t, i18n] = useTranslation();
  29 |   const [lang, setLang] = useState('en');

中身を入れてもダメ、どうすればいいのか詰まったが、

js なのに tsの記述があるのは不自然

とご指摘をいただいた。

確かに元のコードは tsx だ。

調べなおしたらそもそも React.FC は React の type だった。

React で無事に動かす

f:id:kei_s_lifehack:20210306045359p:plain

f:id:kei_s_lifehack:20210306045346p:plain

key を統一して 無事にトグルすることができた。

次やるべきこと

これを Next.js でも結局できるのか試す。

React.js のみでは 複数ページのルーティングが手間だったり meta 周りの実装に苦労するのでやはり最終的には Next で動かさなければしっかりとしたものはつくれないだろう

TS の型を入れると何が嬉しいのかわかるようにする。

ここからやりたいこと

レスポンシブにして json ファイルにデータを分けて、かねてより作りたかった Ark Aberration 生物図鑑のサイトを公開する

json ファイルに辞書配列を分ける

suzukalight.com

i18n.use(initReactI18next).init({
  resources: {
    en: {
      translation: {
        "welcome": "Welcom to i18next",
        "changeLang': "Change Language"
      },
    },
    ja: {
      translation: {
        "welcome": "i18next にようこそ。",
        "changeLang": "言語を切り替える"
      },
    },
  },
  lng: 'en',
  fallback: 'en',
  interpolation: {escapeValue: false},
});

この App.js に書いている resources の鍵と翻訳を別のファイルに分ける。

src/locales/ を作成

en.json を作って...

f:id:kei_s_lifehack:20210306054311p:plain

json は single quote がダメらしい。Shift 押したくないんだがな...

f:id:kei_s_lifehack:20210306054505p:plain

これでよし

{
  "welcome": "i18next にようこそ。",
  "changeLang": "言語を切り替える"
}

ja.json も作成。

json ファイルを読み込む

import enJson from './locales/en.json'
import jaJson from './locales/ja.json'

enJson, jaJson, という形で App.js で読み込み

resources: {             
  en: {                  
    translation: enJson, 
  },                     
  ja: {                  
    translation: jaJson, 
  },                     
},                       

初期化のリソースの英語の訳を enJson, 日本語を jaJson, に当てる

これで無事動いた。

あとは key で t() ちゃんが検索してくれるので react-intl みたいに id をつける必要もなし!!!やったね!t ちゃん!!!

これで攻略ブログ作るぞ〜〜〜!!