最速翻訳 〜選択した文字列をバックグラウンドでDeepL翻訳してクリップボードにコピーするChrome拡張

# TL;DR
* 選択した文字列を英訳or和訳して、クリップボードにコピーする。
* 論文でありがちな改行や半角スペースは除いて翻訳し、カンマやピリオドと句読点の混在をなくして出力する。
* アラートなどは発生しないので最短で翻訳できる。

# Motivation
DeepL翻訳するときに、わざわざブラウザで https://www.deepl.com/translator を開くのは面倒。また、[以前私が作成したChrome拡張](https://github.com/kentoak/revised_DeepL_recognize_slash)では、ポップアップでウィンドウが開くので、ウィンドウを消す作業が面倒だった。そこで、バックグラウンドで翻訳してそのままクリップボードにコピーしてくれないかなと考えた。


# 内容
### 英語to日本語
翻訳したい文字列を選択して、右クリック（Context menu）から`Translate ~~~ by DeepL` をクリック
![20220722-192148.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316390/49cc7841-fa50-eb98-fba4-c07912bf465b.jpeg)

日本語訳がコピーされている。pdfで面倒な改行とか半角スペースは除いてある。
![20220722-192157.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316390/f8e49309-cb68-03bb-af9a-72db0a0d64f3.jpeg)

### 日本語to英語
![20220722-194554.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316390/35096534-7d8c-a84f-a82e-f8da52f22186.jpeg)
同様の作業をすると英訳がコピーされている。
![20220722-194606.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316390/a6771b2a-06c6-0cc6-d24d-1fbeb8b23b05.jpeg)

* 論文でありがちな改行や半角スペースは除いて翻訳し、カンマやピリオドと句読点の混在をなくして出力する。

これによる翻訳の精度を比較する。
(i) まずは、普通にコピーしてDeepLのテキストボックスにペーストしたとき


>我々は、大規模な深層畳み込みニューラルネットワークを学習させ、ImageNet LSVRC-2010コンテストの120万枚の高解像度画像を1000の異なるクラスに分類しました。
ImageNet LSVRC-2010コンテストの高解像度画像120万枚を1000種類のクラスに分類するために、大規模な深層畳み込みニューラルネットワークを学習させました。テストデータにおいて、上位1位と上位5位の誤り率はそれぞれ37.5%と17.0%であり、従来よりも大幅に改善されました。
と17.0%であり、従来技術よりも大幅に改善されました。また
ニューラルネットは、6000万個のパラメータと65万個のニューロンから構成されています。
5つの畳み込み層と、そのうちのいくつかの畳み込み層に続くマックスプーリング層から構成されます。
そして3つの完全連結層と最終的な1000ウェイソフトマックスで構成されています。学習を高速化するために、非飽和ニューロンを用い、畳み込み演算を非常に効率的にGPUで実装した。完全連結層のオーバーフィッティングを低減するために、最近開発された
層におけるオーバーフィッティングを抑制するために、最近開発された正則化手法である "ドロップアウト "を採用した。
これは非常に効果的であることが証明されました。また、このモデルの変形をILSVRC-2012のコンペティションに出品しました。
ILSVRC-2012に応募し、上位5位までのテスト誤差を15.3%とすることに成功しました。
2位の26.2%に対し、15.3%で優勝することができました。
www.DeepL.com/Translator（無料版）で翻訳しました。


(ii) 本ツールを使用したとき

>ImageNet LSVRC-2010に登録された120万枚の高解像度画像を1000種類のクラスに分類するために、大規模で深い畳み込みニューラルネットワークを学習させました。テストデータにおいて、上位1位と上位5位の誤り率は37.5%であった。
17.0%と、従来よりも大幅に改善されました。ニューラルネットワークは、6000万個のパラメータと65万個のニューロンを持ち、5つの畳み込み層と、そのうちのいくつかの層に続く最大プーリング層、3つの完全連結層と最後の1000ウェイソフトマックスで構成されています。学習を高速化するために、非飽和ニューロンを用い、畳み込み演算を非常に効率的にGPUで実装した。完全連結層でのオーバーフィッティングを減らすために、最近開発された正則化手法である「ドロップアウト」を採用したところ、非常に効果的であることが判明しました。また、このモデルをILSVRC-2012に応募したところ、テストエラー率トップ5で15.3%を記録し、2位の26.2%に差をつけることができました。


(ii)の方が変な位置での改行がなく、日本語も自然なのが分かる。


# DeepL APIとは
1か月に50万文字まで無料で翻訳できる。要するに、https://www.deepl.com/translator のテキストボックスにペーストせずとも、バックグラウンドでDeepLを利用するためのAPI。
https://support.deepl.com/hc/ja/articles/360021200939-DeepL-API-Free 

アカウント発行は以下を参照
https://auto-worker.com/blog/?p=5030


# Source
DeepL APIではふつうのDeepLと違って、言語の自動検出ができない。よって、英語to日本語なのか、日本語to英語なのかを確定させないといけないため、日本語を含むかを判定する関数(includeJa)が必要。~~また、DeepL API for Freeの仕様か、URLが長くなりすぎると返ってこない。http リクエストをjson形式でやったら解決するかも？←確認していない。~~ 
（追記：urlを直で書くのではなくて、json形式でmethodをPOSTにするとこの問題は回避できた。GETではダメそう）

