そこそこ実用的な翻訳アプリを開発してみる (3) 翻訳サービスの実装

そこそこ実用的な翻訳アプリを開発しましたので、その構成や作り方を記録しておきます。[コード](https://github.com/yamachan/rtk-lt) は MITライセンスですので、参考になるところがあれば部分的にでも使ってみてください。

# 目次

* [(1) 概要編](https://qiita.com/yamachan360/items/5b20e7181d663d70e924)
* [(2) 開発環境の準備](https://qiita.com/yamachan360/items/d5c4fb2e07d3b9693c37)
* (3) 翻訳サービスの実装 【このページ】
* [(4) ログサービスの実装](https://qiita.com/yamachan360/items/92699a3fc55969337b46)
* [(5) フロントエンド側の実装](https://qiita.com/yamachan360/items/de57df20dc7de5665a44)
* [(6) 学習用データの作成](https://qiita.com/yamachan360/items/7de33c30331745b0d591)

# 翻訳機能を実装しよう

## 翻訳サービスの作成と接続

[IBM Cloud](https://console.bluemix.net/registration/free?cm_sp=dw-bluemix-_-jp-_-devcenter) のカタログで、AI カテゴリにある Language Translator をクリック。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/e4270f6e-0652-dfb4-e8de-a2a966d9b461.png)
価格プランが「ライト」なのを確認して、そのまま右下の「作成」ボタンをクリック。入門チュートリアルが表示されるのでよく読み、またブックマークしておきましょう。これで翻訳サービスが作成されました。

次にダッシュボードに戻り、さきほどアップロードしたNode.jsアプリを選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/8ecf47a3-eaff-6de9-c0b9-3edf8eedebd6.png)
「接続」メニューにある「接続の作成」をクリック。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/6f124b8e-2a25-b787-41b0-f977b0e5ce4c.png)
さきほど作成した Language Translator サービスが表示されるので「接続」をクリック。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/790bf8ff-42ef-f842-b580-475a96d7bf2f.png)
IAMサービスの接続に関するポップアップが表示されるので「接続」をクリック。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/175f8f5b-77b0-a6df-5247-f047bf0619c1.png)
アプリの再ステージに関するポップアップが表示されるので「再ステージ」をクリック。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/ef1d2a6a-6e03-b167-65c7-93e336af0d4c.png)
アプリの概要ページに戻り、再ステージが問題なく完了して「実行中」の表示に戻っていることと、接続の欄に Language Translator サービスが追加されていることを確認しましょう。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/c937ebf0-3b19-05c1-53ed-278cd889ac56.png)
これで翻訳サービスの作成と、rtk-lt アプリへの接続は完了です。

## 翻訳サービスのアプリへの組み込み

Node.js 環境では [Watson APIs Node.js SDK](https://www.npmjs.com/package/watson-developer-cloud) モジュールを使用します。まずは組み込みましょう。

```
npm install --save watson-developer-cloud
```

【追記】いまは [ibm-watson](https://www.npmjs.com/package/ibm-watson) というモジュールに更新されています

## シンプルに翻訳サービスを呼び出してみる

[Watson APIs Node.js SDK](https://www.npmjs.com/package/watson-developer-cloud) にある LanguageTranslator 用のサンプルロジックを、わりとそのまま Server.js の中に追加してみます。

```javascript:Server.js
const LanguageTranslatorV3 = require('watson-developer-cloud/language-translator/v3');

let lt_apikey = '<your apiKey>';
try {
	let lt_obj = JSON.parse(process.env.VCAP_SERVICES);
	if (!!lt_obj.language_translator[0].credentials.apikey) {
		lt_apikey = lt_obj.language_translator[0].credentials.apikey;
	}
} catch (e) {}

languageTranslator.translate(
	{
	    text: 'A sentence must have a verb',
		source: 'en',
		target: 'ja'
	},
	function(err, translation) {
		if (err)  {
			console.log('error:', err);
		} else  {
			console.log(JSON.stringify(translation, null, 2));
		}
	}
);
```

translate 関数に関しては、英語ですが [こちら](https://cloud.ibm.com/apidocs/language-translator?code=node#translate) の API ドキュメントが非常に判りやすいです。

環境変数である `VCAP_SERVICES` を参照しているところは独自に追加した部分です。IBM Cloud 上でアプリにサービスを「接続」すると `VCAP_SERVICES` という環境変数にいろいろな値を設定してくれる仕組みがあるんですよね。それを利用しているって訳です。

VCAP_SERVICES にどんな値が指定されるか知りたい方は、IBM Cloud でアプリの「ランタイム」にある「環境変数」を参照してみてください。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/79593ab8-aa82-2f48-cc99-80b7f0b8e575.png)
さて、これでサービス利用に必要な情報(apiKey)が得られる訳ですから、上記コードは IBM Cloud 上では動作するはずです。以下のように cf push した後、ログを表示して確認してみましょう。

```
cf push -m 128M rtk-lt
cf logs rtk-lt --recent
```

プログラム中に記述された `'A sentence must have a verb'` という英文に対する日本語訳が、ちゃんとログに表示されているのがわかります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/0d4e5f75-0dfb-a07a-497c-9001b0a34dfe.png)
なおローカル環境で動作させる場合は、環境変数 `VCAP_SERVICES` がありませんので、他の方法で apiKey の値を設定しておく必要があります。

さきほどのランタイムの環境変数から、もしくは「接続」から資格情報を表示させて apiKey の値を入手します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/90e1da0d-13c2-6531-f571-924805376fc0.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/c4b726d2-f349-f65d-51f8-861ba2337019.png)
入手した apiKey の値をプログラム中の `'<your apiKey>'` 部分に設定すれば、ローカル環境でも翻訳機能が動作するようになります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/2bbf56e7-64ee-ee5e-0b4c-27829af8ecf6.png)

## 翻訳サービスのAPI化

翻訳サービスの呼び出しに成功したので、ロジックを修正してAPI化します。まずは POST アクセスを処理するため body-parser モジュールを導入。

```
npm install --save body-parser
```

`languageTranslator.translate()` を実行していた部分を以下のように書き換えて POST API アクセス対応にします。

```javascript:Server.js
const bodyParser = require('body-parser');

app.use(bodyParser.json({strict: true}));
app.post('/translate', (req, res) => {
	console.dir(req.body);
	languageTranslator.translate(
		{
			text: req.body.text,
			source: req.body.source,
			target: req.body.target
		},
		function(err, translation) {
			if (err)  {
				res.status(500).send('error:' + err);
			} else  {
				res.status(200).send(translation.translations[0].translation);
			}
		}
	);
});
``` 
curl コマンドを使って、ローカル環境で POST API の動作を確認してみましょう。送った英文が日本語訳されているのがわかります。

```
curl http://localhost:3000/translate -H "Content-Type: application/json" -d '{"source":"en","target":"ja","text":"This is a pen."}'
```

Windows コマンドプロンプトの場合、json の中身をエスケープする必要があり以下を使用してください。

```
curl http://localhost:3000/translate -H "Content-Type: application/json" -d "{\"source\":\"en\",\"target\":\"ja\",\"text\":\"This is a pen.\"}"
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/87ebc557-eedb-0a0d-a1f0-6f872150dba8.png)

※ Windows コマンドプロンプトの場合、返された日本語が文字化けすることがあります。この場合には `chcp 65001` コマンドで utf-8 モードに切り替えてください。`chcp 932` で元に戻ります。

cf push して IBM Cloud 上でも同じように POST API が利用できるのが確認できます。

```
curl https://rtk-lt.mybluemix.net/translate -H "Content-Type: application/json" -d '{"source":"en","target":"ja","text":"This is a pen."}'
```

Windows コマンドプロンプトはこちら。

```
curl https://rtk-lt.mybluemix.net/translate -H "Content-Type: application/json" -d "{\"source\":\"en\",\"target\":\"ja\",\"text\":\"This is a pen.\"}"
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/7c523bc5-bdbf-5b4d-4b8f-31640bc17449.png)

まあ API と言っても、受け取った POST のデータをそのまま、翻訳サービスに投げているだけで、たいした処理はしていません。なのでコードもめっちゃシンプルです。

## 学習モデル

ついでに、今回の翻訳に使用するモデルの情報を表示させておきましょう。以下のコードを追加します。

```javascript:Server.js
languageTranslator.listModels(
  {},
  function(error, response) {
    if (response.models) {
      console.dir(response.models.filter(m => m.model_id.indexOf('ja') >= 0 ));
    }
  }
)
```

Server.js を実行すると、以下のように日英/英日の翻訳モデルの情報が表示されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193519/f0810275-f537-1d4e-1069-f5ef0235541c.png)
※ 実はモデルは様々な言語用に数多く定義されていて、`filter`関数で日本語関連の2つのモデルだけを抜き出しています

どちらの customizable の値も true で、学習可能であることを確認できました。確認後は上記のコードを削除するか、コメントアウトしておきましょう。

# というわけで

さて、これで Node.js アプリに、翻訳を実施する API を追加することができました。次回は [(4) ログサービスの実装](https://qiita.com/yamachan360/items/92699a3fc55969337b46) を実施します。GitHub 上の [ソースコード](https://github.com/yamachan/rtk-lt) も参照してみてください。