pdfの選択部分を翻訳するChrome拡張を作った

作成: 2019年06月06日

更新: 2019年06月06日

やりたいこと

Chromeには文字を選択するだけで翻訳したものをポップアップウィンドウで表示してくれるGoogle翻訳拡張がある。
Google翻訳
しかしこの拡張はpdfではうまく機能しない。右クリックしてコンテキストメニューを選択すると翻訳はできるがGoogle翻訳の新規タブがいちいち開いてめんどくさい。翻訳系拡張は他にもあるがだいたい似たような感じ。
またMacのPDFビューアーには翻訳機能がデフォルトでついているが、基本1単語しか翻訳できなかったり、翻訳結果が物足りなかったりする。
結局pdfを開いているページだけで完結する翻訳拡張が見つからなかったので作ることにした。

作ったもの

以下のようなスクリプトを作成した。

translate

pdf状の文字を選択し、右クリックからコンテキストメニューをクリックするとページ上部に日本語へ翻訳したものが表示される。
ダウンロードページは以下
https://github.com/bana118/PopupTranslate/releases

Chrome拡張の作り方

chrome拡張はプロジェクトディレクトリに以下のようなmanifest.jsonとJavaScript, CSSで構成される。
今回の拡張のmanifest.jsonは以下

manifest.json{
    "name": "PopUpTranslate", //拡張名(必須)
    "version": "0.0.2", //拡張のver(必須)
    "manifest_version": 2, //manifestのver.現在は2(必須)
    "description": "This is an translater of Chrome (includes PDF file)", //拡張の概要
    "permissions": [ //拡張で使うChromeの特殊なAPI
        "contextMenus",
        "tabs",
        "storage"
    ],
    "icons": { //iconのパス.サイズごとに指定できる
      "16": "icons/icon16.png",
      "48": "icons/icon48.png",
      "128": "icons/icon128.png"
    },
    "content_scripts": [{ //開いているページで動くスクリプト
        "matches": ["<all_urls>"],
        "js": ["js/content.js"],
        "css": ["css/header.css"]
      }],
    "background": { //共通で動くバックグラウンドスクリプト
      "persistent": false,
      "scripts": [
        "js/background.js",
        "js/glottologist.min.js"
      ]
    },
    "options_ui": { //オプション用の設定
      "page": "options/options.html",
      "chrome_style": true
    }
  }

この他にユーザーが開いているページで動くスクリプト、裏で動くバックグラウンドスクリプトにより拡張機能として動作する。バックグラウンドスクリプトはページの要素に直接触れないが、ここでしか動かないChromeのAPIが存在する。

PDF上で選択した文字列を取得する

JavaScriptで選択部分の文字列を取得するにはgetSelection()というメソッドがある。しかしPDF上ではとれない。開発者ツールを使うとわかるが、ChromeでPDFを表示したとき文字を選択することはできるが直接文字列は表示されていない。おそらく何らかのアプリをかませてPDFを表示させているためだと思われる。既存の翻訳拡張機能の詳しい構造はわからないがこのあたりが原因だと思う。
それでもPDF上の文字列を取得する方法がある。それはContext menuを経由させるという方法である。
Context menuはChromeで右クリックしたときにでるメニューのことでChrome APIにより設定できる。Context menuクリック時に以下のようにすることでPDFでも文字を取得できる。

background.jschrome.contextMenus.create({id:"translate",title:"translate:%s",contexts:["selection"]});

chrome.contextMenus.onClicked.addListener(function (info,tab){
    if(info.menuItemId == "translate") {
        const glot = new Glottologist();
        glot.t(info.selectionText, "ja").then(translated => {
            console.log(translated);
            var queryInfo = {
                active: true,
                windowId: chrome.windows.WINDOW_ID_CURRENT
            };
            chrome.tabs.query(queryInfo, function (result) {
                var currentTab = result.shift();
                var message = {
                    command: "translate",
                    text: translated
                };
                chrome.tabs.sendMessage(currentTab.id, message, function(response){
                    console.log(response);
                });
            });
        });
    }
});

Context menuのAPIとして用意されているonClicked.addListenerというイベントリスナーメソッドのinfoという引数には選択している文字列が含まれていて、PDF上でも問題なく使える。これを翻訳ライブラリに入れ、翻訳されたものをヘッダーとしてページ上部に表示させている。
Context menu APIはバックグラウンドスクリプトでしか動かないためバックグラウンドで翻訳し、閲覧ページで動くスクリプトに送信するという形にしている。
また翻訳にはGlottologistというライブラリを使用。Google翻訳を内部で使用していて自然な翻訳が返ってくる。

まとめ

探せば求めているものはありそうな気がするが、拡張機能作成はいい経験になったので良しとする