見出し画像

生成AIで多言語翻訳データを用意し、React/JavaScriptアプリで世界市場に挑もう

生成AIが言葉の壁をどんどん取り壊しています。ボイスモード対応のサービスを使うと通訳してくれたり、日本語の動画コンテンツを英語で話してくれたり、言葉の習得はもはや不要なのか?という議論が出るほどの進化です。

ブラウザでブログを読むなら翻訳ツールが使えます。しかしアプリでは事情が違うかもしれません。ウェブアプリでもブラウザの翻訳ツールで訳せますが、訳に違和感があればユーザー体験が落ちてアプリの低評価につながるかもしれません。

そこで生成AIです。ChatGPTに代表される大規模言語モデル (LLM) はそもそも言葉に特化したAIで翻訳は初期の実用例です。

この記事は開発者向けになりますが、React (JavaScript) アプリの多言語化と翻訳データの準備手順を解説します。React多言語対応コードを解説した記事はすでにありますが、この記事で一気通貫で開発できるように私の事例を解説します。

私が開発したmonjuというアプリは最初日本語のみでした。それが日本語圏以外でバズった2日後には多言語化(当時21言語、現在は39言語)しました。なぜそのような短期間で実現できたのか、自分のノウハウを公開します。

ちなみにmonjuは当時Python (Streamlit) でしたが現在はReact版に改修しています。このReact版で解説しますので実用レベルのものと信じます。

英語だけでいいじゃないか?そんなわけありません。自分の母国語に対応していてくれたら親近感湧くじゃないですか。ただ無数に用意してもコスパが悪いので、私の感覚的に大体40言語対応していればグローバルといえそうです。

言語の壁は低くなっています。一人でも多くの方に世界に挑戦してほしい。特に個人開発者がもっと世界を目指してほしいという想いで書きました。


Reactプロジェクトの多言語対応

monju アプリの多言語切り替えの仕組み

上図は私がリリースしている「マルチAIブレインストーミングmonju」のディレクトリ構成です。オープンソースにしていないため抜粋したコードで紹介します。

App.jsで言語切り替えコンポーネントを常にヘッダーに表示

図の一番上、srcフォルダから見ていきます。App.jsにてメイン画面のHTMLと画面操作のコードを書きます。

このApp.js自体はメイン画面の管理が仕事であり、多言語表示を扱いません。ただし下の層の "LanguageSelector.js" をヘッダーとして読み込ませています。これによりアプリ起動中は常に言語切り替えができるようになっています。

また他の画面(上図ではInputScreen.jsなど)をコンポーネントとして読み込むこともApp.jsがやっています。

言語選択コンポーネントはドロップダウンリスト

srcフォルダの下にcomponentsフォルダがあり、さらに役割ごとにサブフォルダを用意しています。ここではscreenとlocalesのみ解説します。

LanguageSelector.jsは上図にあるような言語選択のドロップダウンリストを表示するコンポーネントです。ずばりこの部分のコードはこのようになります。

import { useTranslation } from 'react-i18next';
import '../../locales/i18n';
import '../../App.css';

const LanguageSelector = () => {
  const { i18n } = useTranslation();

  const changeLanguage = (event) => {
    const selectedLanguage = event.target.value;
    i18n.changeLanguage(selectedLanguage);
  };

  return (
    <header>
      <div className="input-language">
        <span>🌐LANGUAGE: </span>
        <select id="language-select" onChange={changeLanguage} value={i18n.language}>
          <option value="en">English</option>
          <option value="ja">日本語</option>
          {/* その他の言語もここに追加 */}
        </select>
      </div>
    </header>
  );
}

export default LanguageSelector;

ここでは抜粋で日本語と英語のみ記載しています。他の言語でも同じように追加実装していきます。

多言語を扱うには外部ライブラリを使うのが便利です。下記のライブラリを開発時にインストールしました。

npm install react-i18next

他の記事では react-i18next だけでなく i18next を別に指定しています。ただ私の場合は react-i18next だけでうまくいきました。違っていたらすみません。

useTranslation を react-i18n から呼び出します。言語切り替えを行う重要なライブラリです。ここで呼び出されている {i18n} はアプリに実装されている言語情報や現在選択中の言語を保持しています。

ドロップダウンリストで現在表示されている言語と違う言語が選択されると onChange にトリガーがかかり changeLanguage 関数が呼ばれます。関数内には  selectedLanuage 変数がありますがこれは i18n.language (リストで選ばれた言語)のことです。言語選択後に即時切り替えが起きます。

