2
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[Python]EelとGoogletransを使ってお手軽Google翻訳アプリ

Last updated at Posted at 2021-01-04

はじめに

Googletrans APIを使ってGoogle翻訳簡易verを作ってみました。

実行環境

  • Windows10
  • Python3.8
  • Eel 0.14.0
  • Bootstrap4
  • Vue.js 2.6.12

どうやらEelはPython3.9系未対応のようです。

こんな感じの完成物

ぷち翻訳 2021_01_04 17_42_21.png

Eelの使い方

簡単にPythonでGUI作れるんですね。
[[Python] EelをつかってHTML/CSS/JavaScriptでGUIを構築][1]
[1]:https://qiita.com/inoory/items/f431c581332c8d500a3b

Googletransでお手軽Google翻訳

とりあえず実行。
[Python – googletransを試してみました。][2]
[2]:https://dev.classmethod.jp/articles/python-py-googletrans/

が、下記エラーが出ます。
AttributeError: 'NoneType' object has no attribute 'group'
上手くいったりいかなかったりという謎エラーなようです。

以下を参考にさせていただいてクリア。
[【python】googletransの『AttributeError: 'NoneType' object has no attribute 'group'』対策【2020/12/02追記】][3]
[3]:https://qiita.com/_yushuu/items/83c51e29771530646659

translate.py
from googletrans import Translator

class MyTranslator:
    @staticmethod
    def translate(org_text,src_lang,dest_lang):
        tr = Translator(service_urls=['translate.googleapis.com'])
        # エラーが出る時と出ない時があるので、
        # 成功するまでインスタンスを作り直してから再度実行
        i = 0
        while i < 10:
            try:
                return tr.translate(org_text, src=src_lang, dest=dest_lang).text
            except Exception as e:
                tr = Translator(service_urls=['translate.googleapis.com'])
                i+=1

BootstrapできれいめGUIにする

index.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ぷち翻訳</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css"
        integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
</head>

<body>
    <div class="container p-5" id="app">

        <!-- 日英or英日切り替えラジオボタン -->
        <form>
            <div class="form-group">
                <div class="custom-control custom-radio">
                    <input type="radio" name="lang_radio" class="custom-control-input" id="custom-radio-1"
                        v-model="trans_str" value="翻訳" checked>
                    <label class="custom-control-label" for="custom-radio-1">
                        日本語 → English
                    </label>
                </div>
                <div class="custom-control custom-radio">
                    <input type="radio" name="lang_radio" class="custom-control-input" id="custom-radio-2"
                        v-model="trans_str" value="Translate">
                    <label class="custom-control-label" for="custom-radio-2">
                        English → 英語
                    </label>
                </div>
            </div>
        </form>

        <!-- 翻訳ボタン -->
        <button id="translate-btn" class="btn btn-primary btn-block mb-2" type="button">
            {{trans_str}}
        </button>

        <!-- ユーザー入力 -->
        <div class="form-group mb-2">
            <textarea class="form-control" id="input" rows="3" placeholder="Please input..."></textarea>
        </div>
        
        <!-- 翻訳結果 -->
        <div class="form-group">
            <textarea class="form-control" id="output" readonly rows="3"></textarea>
        </div>

    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="vue.js"></script>
    <!-- eelを使用 -->
    <script type="text/javascript" src="/eel.js"></script>
    <script type="text/javascript" src="js/main.js"></script>
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
</body>

</html>

ついでにVue.jsデビュー

日英、英日のラジオボタンでの切り替えに合わせて、「翻訳ボタン」のラベルを変えます。
[Vue.jsでラジオボタンをデータバインディングする][4]
[4]:https://www.paveway.info/entry/2020/03/10/vue_vmodelradiobutton

main.js
// 翻訳関数をpythonから呼ぶ
async function translate(src, src_lang, dest_lang) {
    let ret = await eel.translate(src, src_lang, dest_lang)();
    return ret;
}

// 翻訳ボタンクリックイベント
var translate_btn = document.getElementById('translate-btn');
var lang_radio_btn = document.getElementById( "custom-radio-1" );
translate_btn.addEventListener('click', () => {
    var src_lang = 'en'
    var dest_lang = 'ja'
    // 日英or英日 切り替え
    if ( lang_radio_btn.checked ) {
        var src_lang = 'ja'
        var dest_lang = 'en'
    } 
    let original = document.getElementById('input')
    let translated = document.getElementById('output')
    if (original.value) {
        promise = translate(original.value, src_lang, dest_lang);
        promise.then((ret) => {
            translated.value = ret
            });
        }
})

// 翻訳ボタンの文字列をVueで操作
Vue.config.productionTip = false
var app = new Vue({
    el: '#app',
    data: {
        trans_str: '翻訳'
    }
})

Pythonから実行する

@eel.exposeを付けた関数がJavascriptから呼びだされます。

view.py
import eel
import desktop
import numpy as np
import pandas as pd
from translate import MyTranslator

@eel.expose
def translate(src, src_lang, dest_lang):
    return MyTranslator.translate(src, src_lang, dest_lang)

def main():
    start_dir="web"
    end_point="index.html"
    size=(600,400)
    desktop.start(start_dir, end_point, size)

if __name__ == "__main__":
    main()

[Eel]起動時のオプション設定

desktop.py
# -*- coding: utf-8 -*-

import eel
import sys
import socket 

# 定数
# ENTRY_POINT = 'index.html'
CHROME_ARGS = [
    '--incognit',  # シークレットモード
    '--disable-http-cache',  # キャッシュ無効
    '--disable-plugins',  # プラグイン無効
    '--disable-extensions',  # 拡張機能無効
    '--disable-dev-tools',  # デベロッパーツールを無効にする
]
ALLOW_EXTENSIONS = ['.html', '.css', '.js', '.ico']

def start(start_dir, start_page, size):  
    # 画面生成
    eel.init(start_dir, allowed_extensions=ALLOW_EXTENSIONS)
    # 未使用ポート取得
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('', 0))
    port = s.getsockname()[1]
    s.close()
    options = {
        'mode': "chrome",
        'close_callback': exit,
        'port': port,
        'cmdline_args': CHROME_ARGS
    }
    eel.start(start_page, options=options,
        size=size, suppress_error=True)

def exit(arg1, arg2):  # 終了時の処理
    sys.exit(0)
2
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?