5
2

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.

Reactでi18nのjson書き出しサボり設定

Last updated at Posted at 2021-06-15

使いどころ

create-react-appを使って作成したプロジェクトを、
i18n-nextで多言語化したとき、翻訳データの*.jsonを手書きするのが面倒。
*.tsx*.jsで翻訳する部分を自動で見つけて*.jsonに追記してほしい!

今回の例として紹介するのは、フロントエンドだけで完結する、非常に簡単なSPAです。
i18n-nextは、locizeを導入できたり、バックエンド開発にも使えるので、知っておくとお得です。

ハマりポイント

先にハマりポイント解説します。

少し前はbabel-plugin-i18next-extractが流行ったようです。しかし、webpackの設定変更が必須となります。create-react-appを使って制作しているアプリケーションの場合、webpackの設定を変更することは、npm ejectというコマンドを使うと実現できますが、不可逆であり得策ではありません。そのため、babel-plugin-i18next-extractを使うのはやめたほうが良さそうです。そこで、利用するのは、公式ドキュメントにも記載のある、i18next-parserです。

#extraction-tools)にも記載のある、i18next-parserです。

まずは、公式ドキュメントにも記載のある、i18next-parserです。

terminal
npm install -g i18next-parser

設定

公式ドキュメントを参考に、i18next-parserの設定を書きます。

./i18next-parser.config.js
module.exports = {
    locales: ['en', 'ja', 'cn'],
    output: 'src/locales/$LOCALE/$NAMESPACE.json',
}
  • locale: 欲しい分だけ言語を追記
  • output: 位置がsrcの中でないと、tsxからimport出来ないので、デフォルト設定を上書き

npm の scripts を設定します。

./package.json
"scripts": {
    "i18next-extract": "i18next 'src/**/*.{tsx,ts}'"
}

ひとまず設定が終わったので、翻訳ファイルのjsonを書き出します。

terminal
npm run i18next-extract

すると、下記のような構成で出力されるはずです。

tree
./
┃
┣━ src
┃    ┣━ locales
┃    ┃    ┣━ cn
┃    ┃    ┃    ┗━ translation.json
┃    ┃    ┣━ en
┃    ┃    ┃    ┗━ translation.json
┃    ┃    ┗━ jp
┃    ┃         ┗━ translation.json

コード変更

ここから、ソースコードに翻訳用の設定を足していきます。
まずは、トップレベルのReactファイルにi18nの初期化設定を書き込みます。
この時、先ほど出力されたtranslation.jsonファイルを設定します。

./src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.sass';
import reportWebVitals from './reportWebVitals';
import App from "./components/App";
import { createBrowserHistory } from "history"
import { Router } from 'react-router';
import HeaderApp from './components/header';
// i18n
import i18next from 'i18next';
import { initReactI18next } from 'react-i18next';

import jaCommon from './locales/ja/translation.json'
import enCommon from './locales/en/translation.json'
import cnCommon from './locales/cn/translation.json'

i18next.use(initReactI18next).init({
  debug: true,
  resources : {
    ja:{common:jaCommon},
    en:{common:enCommon},
    cn:{common:cnCommon},
  },
  lng: 'ja',
  fallbackLng: 'ja',
  keySeparator: false,
  interpolation: { escapeValue: false },
});

const history = createBrowserHistory({ basename: '/' });
ReactDOM.render(
  <React.StrictMode>
    <Router history={history}>
      <HeaderApp/>
      <App></App>
    </Router>
  </React.StrictMode>,
  document.getElementById('root')
);

reportWebVitals(console.log);

<App />の定義は下記です。

./src/components/App.tsx
import React from 'react';
import i18next from 'i18next';
import { useTranslation } from 'react-i18next';
import { useEffect } from 'react'

import ja_Trans from '../locales/ja/maincontent.json'
import en_Trans from '../locales/en/maincontent.json'
import cn_Trans from '../locales/cn/maincontent.json'

export function Translate() {
    const { t } = useTranslation('maincontent');
    useEffect(() => {
        i18next.addResources('ja', 'maincontent', ja_Trans);
        i18next.addResources('en', 'maincontent', en_Trans);
        i18next.addResources('cn', 'maincontent', cn_Trans);
    }, [])
    return (
        <React.StrictMode>
             <p>{t('message')}</p>
        </React.StrictMode>
    );
}
class App extends React.Component {
    render() {
        return <Translate/>;
    }
}

export default App;

これだけだと、まだ言語切り替えが出来ません。先ほどのindex.tsxに書かれている<HeaderApp />の詳細を書いていきます。

./src/components/header.tsx
import React from 'react';
import { FunctionComponent } from "react";
import { LanugageSelector } from "./LanguageSelector";
import { useTranslation } from 'react-i18next'
import { useState } from 'react';
import { useEffect } from 'react';

const HeaderApp: FunctionComponent = () => {
    const [t, i18n] = useTranslation();
    const languages = [{ code: "ja", name: "JAPANESE" }, { code: "en", name: "ENGLISH" }, { code: "cn", name: "CHINESE" }];
    const [currentLang, setCurrentLang] = useState("ja");
    // Change language of i18n
    useEffect(() => {
        i18n.changeLanguage(currentLang);
    }, [currentLang, i18n])
    // Handler
    const handleLanguageSelectionChange = (lang: string) => {
        setCurrentLang(lang);
    }
    return (
        <React.StrictMode>
            <header>
                <div>
                    <ul>
                        <LanugageSelector languages={languages} selectedLanguage={currentLang} onLanguageSelectionChange={handleLanguageSelectionChange} />
                    </ul>
                </div>
            </header>
        </React.StrictMode>
    );

}

export default HeaderApp;

<LanugageSelector />の定義は下記です。

./src/components/LanguageSelector.tsx
import React, { FunctionComponent } from "react";

export type LanguageSelectorProps = {
    languages: { code: string, name: string }[],
    selectedLanguage: string,
    onLanguageSelectionChange: (lang: string) => void,
}

export const LanugageSelector: FunctionComponent<LanguageSelectorProps> = (props) => {
    const langs = props.languages.map((lang) => {
        return (lang.code === props.selectedLanguage) ? '' : <li><p onClick={() => props.onLanguageSelectionChange(lang.code)}>{lang.name}</p></li>
    });

    return (
        <div className="i18n">
            {langs}
        </div>
    );
}

この<LanguageSelector/>により、<p>ENGLISH</p><p>JAPANESE</p><p>CHINESE</p>が表示されます。これをクリックすると表示が切り替わる仕組みです。3項演算子で、「現在表示中の言語」は消しています。

再びのextract

ここまで書けたら、もう一度、先ほどのコマンドを実行してみます。

terminal
npm run i18next-extract
tree
./
┃
┣━ src
┃    ┣━ locales
┃    ┃    ┣━ cn
┃    ┃    ┃    ┣━ maincontent.json
┃    ┃    ┃    ┗━ translation.json
┃    ┃    ┣━ en
┃    ┃    ┃    ┣━ maincontent.json
┃    ┃    ┃    ┗━ translation.json
┃    ┃    ┗━ jp
┃    ┃    ┃    ┣━ maincontent.json
┃    ┃         ┗━ translation.json

ここまで来たら、おめでとうございます!
あとは、ひたすら翻訳を書いていくだけです。

まとめ

npm startの中でnpm run i18next-extractを動かすにはどうするのでしょう?

Excelsior!

5
2
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
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?