Firefoxの拡張機能を作ってみたので、作り方をまとめてみました。
この記事はFirefox 82.0.2で動作確認しています。
作成する拡張機能
ページの翻訳機能と、文章の翻訳機能を作ります。
右クリックで出るコンテキストメニューをクリックすると、翻訳ページに遷移する拡張機能を作ります。
ページ翻訳機能では、ページ全体を翻訳したページに、文章の翻訳機能は、選択した文章を翻訳したページに遷移します。
サンプルを動かす
とりあえず動かしてみるため、公式のサンプルを動かして、動作確認してみます。
下のリンクからサンプル集をクローンします。
https://github.com/mdn/webextensions-examples
サンプルは大量にあるのですが、今回はMenu APIを使うので、
その中の、menu-demoフォルダを使います。
拡張機能のデバッグ
手っ取り早く拡張機能を動かすには、拡張機能のデバッグ機能を使います。
Firefoxのアドレスバーに、about:debugging#/runtime/this-firefox
を打ち込み、デバッガを出します。
デバッガから、一時的な拡張機能 → 一時的なアドオンを読み込む を選び、ファイル選択ダイアログを出します。
ファイル選択ダイアログで、先ほど入手したmenu-demoフォルダの「manifest.json」を選び実行します。
動作確認として、ブラウザの適当なところを右クリックしてみます。
うまく動作していれば、コンテキストメニューに、Menu demoと表示されています。
表示まで確認できたら、サンプルの中身を見ていきます。
サンプルのファイルについて
サンプルのファイル構成は次のようになっています。
- _locales
- icons
- sidebar
- background.js
- manifest.json
- README.md
今回必要なのは、manifest.json、_locales、background.jsの3つだけです。
それぞれ何に使うのか下に書きます。
- manifest.json
- 拡張機能のメタデータや、他のファイルの参照を記述するファイルです。
- 今回はメタデータ(名前など)、パーミッションを指定するために使います。
- background.js
- 拡張機能の処理を書くファイルです。
- _locales
- 拡張機能を多言語に切り替えられるように、文字列を定義するフォルダです。
- 多言語に対応しないのであれば不要です。
#マニフェストファイルの作成
manifest.json
には、拡張機能の名前、説明などを書くことができます。
最も重要なのはpermissions
で、APIへのアクセス権を付与します。
今回は、コンテキストメニューの追加と、タブの生成を行うので、menus
、tabs
を指定します。
icons
とsidebar_action
は今回は使わないので、削除します。
{
"manifest_version": 2,
"name": "__MSG_extensionName__",
"description": "__MSG_extensionDescription__",
"version": "1.0",
"default_locale": "en",
"browser_specific_settings": {
"gecko": {
"strict_min_version": "56.0a1"
}
},
"background": {
"scripts": ["background.js"]
},
"permissions": [
"menus",
"tabs"
]
}
スクリプトの作成
それでは実際に、スクリプトを書いて動かしてみましょう。
サンプルのbackground.jsは、大きく分けて3つの部分からできています。
- ボタンのイベント登録
- onCreated、onRemoved、onErrorが定義されている部分です。
- サンプルではログへの書き込みしか行っていないので、今回は使いません。
- メニューボタンの追加
- browser.menus.create()が呼ばれている部分です。
- ボタンの文字や、ボタンの種類(チェックボックスやラジオボタン)の定義をしています。
- 処理部分
- 上記以外の部分です。
- メニューボタンを押した際のイベントや、クリック時の処理の実装をしています。
###メニューボタン
まずはコンテキストメニューにボタンを追加してみます。
browser.menus.create()が呼び出されているあたりを、以下に書き換えます。
// 選択範囲翻訳
browser.menus.create({
id: "translation-selection",
title: "選択範囲を翻訳する",
contexts: ["selection"],
}, onCreated);
// ページ翻訳
browser.menus.create({
id: "translation-page",
title: "このページを翻訳する",
contexts: ["all"],
}, onCreated);
-
id
- ボタンのID。クリック時のイベント処理で使います。
-
title
- ボタンの文言。
-
contexts
- ボタンの表示される条件を指定します。(詳細)
- allは常時表示されます。
- selectionは選択しているものがある場合のみ表示されます。選択範囲翻訳は選択中のみ使用可能なのでこっち。
###処理の実装
次は、ボタンをクリックしたときの処理を実装していきます。
browser.menus.onClicked.addListener((info, tab) => {
var url = "";
switch (info.menuItemId) {
case "translation-selection":
url = CreateTranslationURL(info.selectionText, null);
break;
case "translation-page":
url = CreateTranslationURL(null, info.pageUrl );
break;
}
OpenTranslationText(url, tab.index + 1); // 直後のタブに生成
});
info.menuItemId
に押したボタンのIDが入っているので、switch文で各ボタンを押した際の処理に振り分けます。
今回は、ボタンに応じて、ページ翻訳 or 文章翻訳のURLを生成し、そのURLを開く処理を行っています。
URL生成はこんな感じ。
google翻訳のクエリストリングを参考に、無理やり作ってます。
function CreateTranslationURL(text = null, targetUrl = null){
var isTextTranslate = text != null;
// URL生成
var url = "https://translate.google.co.jp/";
if (!isTextTranslate){
// ページ翻訳の場合
url += "translate";
}
// 共通部クエリ生成
var query = "?";
query += "sl=auto"; // 翻訳元言語 : 自動検出
query += "&tl=ja"; // 翻訳先言語 : 日本語
// テキスト翻訳orページ翻訳で固有のクエリ
if(isTextTranslate){
query += "&op=translate"; // テキスト翻訳
query += "&text=" + text; // 翻訳対象テキスト
}else{
query += "&u=" + encodeURI(targetUrl);
}
url += query;
return url;
}
最後にタブを開く部分の処理を実装します。
tabs
のAPIを使って、URLを新しいタブで開きます。
function OpenTranslationText(url, index) {
browser.tabs.create({
"url" : url,
"active" : true,
"index" : index
}).then(function(value) {
// 非同期処理が成功した場合
console.log('実行結果:' + value);
}).catch(function(value) {
// 非同期処理が失敗した場合
console.log('実行結果:' + value);
});
}
-
url
- タブのURLです。
-
active
- 開いたタブをアクティブにするか設定できます。
-
index
- タブを何番目に生成するか設定できます。現在のタブの直後に生成するため、
tab.index + 1
を呼び出し側で指定しています。
- タブを何番目に生成するか設定できます。現在のタブの直後に生成するため、
###動作確認
ここまで実装したら、動作確認してみます。拡張機能のデバッグと同様に実行します。
以下の項目ができればOKです。
- コンテキストメニューに「このページを翻訳する」が表示される
- 「このページを翻訳する」をクリックすると、ページ全体を翻訳したタブが開く
- 文字列を選択しているとき、コンテキストメニューに「選択範囲を翻訳する」が表示される
- 「選択範囲を翻訳する」をクリックすると、選択範囲の文字列を翻訳したタブが開く
動かない場合は、デバッガから原因を特定できます。拡張機能のデバッグを参照して、デバッガを開きます。
デバッガにはデバッグ中の拡張機能が表示されます。そこの調査ボタンを押すと、拡張機能用のデベロッパーツールが表示されるので、ブレークポイント等で、調査してみてください。
#多言語対応
メニューボタンでは、言語を日本語で定義しました。このままでは、日本以外のユーザが使えないので、英語でも表示できるようにします。(詳細)
###スクリプトの多言語対応
拡張機能では、ブラウザの言語によって自動的に、対応する言語に文字列をを切り替える機能が付いています。
_locales
フォルダは、各言語の文字列定義することができます。まずは日本語用の文字列を定義してみます。
_locales/ja/messages.json
を作成し、以下のように文字列を定義します。
{
"menuItemTranslationSelection": {
"message": "選択範囲を翻訳する",
"description": "選択範囲の文章を、Google翻訳で翻訳します"
},
"menuItemTranslationPage": {
"message": "このページを翻訳する",
"description": "このページを、Google翻訳で翻訳します"
}
}
同じように、英語の文字列を_locales/en/messages.json
に定義します。
{
"menuItemTranslationSelection": {
"message": "Translate selected range ",
"description": "Translate selected range with Google Translation"
},
"menuItemTranslationPage": {
"message": "Translate this page",
"description": "Translate this page with Google Translation"
}
}
この文字列は、browser.i18n.getMessage("menuItemTranslationSelection")
の形式で呼び出すことができます。
background.js
のメニューボタンを、以下のように書き換えます。
// 選択範囲翻訳
browser.menus.create({
id: "translation-selection",
title: browser.i18n.getMessage("menuItemTranslationSelection"),
contexts: ["selection"],
}, onCreated);
// ページ翻訳
browser.menus.create({
id: "translation-page",
title: browser.i18n.getMessage("menuItemTranslationPage"),
contexts: ["all"],
}, onCreated);
変更点は、title
を文字列定義から呼び出すようにしただけです。
確認する場合は、ブラウザの言語を英語にすることで確認できます。
さらに他の言語を追加する際は、言語コードのフォルダと、messages.json
を追加すればOKです。
###マニフェストファイルの多言語対応
manifest.js
では、ブラウザAPIが使えないので、他の方法を使います。
manifest.js
内で、__MSG_XXXXXXX__
と記述すると、各言語のmessages.json
ファイルから、XXXXXXX
キーと一致する文字列に置換します。
ですので、manifest.js
と、messages.json
ファイルを以下のように変更します。
"name": "__MSG_extensionName__",
"description": "__MSG_extensionDescription__",
"extensionName": {
"message": "Translation konjac",
"description": ""
},
"extensionDescription": {
"message": "Google翻訳へのリンクを追加する拡張機能です",
"description": ""
},
これで、マニフェストも他言語に対応させることができるようになります。