最近のChatGPTの流行りようは目まぐるしいものがありますよね。日々新しい情報を取得するだけで時間が経ってしまいます。
興味はあるものの、別に人工知能や機械学習に特段精通しているわけでもないので、すげーって思うだけなのが悲しいです。
で、色々そういった情報を調べていると英語の論文が多いですよね。英語得意じゃないんですが、DeepLやGoogle翻訳のおかげでなんとか読んだりできてます。
Chromeだと右クリック、もしくは拡張機能の部分で英文のWebページを一気に日本に翻訳できますよね。
日本語に翻訳できるのはいいんですが、変な翻訳になっていて、ふともとの英語どんなんだっけと思うことがあって、いちいちもとの英語に戻す操作が煩わしいなと思ってました。
そこで、自分で拡張機能を作る練習を兼ねて翻訳した日本語からもとの英語を参照できる拡張機能を作ることにしました。
できたのがこんな感じ。単に日本語クリックしたら英語がポップアップされるだけです。多分動作の不具合とか使いづらさはあると思います。
↓に作った拡張機能のダウンロードリンクをおいておきます。
自作拡張機能の適用方法はこちらを参考にしてください。
作り方
拡張機能の作り方はこちらのサイトを参考にしました。
作るの難しいと思っていたのですが、わりと簡単にできました。
ファイルの構成はこんな感じです。
manifest.jsonはこんな感じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
json manifest.json { "name":"原文チェッカー", "description":"サイトのGoogle翻訳前の文章を確認します。", "version":"1.0.0", "manifest_version":3, "icons": { "16": "img/trans.png", "48": "img/trans.png", "128": "img/trans.png" }, "content_scripts":[ { "matches":["*://*/*"], "css":[ "css/content_script.css" ], "js":[ "js/content_script.js" ], "run_at":"document_end", "all_frames":false } ], "background": { "service_worker": "js/background.js" }, "action": { "default_icon": { "16": "img/trans.png" }, "default_title": "Click Me" } } |
結局background.jsは使っていないので、この部分はいらないかもしれません。
content_script.jsの中身
詳しくはコードの中に書きましたが流れは以下のとおりです。
コードは以下のとおりです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
#js content_script.js // XPathを取得する function getXPath(node) { if (node === document.body) return "/html/body"; var paths = []; // 親要素を遡って#documentになるまで while (node && node.nodeName.toLowerCase()!="#document") { var index = 0; var flag = false; // 兄弟要素を遡って検索(xpathでdiv[3]等の場合3を取得する必要があるため、それをindexに格納する) for (var sibling = node.previousSibling; sibling; sibling = sibling.previousSibling) { if (sibling.nodeName == node.nodeName) { index++; } } // 0の場合はその後の兄弟要素に同じnodeがないか確認(あった場合(1)なかったらなにもない) if(index===0){ for (var sibling = node.nextSibling; sibling; sibling = sibling.nextSibling) { if (sibling.nodeName == node.nodeName) { flag=true; break; } } } // xpathを結合する var tagName = node.nodeName.toLowerCase(); if(tagName === "#text") tagName = "text()"; var pathIndex = (index > 0 ? "[" + (index+1) + "]" : index===0 && flag ? "[" + (1) + "]" : ""); paths.unshift(tagName + pathIndex); node = node.parentNode; } return "/"+paths.join("/"); } // ロード後に実行する関数 function excute() { // すべてのxpathを再帰的に取得する関数 function getAllXPaths(node) { // xpathの取得 var xpath = getXPath(node); // テキストの取得 var content = getAllTextContent(node); var xpathObj = { "xpath": xpath, "content": content }; xpathList.push(xpathObj); //var children = node.childNodes; #textまで含めたい場合 var children = node.children; for (var i = 0; i < children.length; i++) { var child = children[i]; getAllXPaths(child); } } getAllXPaths(document.body); } // ロードしてから1秒後にxpathを取得する window.addEventListener('load', function() { window.setTimeout(excute, 1000); }); // テキストを取得する関数 function getAllTextContent(node) { var textContent = ''; // テキストノードの場合は結合 if (node.nodeType === Node.TEXT_NODE) { textContent += node.textContent; // 異なる場合は再帰的に子要素を調べてテキストノードを取得する } else { var childNodes = node.childNodes; for (var i = 0; i < childNodes.length; i++) { // 再帰的に探索 textContent += getAllTextContent(childNodes[i]); } } return textContent; } // xpathを取得する関数(クリックターゲット用) function getXpath(element) { if(element && element.parentNode) { let xpath; if(element.tagName === 'font' || element.tagName === 'FONT'){ // fontタグは日本語化で生成されるため元のタグを検索するのに必要ない xpath = getXpath(element.parentNode); }else{ xpath = getXpath(element.parentNode) + '/' + element.tagName; var s = []; for(var i = 0; i < element.parentNode.childNodes.length; i++) { var e = element.parentNode.childNodes[i]; if(e.tagName == element.tagName) { s.push(e); } } if(1 < s.length) { for(var i = 0; i < s.length; i++) { if(s[i] === element) { xpath += '[' + (i+1) + ']'; break; } } } } return xpath.toLowerCase(); } else { return ''; } } // クリックイベント document.addEventListener('click', function(e){ // クリックターゲットのxpath取得 const targetXpath = getXpath(e.target); // ターゲットがあって、xpath配列に格納されているターゲットのxpathが空白でない場合 if(e.target && xpathList.find(path=>path.xpath===targetXpath).content != ""){ // ツールチップを表示する tooltip.show(Object.assign({},xpathList.find(path=>path.xpath===targetXpath)).content, e.target); } }); // ツールチップクラス class Tooltip { constructor(){ // 内容 this.content = document.createElement( 'code' ); this.content.className = 'tooltip-content'; // 影 this.shadow = document.createElement( 'div' ); this.shadow.className = 'tooltip-shadow'; this.shadow.appendChild( this.content ); if( this.shadow.parentNode != document.body ) { // ドキュメントのbody要素に追加する document.body.appendChild( this.shadow ); }else{ } this.shadow.style.visibility = 'hidden'; } // ツールチップを表示する関数 show( text, el ){ // 内容 while( this.content.hasChildNodes() ) { this.content.removeChild( this.content.lastChild ); } this.content.appendChild( document.createTextNode( text ) ); // fontタグの場合はfont出ないところまでさかのぼって要素を取得する const targetEl = fontParentSearch(el); // クリックした要素の座標を取得する const targetRect = targetEl.getBoundingClientRect(); let x = targetRect.left; let y = targetRect.top; // マウスポインタの位置をドュメント座標に正する x += window.pageXOffset || document.documentElement.scrollLeft; y += window.pageYOffset || document.documentElement.scrollTop; // ツールチップを表示する位置を計算する this.shadow.style.left = x + 'px'; this.shadow.style.top = y - this.shadow.offsetHeight + 'px'; this.shadow.style.width = targetRect.width + 'px'; this.shadow.style.zIndex = 10000; // 影 this.shadow.style.visibility = 'visible'; } // ツールチップを隠す関数 hide(){ this.shadow.style.visibility = 'hidden'; } } // fontタグを遡って検索する関数 function fontParentSearch(el){ let targetEl = el; if(targetEl.tagName === "font" || targetEl.tagName === "FONT"){ targetEl = fontParentSearch(el.parentNode); } return targetEl; } var xpathList = []; //xpathを格納しておく配列 let tooltip = new Tooltip(); // tooltipを作成 |
無理くりなので、はじめに翻訳前の文章が保持できなかった場合は、もちろん表示できませんし、翻訳後とXPathが同じでないと表示できないなど、色々懸念点はありますが、なんとなくできました。
まとめ
以上ゴリ押しですが、「翻訳後の日本語からもとの英文を表示する拡張機能を作ってみた。」でした。
コメント