```
function selectionOnClick(info, tab) {
    var keyword = sendTextToDeepL(info.selectionText);
    var targetLang = 'EN';
    var sourceLang = 'JA';
    //await translate(sourceLang, targetLang, keyword);
    if (keyword != '') {
        if(includeJa(keyword)){//ja2en
            
        }
        else{//en2ja
            targetLang = "JA"
            sourceLang = "EN";
        }
        deeplTranslate(targetLang, sourceLang, keyword)
    }
}

function sendTextToDeepL(inString) {
    //var input = encodeURIComponent(inString);
    var input = inString;
    setText = input.replace('，', '、').replace('．', '。').replace(/([^\x01-\x7E\uFF61-\uFF9F]) +([^\x01-\x7E\uFF61-\uFF9F])/g, '$1$2').replace(/([^\x01-\x7E\uFF61-\uFF9F]) +([A-Za-z0-9])/g, '$1$2').replace(/([A-Za-z0-9]) +([^\x01-\x7E\uFF61-\uFF9F])/g, '$1$2').replace(/([^\x01-\x7E\uFF61-\uFF9F]),/g,'$1、').replace(/(^[^\x01-\x7E]*$)\．+/g, '$1。').replace(/(^[^\x01-\x7E])、+([^\x01-\x7E])/g, '$1, $2').replace(/([^\x01-\x7E\uFF61-\uFF9F])\./, '$1。').replace(/(^[^\x01-\x7E]*$)。+/g, '$1.').replace(/([^\x01-\x7E]):+([^\x01-\x7E])/g, '$1: $2');
    return setText
}

var token = 'ここにあなたのAPI';
async function deeplTranslate(targetLang,sourceLang,keyword){
    var url = new URL("https://api-free.deepl.com/v2/translate"+ '?' + "auth_key=" + token + "&text=" + keyword + "&target_lang=" + targetLang + "&source_lang=" + sourceLang);
    var url_ = "https://api-free.deepl.com/v2/translate"
    var options = {
        method: "POST",
        headers:{"Content-Type": "application/x-www-form-urlencoded"},
        body: "auth_key=" + token + "&text=" + keyword + "&target_lang=" + targetLang + "&source_lang=" + sourceLang
    }
    //const response = await fetch(url.toString());
    const response = await fetch(url_,options);
    const data = response.ok ? await response.json() : null;
    const source = keyword;
    var target = data?.translations?.[0]?.text;
    for(var i=0; i<10; i++){
        target = target.replace('，', '、').replace('．', '。').replace(/([^\x01-\x7E\uFF61-\uFF9F]) +([^\x01-\x7E\uFF61-\uFF9F])/g, '$1$2').replace(/([^\x01-\x7E\uFF61-\uFF9F]) +([A-Za-z0-9])/g, '$1$2').replace(/([A-Za-z0-9]) +([^\x01-\x7E\uFF61-\uFF9F])/g, '$1$2').replace(/([^\x01-\x7E\uFF61-\uFF9F]),/g,'$1、').replace(/(^[^\x01-\x7E]*$)\．+/g, '$1。').replace(/(^[^\x01-\x7E])、+([^\x01-\x7E])/g, '$1, $2').replace(/([^\x01-\x7E\uFF61-\uFF9F])\./, '$1。').replace(/(^[^\x01-\x7E]*$)。+/g, '$1.').replace(/([^\x01-\x7E]):+([^\x01-\x7E])/g, '$1: $2');
    }
    var target1 = target
    saveToClipboard(target1)
    var result = target1 ? { source, target1 } : null
    return result;
};

function includeJa(text) {
    let gmi = 'gmi';
    let regeIncludeHiragana = '^(?=.*[\u3041-\u3096]).*$';
    let regeIncludeKatakana = '^(?=.*[\u30A1-\u30FA]).*$';
    let regeIncludeKanji = '^(?=.*[\u4E00-\u9FFF]).*$';
    let regeHiragana = new RegExp(regeIncludeHiragana, gmi);
    let regeKatakana = new RegExp(regeIncludeKatakana, gmi);
    let regeKanji = new RegExp(regeIncludeKanji, gmi);
    let includeJa = false;
    if (regeHiragana.test(text))
        includeJa = true;
    if (regeKatakana.test(text))
        includeJa = true;
    if (regeKanji.test(text))
        includeJa = true;
    return includeJa;
}

function saveToClipboard(str) {//任意の文字列をクリップボードに入れる
    document.addEventListener('copy', (e) => {
        e.preventDefault();
        e.clipboardData.setData('text/plain', str);
    }, {once:true});
    document.execCommand('copy');
}


// right click context menu
const parent = chrome.contextMenus.create({
    "id" : "bgdl",
    "type" : "normal",
    "title" : "Translate %s by DeepL",
    "contexts" : ["all"],
    "onclick" : selectionOnClick
});
```
すべてのソースは以下。

https://github.com/kentoak/DeepL_in_background



# 最後に
特に慎重に英訳したりする必要がなくてざっくり翻訳で良い状況のときにクリップボードにコピーされているので便利。バックグラウンドでDeepLやってくれるので、最速だと思う。
1か月に50万文字までしか使えないのと、長い翻訳のときには動かないのに注意が必要。

他にもDeepL系のもの作っているのでぜひ↓↓↓

https://github.com/kentoak/revised_DeepL_recognize_slash  


https://github.com/kentoak/deepl_translator_alfred