このようにLanguageSelectorは言語切替処理に特化したコンポーネントです。本当はヘッダーに固定表示されるのでheaderというワードも入れないと混乱するのですがそのままにしました。

i18n.js: 言語情報の中心部

構成図の一番下に行きます。 i18n.js が言語切替のコアの部分です。monjuアプリの実際のコードを抜粋するとこのようになります。

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

import enTranslations from './en.json';
import jaTranslations from './ja.json';

const getBrowserLanguage = () => {
  // ブラウザの言語設定を呼び出して自動切換え(起動時のみ)
  const supportedLanguages = [
    'en', 'ja'
  ]; // 他の言語コードも追加

  // 言語コードの最初の2文字を取得 (例: "ja-JP" -> "ja")
  const browserLang = navigator.language || navigator.userLanguage;
  const shortLang = browserLang.split('-')[0];

  return supportedLanguages.includes(shortLang) ? shortLang : 'en';
};

i18n
  .use(initReactI18next)
  .init({
    resources: {
      en: { translation: enTranslations },
      ja: { translation: jaTranslations }
    },
    lng: getBrowserLanguage(),
    fallbackLng: 'en',
    interpolation: {
      escapeValue: false
    }
  });

export default i18n;

i18n, initReactI18next はライブラリをそのまま使います。何も考えずにそのままコピペしましょう。ここではライブラリの解説はしません。

次に各言語で書かれた文のJSONファイルを import します。ここでは英語と日本語だけですが、実際には39言語分=39行あります。JSONについては後ほど解説します。

import の enTranslations の部分はこちらで定義したものですが、他の言語についてはこのまま最初の2文字だけ変えればいいでしょう。

基本的に言語コードは ISO 639-1 に定義された2文字だけを使います。

getBrowserLanguage はブラウザで使われている言語情報を取り出して、アプリの言語を自動で切り替えます。アプリ起動時の一回だけ呼ばれます。

この機能は大事です。先ほどのドロップダウンリストで選択された言語を使って、アプリはブレインストーミングのコンテンツを生成します。ブレインストーミングのテーマを日本語で書いても言語選択が English だと英語で結果を出してしまいます。

ユーザーさんは知らずに日本語でテーマ入力、言語を English のまま使っているケースがありましたし、私自身もこの自動切換えがないと同じことをやってしまいます。ご不便をおかけしますのでこの機能は大事です。

getBrowserLanguage では「言語コードの最初の2文字を取得」する処理があります。この関数内の supportedLanguages 配列には対応済み言語を2文字で入れていきます。

そして本丸の i18n です。このクラスは i18next から持ってきています。そのままコピペして必要な部分だけ追加編集します。

resources にはJSON形式で対応済み言語の ISO コードと、 import で定義した enTranslations 形式のオブジェクトを指定します。

lng には初期設定としてすでに解説したブラウザ言語の情報をセットします。非対応言語だった場合は英語 (en) をセットするように getBrowserLanguage にて処理してあります。

そして念のために fallbackLng にも英語を使うように設定してあります。この部分も resources にない言語が指定された場合の対応処理です。

各ページでの表示切替

いよいよ画面表示の部分です。説明をシンプルにするため仮にこのようなコードでやってみましょう。入力画面を想定して InputScreen.jsとしましょう。

import { useTranslation } from 'react-i18next';
import '../../App.css';

const InputScreen = () => {
  const { t } = useTranslation();
  const [theme, setTheme] = useState('');

  return (
    <div className="input-screen">
      <input
        type="text"
        id="prompt"
        placeholder={t('input.theme')}
        value={theme}
        onChange={(e) => setTheme(e.target.value)}
      />
    </div>
  );
};

export default InputScreen;

ここでも useTranslation を使います。しかし LanguageSelector では i18n をインポートしていましたが、ここでは const {t} だけです。この t が言語を切替してくれます。

上記のコードでは1例だけです。テーマ入力のテキストボックスのプレイスホルダー表示を切り替えます。

プレイスホルダーの表示切替の例

すでに言語設定済みなので各ページ・フレームではこの t を呼び出すだけです。どの文章を呼び出すかはJSONのキー指定、ここでは

{"input": {"theme": "Theme or Topic"}}

の部分を input.theme で呼び出しています。

およそ文字表示ができる部分はこの t 関数で切替できます。上記の例のようなプレイスホルダーだけでなくリストやヘッダー、ボタン表示もできます。

懸念事項:同一言語の派生対応

