VBA）DeepL API を使って翻訳してみる

# 背景

これは「[自由研究室 AIRS-Lab](https://www.airs-lab.jp)」の [Advent Calendar](https://qiita.com/advent-calendar/2021/airs-lab)、22日目の記事です。

C# で DeepL API を使った翻訳ツールを作った私だったが、VBA でも同様のことができないか？との要請があり、いったんは難しいのじゃないかと諦めかけたが、よくよく考えたら難しい理由がわからなかったので、なんとなく調べたらできるようになった。せっかくなので、記事としてまとめることとした。そんな感じ。

C#の記事はこちら↓
[C#）DeepL API で翻訳した結果をボイスロイドにしゃべってもらいたい！（１）](https://qiita.com/fuji_t/items/4d9ca45006ce0c2aaa32)
[C#）DeepL API で翻訳した結果をボイスロイドにしゃべってもらいたい！（２）](https://qiita.com/fuji_t/items/2ff27f9d73a9893c0b86)

# やりたいこと
Excel VBA で DeepL API を使って翻訳したい。

【最終的な完成イメージ】
<iframe width="560" height="315" src="https://www.youtube.com/embed/Alz_9O87rwk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

# DeepL
## DeepL API 認証キーの取得
DeepL API を利用するには、認証キーを取得する必要がありますので、事前に準備を済ませておきましょう。（[参考](https://qiita.com/fuji_t/items/4d9ca45006ce0c2aaa32)）

# Excel
## Excel シート
このような感じでソーステキストの入力欄と翻訳結果の表示欄を作りましょう。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276318/7124c745-36ef-98b6-6acf-4cc7e29a7085.png" width="64%">

## VBA
では、VBAを書いていきましょう。

### 参照設定
まずは参照設定をしておきます。「Microsoft XML, v6.0」のチェックを入れておきましょう。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276318/b011ee24-8c17-418f-9d34-9be5be902fab.png" width="64%">


### オブジェクト：Sheet1
#### Worksheet_Change()
ワークシートのチェンジイベントに、ソーステキストに入力されたら翻訳結果をクリアする処理を組み込んでおきます。今回は面倒だったのでセル番地を直書きしちゃってます。

```vb:Worksheet_Change()
Private Sub Worksheet_Change(ByVal Target As Range)

    If Target.Address(False, False) = "B3" Then
        Range("D3").ClearContents
    End If

End Sub
```

### オブジェクト：Module1
標準モジュールを追加し、メインのコードを書いていきます。

#### 認証キー
認証キーを定数として宣言しておきましょう。ここでは xxxx… としていますが、実際は取得した認証キーを書きます。

```vb
Const AUTH_KEY As String = "xxxxxxxxxxxx"
```

#### 翻訳部分
ソーステキストを受け取って翻訳結果を返す処理です。HTTP REQUEST で文字列を送って結果を受け取っているだけですね。

```vb:deepL_translate()
Private Function deepL_translate(ByVal srcText As String) As String
    Dim httpReq As XMLHTTP60
    Set httpReq = New XMLHTTP60
    
    Call httpReq.Open("POST", _
                      "https://api-free.deepl.com/v2/translate?auth_key=" & AUTH_KEY & _
                      "&text=" & srcText & "&target_lang=ja")
                      
    Call httpReq.send
    
    Do While httpReq.readyState < 4
        DoEvents
    Loop
    
    deepL_translate = httpReq.responseText
    
    Set httpReq = Nothing

End Function
```
srcTextに「test」を渡して翻訳を実行すると、次のようなJSON形式で結果が返ってきます。
`{"translations":[{"detected_source_language":"EN","text":"テスト"}]}`
ここから「テスト」の部分を取り出す必要があります。

#### 翻訳結果のテキスト抽出
本来はJSONをきちんと扱ってやった方がよいのでしょうが、面倒なので力業で処理しちゃいました。簡単に言うと、`{"translations":[{"detected_source_language":"EN","text":"テスト"}]}` から、文字列 `text` の位置を調べて、その位置を起点に必要な部分を取得しています。

```vb:getResultText()
Private Function getResultText(ByVal resText As String) As String
    Dim pos As Long
    Dim res As String
    
    pos = InStr(resText, "text")
    
    If pos > 0 Then
        res = Mid(resText, pos + Len("text") + 3)
        res = Left(res, Len(res) - 4)
    Else
        res = ""
    End If
    
    getResultText = res
    
End Function
```

#### メインルーチン
【翻訳実行】ボタンを押したときの処理です。上で作った処理を呼び出して最終結果を翻訳結果のセルに表示させています。【翻訳実行】ボタンにマクロを登録しておきましょう。

```vb:翻訳実行()
Public Sub 翻訳実行()
    Dim srcText As String
    Dim res As String
    
    srcText = Range("B3").Value
    srcText = WorksheetFunction.EncodeURL(srcText)    '追記：「&」があると正しくテキストを渡せないと教えていただきました。
    
    res = deepL_translate(srcText)
    
    Range("D3").Value = getResultText(res)
    
End Sub
```

## 完成！？
これで完成です！
で、、英語 → 日本語翻訳はできたけど、日本語 → 英語翻訳は…？ 
`target_lang=ja` を `target_lang=en` にすればいけるかも？と思いきや、実は日本語文字列を UTF-8 に変換してから渡す必要があります。この記事では触れませんが、興味のある方は「VBA UTF-8 変換」あたりでググってみてください。
（WorksheetFunction.EncodeURL()を使ったらできました。）

# 参考にさせていただいたサイト
いつも隣にITのお仕事：[エクセルVBAでHTTPリクエストをする最も簡単なプログラム](https://tonari-it.com/excel-vba-http-request/)