選択した文字を翻訳する機能をiPhoneに付けてみた(Pythonista3, DeepL API)

## 概要
Pythonista3とDeepLの無料APIを使い、
iOS上で選択した文字列を翻訳するアプリを作ってみました。

文字列を選択し、「共有…」からアプリを起動することで翻訳することができます。
![34C05510-850C-461E-B2B9-E485CD82DCD0.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614475/c258666e-ce7a-26f2-f604-80573d4f2f70.jpeg)
![F84C82AA-C4AB-4347-893A-4901E21FAB58.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614475/109f33c6-7e6f-5ea2-b632-9611c3706340.jpeg)



## 実行環境
- iOS 12.5.4 (iPad mini3)
- Pythonista 3.3
- Python 3.6


## 事前準備
- App StoreからPythonista3をダウンロードしてください。(有料)
- [DeepL APIのページ](https://www.deepl.com/pro-api/)で会員登録を行い、DeepL API Freeの認証キーを取得してください。(無料でもクレジットカードの登録が必要です。)


## 使い方

pythonista3の中で適当なフォルダを作り、中に以下の2つのファイルを作成してください。

```python:translate_with_deepl.py
# coding: utf-8

import json
import requests
import urllib
import appex
from html2text import html2text
import console


def get_text_input_with_appex():
    text = appex.get_text()
    if not text:
        print('No text input found.')
        return
    text = html2text(text)
    text = text.replace("\n", " ")  # 改行があると訳がおかしくなるため、空白に置換
    return text


def post_to_deepl_api(text, target_lang="JA", auth_key_path="auth_key.json"):
    """
    DeepLのAPIにテキストを送信して翻訳を実行する関数。
    
    text : str
        翻訳したいテキスト。
        
    target_lang : str
        どの言語に翻訳するか。デフォルトはJA(日本語)。
        選べる言語は以下のドキュメントの target_lang を参照してください。
        https://www.deepl.com/docs-api/translating-text/request/
        
    auth_key_path : str
        認証キーが書かれたJSONのパス。
    """
    # 認証キーの読み込み
    with open(auth_key_path) as f:
        auth_dict = json.load(f)
        auth_key = auth_dict["auth_key"]

    # リクエストヘッダーの指定
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded',
        "Authorization": "DeepL-Auth-Key {}".format(auth_key)
    }

    # 送信するパラメータを指定し、コンテンツタイプに合うようにエンコードする
    params = {"text": text, "target_lang": target_lang}
    params = urllib.parse.urlencode(params)

    # DeepLのAPIにリクエストを送信する
    response = requests.post(
        url="https://api-free.deepl.com/v2/translate",
        headers=headers,
        data=params)

    return response


def main():
    if not appex.is_running_extension():
        print('This script is intended to be run from the sharing extension.')
        return

    text = get_text_input_with_appex()

    # DeepLのAPIにリクエストを送信する
    target_lang = "JA"
    response = post_to_deepl_api(text, target_lang=target_lang)

    # 翻訳結果を表示
    try:
        response.raise_for_status()  # ステータスコードが200以外の場合はエラー

        r_content = response.json()["translations"][0]
        translation = r_content["text"]
        detected_lang = r_content["detected_source_language"]
        console.alert(
            title="翻訳結果",
            message=f"入力した言語: {detected_lang}\n"
            f"翻訳した言語: {target_lang}\n"
            "\n入力した文章:\n" + text + "\n\n"
            "訳:\n" + translation,
            button1="OK",
            hide_cancel_button=True)

    except requests.exceptions.RequestException as e:
        # ステータスコードが200以外の場合はエラーを表示する
        console.alert(
            title="サーバーへリクエストを送信した際にエラーが発生しました。\n",
            message=f"ステータスコード: {response.status_code}\n"
            "エラー内容: \n" + str(e),
            button1="OK",
            hide_cancel_button=True)


if __name__ == '__main__':
    main()

```

```json:auth_key.json
{
  "auth_key": "取得した認証キー"
}
```

`auth_key.json`には先ほど取得したDeepL Free APIの認証キーを記述してください。(間違ってgithubに公開するなどして認証キーが流出しないよう、別ファイルに分けてあります。)

ファイルを作成したら`translate_with_deepl.py`を開いてください。右上のレンチマークを押して Shortcuts… を選び、Share Extentionを選びます。 
 
Edit ShortcutのCustom Titleは呼び出し時のメニューにアプリ名として表示されるので、分かりやすい名前を付けてください。

![E73F3D48-DF6C-474B-BB49-A920BAB219B4.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614475/bd57f57e-6cb3-c650-4464-307b9aaba43a.jpeg)
![6099BA2E-A086-4EF8-A495-843C4A3AE03B.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614475/cc18d885-a25c-9032-8a29-d61370474bde.jpeg)
![2AE90782-1F31-4B7D-B4E5-31F94022B9DE.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614475/88be0c66-fde5-6370-7f0a-8854ef52abb5.jpeg)

また、メニューの右下の歯車マークからSettingsを開き、Default Interpreterがpython3.6になっていることを確認します。

![D376F973-5791-478C-9958-F491B2286712.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614475/9041afdc-e0cf-b86b-7b38-8dac7b44ba25.jpeg)

![2E75B21A-AA86-4A93-A26B-00D41ED1583F.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614475/892f9061-9610-943a-7ca9-a31b2a239f96.jpeg)

これで準備完了です。

翻訳したいテキストを選択して
共有… → Run Pythonista Script → Custom Titleで設定した名前のアプリ  
の順に選ぶことで翻訳を実行できます。

![B73BDE72-1F9D-4A3F-B254-962F3AF66AE1.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614475/350b0a1e-e668-52ad-9be1-13e6a1abe9f0.jpeg)
![1DEAEA87-8F69-4B86-B4F9-EE4C0E33C041.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614475/b3cd64fb-5aa6-9f9b-0722-2a55c40ffdf7.jpeg)
![4D4895CF-C07F-4948-B79A-57643233D68E.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614475/99e155b5-6ce5-90f9-cea4-d78d58beaa9d.jpeg)
![656118CE-C07F-4CBD-AC93-684C87996520.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614475/be6cde49-2454-e944-f8f4-70e54a83d0ac.jpeg)


## Github
今回作ったアプリをGithubにも公開しました。
https://github.com/tnd5176/deepl_on_ios

pythonista3でGitを使う方法に関しては、以下を参考にしてください。
[Pythonista3でGithubにPushする方法 │ yhayashi30.org](https://yhayashi30.org/blog/2020/05/06/pythonista3-github-push/)
[iOSのGitクライアント Working Copy と Pythonista3 の組合わせが素晴らしい - a_sue’s diary](https://a-sue.hatenablog.com/entry/2018/11/04/135237)


## まとめ
pythonista3とDeepL API Freeを使ってiOS用の翻訳アプリを作りました。  
pythonista3のメニュー呼び出しとAPI通信に数秒かかるのがネックであるものの、わざわざDeepLのwebサイトを開かなくても翻訳できるようになりました。

このようにpythonista3ではiPhoneの機能を使ったアプリが作れるので、上手く使いこなせば色々効率化できそうです。


## 参考サイト
Pythonista3
[まだPythonista3使ってないの？ー基本操作・StaShインストール編ー - Qiita](https://qiita.com/n_oshiumi/items/4a647f2c8a3c209fd87b)

DeepL APIの公式サイト
[DeepL API](https://www.deepl.com/ja/docs-api/)

API通信関連
[Python3でBasic認証のあるAPIにx-www-form-urlencodedでPOSTし、Requestsとurllibを理解する - Qiita](https://qiita.com/TeruhisaFukumoto/items/0eb3a81e75ff8e029ab1)
[Python, RequestsでWeb APIを呼び出し（データ取得・操作） | note.nkmk.me](https://note.nkmk.me/python-requests-web-api/)
[HTTPとPOSTとGET - Qiita](https://qiita.com/Sekky0905/items/dff3d0da059d6f5bfabf)

エラー処理(ステータスコードが200以外の時用)
[Python API通信時の例外処理 - Qiita](https://qiita.com/d_kvn/items/5da7f5cdfc8200172a39)
[Pythonの例外処理（try, except, else, finally） | note.nkmk.me](https://note.nkmk.me/python-try-except-else-finally/)