ここまで紹介したやり方はシンプルで実装しやすいものと信じています。ただしこのやり方には懸念があります。

例えば中国語の場合は簡体字 (Simplified) と繁体字 (Traditional) があります。言語コード上はどちらも zh なのですが、monjuでは zh_HANS と zh_HANT のふたつを用意しています。おそらく自動読み込みはどちらかに固定されてしまいます。アルファベット順に設定しているので先にくる簡体字 (S) が読まれるでしょう。

細かい話をすると、アメリカ英語とイギリス英語は厳密には en_US, en_GB に分かれます。さらにほかのマイナー言語まで対応するときりがありません。monjuはアメリカ英語を採用しています。

全対応するには2文字のコードではなくて en_US のようなフルコードで使う必要があります。しかし JavaScript の navigator.language ではフルで返すブラウザと2文字しか返さないブラウザに分かれます。全ブラウザ対応は流石に大変すぎます。

そのため中国語圏、英語圏のユーザー様には申し訳ないですがここではこれ以上の実装をしません。

各言語のJSON文章用意と生成AIを使った翻訳

Cursor + Google 翻訳による翻訳データの作り方(フランス語の例)

ここからは多言語文章(文言)データ生成の手順を解説します。図に示すとこのようになります。

やることとしては3つ「英語JSONの用意」、「AIで翻訳」、「Google翻訳でチェック」です。

英語JSONをしっかり用意すればあとは単純作業

英語が苦手な方にとってはここが一番のネックかもしれません。しかし翻訳の精度を上げるにはこれがベストです。

翻訳元のJSONをまずは英語で作ります。AI翻訳はJSONの構造には触らず中身の文章だけ翻訳してくれます。なので翻訳時は翻訳先の言語だけ指定すれば、退屈ですが単純作業で精度良く翻訳できます。

例を紹介します。英語(と日本語)でJSONを作ります。下記は抜粋です。

{
  "input": {
    "theme": "Theme or Topic",
    "num_ideas": "Number of ideas to generate by each AI",
    "freedom": "Freedom (higher the more creative)",
    "start": "START"
  },
  "result_sidebar": {
    "reset": "HOME",
    "ideas": "Idea List",
    "mindmap": "Mindmap",
  }
}
{
  "input": {
    "theme": "テーマまたはトピック",
    "num_ideas": "AIに出させるアイデア数",
    "freedom": "自由度(大きいほど活発)",
    "start": "開始"
  },
  "result_sidebar": {
    "reset": "ホーム",
    "ideas": "アイデアリスト",
    "mindmap": "マインドマップ",
  }
}

JSONのキー (input, result_sidebar, etc) に欠けがあるとアプリはエラーになります。見落としを防ぐために必ず日英ペアで作っておくことをお勧めします。

英語で準備ができなければ日本語ベースでも良いが、ちゃんと「言語化」すること

大事なことなので繰り返しますが、原文は英語で準備することをお勧めします。

日本語は描写文といって多少単語が欠けていても想像で補うことができる言語です。しかし英語は説明文です。単語が欠けると文章が成立しません。よく言われる例は川端康成『雪国』の冒頭文です。

  • 日本語(原文):国境の長いトンネルを抜けると雪国であった。

  • 英語(訳文): The train came out of the long tunnel into the snow country.

英語は主語の Train (鉄道)がないと文として成立しません。しかし日本語にはこの単語がなくても成立するどころか「情緒をかきたてる」のです。

文学では良いですが技術文書ではこうはいきません。読者に誤解を与えないように必要最小限な単語(特に名詞と動詞)を与えないといけません。このような科学技術向けの英文の書き方を技術英語(以前は工業英語)と呼びます。

もしどうしても英語での準備が難しければ日本語でもいいです。しかし単語の抜けやあいまいな表現を避けることを理解しましょう。開発者の意図を正しく言語化してください。

抜けがないか、あいまいさがないかを確認するために生成AIを使ってもいいですし、Google翻訳で英語と日本語を交互に訳して同じ結果が出るまで繰り返してもいいです。ただ正確なニュアンスかどうかまでは保証できません。

翻訳するならCursorエディタ一択

ここでいよいよ生成AIの登場です。言葉を操る大規模言語モデル (LLM) にとって翻訳はまさに得意な作業です。使わない手はありません。

私がお勧めするのはなんといっても Cursor エディタです。昨今の生成AI界隈では知らない人はいないでしょう。そのため Cursor とは何かという説明はしません。

