Google翻訳APIで複数テキストを処理可能にする

# はじめに

１年位前に、以下の記事

- [Google翻訳APIをPythonで実行する](https://qiita.com/kimisyo/items/ff0ae2aae97e4d8e3f65)

及びその参照記事

- [Google翻訳APIを無料で作る方法](https://qiita.com/satto_sann/items/be4177360a0bc3691fdf)
- [3 分で作る無料の翻訳 API with Google Apps Script](https://qiita.com/tanabee/items/c79c5c28ba0537112922)

を参考にさせていただき、無料のGoogle翻訳APIを作成し、便利に活用していた。
しかし、上記の記事のAPIは、翻訳テキストを１データしか処理できないので、今回、複数のテキストを処理できるように改良した。

# 元記事からのAPI変更点
`var p = e.parameters`として、リスト型のパラメータを受け取り、[Google Apps Script の LanguageApp を使う](https://qiita.com/a_eau_/items/92430bc9d749fb9c330a)を参考に、リストを`map`関数を用いて、`LanguageApp`で一括変換した。
ただ、この方法は`LanguageApp`をリストの要素ごとに呼び出すので、別の方法として、改行を入れて結合したり、何らかの置換を噛ませたりすることも考えられる。
Google Apps Script(GAS)については、[今から10分ではじめる Google Apps Script(GAS) で Web API公開](https://qiita.com/riversun/items/c924cfe70e16ee3fe3ba)も参考にした。

```javascript
function doGet(e) {
  return doPost(e);
}

function doPost(e) {
    // リクエストパラメータを取得する
    var p = e.parameters;
    // LanguageAppクラスを用いて翻訳を実行
    // var translatedText = LanguageApp.translate(p.text, p.source_lang, p.target_lang); // 元記事
    var translatedText = p.text.map(x => LanguageApp.translate(x, p.source_lang, p.target_lang)); // map関数
    // var translatedText = LanguageApp.translate(p.text.join("\n"), p.source_lang, p.target_lang).split("\n"); // 改行で結合
    // レスポンスボディの作成
    var body;
    if (translatedText) {
        body = {
          code: 200,
          text: translatedText
        };
    } else {
        body = {
          code: 400,
          text: "Bad Request"
        };
    }
    // レスポンスの作成
    var response = ContentService.createTextOutput();
    // Mime TypeをJSONに設定
    response.setMimeType(ContentService.MimeType.JSON);
    // JSONテキストをセットする
    response.setContent(JSON.stringify(body));

    return response;
}
```

# 元記事からのPythonコード変更点
入力ファイルの中身を、行ごとにリスト`input`の要素として追加している。
GOOGLE_API_URLには、上で作成したAPIのURLを指定する。

```translate.py
import argparse
import requests

GOOGLE_API_URL = "https://script.google.com/macros/s/****************************************/exec"

parser = argparse.ArgumentParser()
parser.add_argument("-input", type=str, required=True)
args = parser.parse_args()

input = []
with open(args.input, 'r',encoding="utf-8") as f:
    for line in f:
        line = line.strip()
        print(line)
        input.append(line)

params = {
    'text': input,
    'source_lang': 'en',
    'target_lang': 'ja'
}

r_post = requests.post(GOOGLE_API_URL, data=params)
result = r_post.json()
for c in result['text']:
    print(c)
```

#使い方

## 1.テキストファイルに文章を入力
テキストファイルの行ごとに別のテキストとして認識される。

```input.txt
Dragon Quest #3 is my favorite game.
Dragon Quest #5 is also my favorite game.
```

## 2.コマンド実行
```
$ python translate.py -input input.txt
```

## 3.コンソールに結果出力
以下のような形でコンソールに出力結果が得られる。

```
ドラゴンクエスト＃3は私のお気に入りのゲームです。
ドラゴンクエスト＃5も私のお気に入りのゲームです。
```


#処理時間計測
次に、今回作成したAPIの複数テキストの処理時間を計測した。
まず、10行で計649語の英文テキストと、100行で計5415語の英文テキストを準備し、それぞれの行を要素としたリストを作成した。
次に、10行と100行のそれぞれのリストを次の3つの方法で3回づつ処理し、処理時間を平均した。

- TEST01：リストをそのままAPIに投げて処理
- TEST02：リストの要素ごとにAPIに投げて処理
- TEST03：リストの要素を改行記号で結合してAPIに投げて処理し、得られた翻訳結果を改行記号で分離

結果は以下の通り。

|            | 10行(秒)  | 100行(秒) |API呼び出し回数|LanguageApp呼び出し回数|
|:-----------|----------:|:--------:|:--------:|:--------:|
| TEST01     | 1.14      | 2.78     | 1    | 行数 |
| TEST02     | 13.04     | 102.02   | 行数 | 行数 |
| TEST03     | 1.04      | 1.15     | 1    | 1   |

TEST03の改行記号で結合する処理が一番早い。10行と100行で処理時間があまり変わらないことから、API内の処理にはほとんど時間がかからないと思われる。
TEST01のリストをそのまま処理する方法は、TEST03よりは遅いが現実的な処理時間である。
TEST02の要素ごとの処理は、APIの呼び出し回数が多く、かなり遅い。
TEST03が最も早いが、テキスト内に改行記号が含まれている場合など、改行記号で結合する処理が難しい場合には、TEST01も現実的な処理方法になる。

計測に使用したテストコードは以下の通り。
なお、`LanguageApp`の呼び出し回数は一日5000回までのようなので、注意が必要である。
https://developers.google.com/apps-script/guides/services/quotas

```test.py
import requests
import time

GOOGLE_API_URL = GOOGLE_API_URL = "https://script.google.com/macros/s/****************************************/exec"

def test1(input):
    params = {
        'text': input,
        'source_lang': 'en',
        'target_lang': 'ja'
    }
    r_post = requests.post(GOOGLE_API_URL, data=params)
    result = r_post.json()
#    for c in result['text']:
#        print(c)

def test2(input):
    for c in input:
        params = {
            'text': c,
            'source_lang': 'en',
            'target_lang': 'ja'
        }
        r_post = requests.post(GOOGLE_API_URL, data=params)
        result = r_post.json()
#        print(result['text'][0])

def test3(input):
    s = '\n'.join(input)
    params = {
        'text': s,
        'source_lang': 'en',
        'target_lang': 'ja'
    }
    r_post = requests.post(GOOGLE_API_URL, data=params)
    result = r_post.json()
    cc = result['text'][0].split("\n")
#    for c in cc:
#        print(c)

input = []
#with open("sample_10lines_649words.txt", 'r',encoding="utf-8") as f:
with open("sample_100lines_5415words.txt", 'r',encoding="utf-8") as f:
    for line in f:
        line = line.strip()
        input.append(line)

print("=========TEST01=========")
start = time.time()
test1(input)
print(time.time() - start)

print("=========TEST02=========")
start = time.time()
test2(input)
print(time.time() - start)

print("=========TEST03=========")
start = time.time()
test3(input)
print(time.time() - start)
```

#おわりに

元記事のコードのように翻訳テキストを１データしか受け付けない場合でも、複数の文を空白や改行記号で結合すれば、一度に複数の文の翻訳を得ることができる。
ただし、複数のパラグラフのデータがあって、結合せずに翻訳結果を得たい場合などには、複数テキストをリストで受け取るやり方が都合がよい。
自分は、Google翻訳とDeepL翻訳を併用しているが、最近DeepL APIが複数テキストをリストとして受け取り可能であることがわかったので、今回Google翻訳APIも同じ処理ができるようにした。（参考：[DeepL APIで複数のテキストを一度に翻訳](https://qiita.com/mctk/items/825c315980d694015a46)）