使い方です。Cursor で開発プロジェクトをフォルダごと開き、右側のサイドバーにチャットを呼び出します。ファイルメンションで英語のJSONを指定 (@en.json) して下記のプロンプトで翻訳させます。

Could you translate attached JSON into fr of ISO 639-1 suitable for i18n purpose?

作業指示は日本語である必要がないのでこのプロンプトをコピペしましょう。上記の fr (フランス語) を他の言語コードにすれば繰り返し使えます。

Cursorによる翻訳作業画面(monjuの開発画面より)

上図は実際に英語からフランス語に翻訳した結果です。AIはJSONの構造を残したまま、中身の文章だけをフランス語にしてくれます。

生成結果は Apply ボタンでメイン画面のコード編集エリアに移します。ありがたいことに元のファイルを en.json としておくと、フランス語は自動的に fr.json と命名してくれます。そしてlocalesフォルダに保存します。

これを残りの言語でも繰り返します。先ほどのプロンプトでドイツ語なら de, アラビア語なら ar とします。西欧言語だけでなくビルマ語(ミャンマー語)やタミル語、ペルシャ語のような言語でも対応してくれます。

おすすめAIモデルは Claude 3.5 Sonnet です。実際に monju の多言語化はすべてこのモデルで対応しました。

以前は Claude のブラウザチャットで翻訳作業をやっていました。これでもできますがコピペや保存といった作業はブラウザとエディタ画面をまたがるので面倒です。Cursorを使えば同じ画面で完結します。素晴らしい!

念のためGoogle翻訳で確認

AIがやるとはいえ正しく翻訳してくれたか不安は残りますね。そのため私は最終チェックとして Google 翻訳で確認します。

AIで確認すればいいじゃん?という突っ込みがきそうです。同じツールを使うより、他のツールを使っても同じ翻訳結果になる方が正確度が増します。

上の図はフランス語のJSON構造を残したまま Google 翻訳の入力側に入れています。出力は日本語でもいいですが、JSONキーまで翻訳されてしまうので私は英語に翻訳します。そして元のen.jsonの文と同じ結果になるかどうか確認します。

Google 翻訳の良い点として他には「言語の自動検出」があります。入力元言語を指定せずに「言語を検出する」とし、JSONをコピペするだけでどの言語か検出してかつ翻訳する。検証ツールとしていいですよ。

一部翻訳が違うケースもあります。しかし多くは同じ意味の別の単語か、もしくは「辞書にない単語」です。

例えばブレインストーミングという単語はマイナー言語では英語化してもBrainstormingに戻りません。そういうケースはうまくいっていると信じるしかありません。

まとめ

今回紹介した方法で作った多言語データの一例(monju.ai)

以上、私はこのやり方で39言語用意しました。monjuというアプリはアラビア語でよく使われていますが「訳がおかしい」といったクレームは今のところありません。

私のXのアカウント名は habatakurikei, つまり「(世界に)羽ばたく理系」です。2014年に一大決心して日本を出て「海外にエンジニアとしてキャリアアップするんだ!」という当時の想いをアカウント名に込めました。

ここで紹介した方法を使って多言語化し、日本発のグローバルアプリをどんどん発信して盛り上げて行きましょう!

【補足】monjuで対応した全39言語

英語 (en), 日本語 (ja), アラビア語 (ar), ベンガル語 (bn), チェコ語 (cs), デンマーク語 (da), ドイツ語 (de), ギリシャ語 (el), スペイン語 (es), ペルシャ語 (fa), フィンランド語 (fi), フランス語 (fr), ヘブライ語 (he), ヒンディー語 (hi), ハンガリー語 (hu), インドネシア語 (id), イタリア語 (it), ジャワ語 (jv), 韓国語 (ko), マレー語 (ms), ミャンマー語(ビルマ語) (my), オランダ語 (nl), ノルウェー語 (no), パンジャーブ語 (pa), ポーランド語 (pl), ポルトガル語 (pt), ルーマニア語 (ro), ロシア語 (ru), スウェーデン語 (sv), スワヒリ語 (sw), タミル語 (ta), テルグ語 (te), タイ語 (th), トルコ語 (tr), ウクライナ語 (uk), ウルドゥー語 (ur), ベトナム語 (vi), 簡体字中国語 (zh_HANS), 繁体字中国語 (zh_HANT)

【参考】ISO 639-1コード一覧

【多言語対応アプリ】マルチAIブレインストーミングmonju


いいなと思ったら応援しよう!