MylingualでCodecademyの日本語化を行っていたが、Mylingualはシステムメッセージの翻訳ツールでドキュメント翻訳ツールとしては無理があることがわかった。
それならば、と独自で日本語化出来るようスクリプトを書いてみた。
prototype.ja.www.codecademy.com.user.js
ソースはあるツールを使用して半自動で作成したのであまりよろしくないがとりあえず。
translationが翻訳テーブルとなる。このように特定要素内で正規表現による置換ができると翻訳の自由度がとても高くなり、極端な話なんでもできてしまう。原文に忠実でなくとも超意訳で日本語化できるはず。ページ毎に翻訳の判断をする必要があるかまだわからない。
このソースの正式版をgithubで公開し、userscripts.orgで配布する形になる予定。githubのソースはフォークして独自の物を作成してもよし、追加変更してプルリクエストもよし。手伝ってくれる人がいれば非常に助かる。
課題としてユーザーにスクリプトの更新をどうさせるか。自動で出来るか、何か良い方法があるか調査中。どなたかコメントを待っています。
スクリプトの更新が確実にできるようになれば、Mylingualのようにデータベース化せずスクリプトの配布だけで日本語化が出来ると見込んでいる。
将来の展望としてはブラウザ上で見ているサイトを直接翻訳可能にする機能拡張の作成、それで作成されたスクリプトを各自がgithubで公開できるようになればいいなぁ。
それならば、と独自で日本語化出来るようスクリプトを書いてみた。
prototype.ja.www.codecademy.com.user.js
// ==UserScript== // @name Codecademy for Japanese // @namespace Codecademy4Japanese // @author Ryo Link // @include http://*.codecademy.com/* // @description Translates http://www.codecademy.com/ to Japanese. // ==/UserScript== (function(){ // start custom code. var translation = [ { xpath:'/html/body/div[3]/div/div/div/section/div[2]/h1', original:/Learn to code/, newtext:'コードを学ぼう' } , { xpath:'/html/body/div[3]/div/div/div/section/div/div[2]/pre/span[@class="log"]', original:/Hey! .*? keyboard\./, newtext:'はじめまして、Ryanです。あなたのお名前は何ですか?あなたのお名前を<strong>"Ryan"</strong>のように引用符で括り入力してください。入力できたらキーボードの <strong>Enter</strong> と書かれたエンターキーを押してください。<span style="font-size:small;">注意:引用符「<strong>"</strong>」は必ず半角で入力します。</span>' } , { xpath:'/html/body/div[3]/div/div/div/section/div/div[2]/pre/span[@class="log"]', original:/Well done! .*?<code>"Ryan".length<\/code>/, newtext:'よろしく!お名前は何文字ですか?あなたのお名前を引用符で括り、そのまま続けて<code class="ace-tm"><span class="ace_line"><span class="ace_punctuation ace_operator">.</span><span class="ace_identifier">length</span></span></code>と入力しエンターキーを押してください。例: <code class="ace-tm"><span class="ace_line"><span class="ace_string">"Ryan"</span><span class="ace_punctuation ace_operator">.</span><span class="ace_identifier">length</span></span></code>' } , { xpath:'/html/body/div[3]/div/div/div/section/div/div[2]/pre/span[@class="log"]', original:/Well done! .*? <code class="ace-tm"><span class="ace_line"><span class="ace_string">"Ryan"<\/span><span class="ace_punctuation ace_operator">\.<\/span><span class="ace_identifier">length<\/span><\/span><\/code>/, newtext:'よろしく!お名前は何文字ですか?あなたのお名前を引用符で括り、そのまま続けて<code class="ace-tm"><span class="ace_line"><span class="ace_punctuation ace_operator">.</span><span class="ace_identifier">length</span></span></code>と入力しエンターキーを押してください。例: <code class="ace-tm"><span class="ace_line"><span class="ace_string">"Ryan"</span><span class="ace_punctuation ace_operator">.</span><span class="ace_identifier">length</span></span></code>' } ]; function do_platypus_script() { for(var i in translation){ var o = translation[i]; translate(o.xpath, o.original, o.newtext); } }; function getElementsByXPath(xpath, node) { var node = node || document; var doc = node.ownerDocument ? node.ownerDocument : node; var nodesSnapshot = doc.evaluate(xpath, node, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); var data = []; for (var i = 0; i < nodesSnapshot.snapshotLength; i++) { data.push(nodesSnapshot.snapshotItem(i)); } return (data.length >= 1) ? data : null; } function translate(xpath, original, newtext) { var elements = getElementsByXPath(xpath, document); for(var e in elements){ do_modify_html_it(window.document, elements[e], original, newtext, null); } } function loadHandler(){ var handler = function (evt) { if (handler.underOperation) { return; } setTimeout( function () { handler.underOperation = true; do_platypus_script(); handler.underOperation = false; }, 1); }; document.addEventListener("DOMNodeInserted", handler, false); document.addEventListener("DOMCharacterDataModified", handler, false); } window.addEventListener("load", loadHandler, false); // end custom code. var gplatypusBundle = Components.classes["@mozilla.org/intl/stringbundle;1"].getService(Components.interfaces.nsIStringBundleService); var mystrings = gplatypusBundle.createBundle("chrome://platypus/locale/platypusCore.properties"); var platypusplatypuscouldntfi1 = mystrings.GetStringFromName("platypusplatypuscouldntfi1"); var platypusthisusuallyhappens = mystrings.GetStringFromName("platypusthisusuallyhappens"); function walk_down(node, func) { if (node.nodeType == 1) { if (node.tagName != "IMG") func(node); if (node.childNodes.length != 0) for (var i=0; i<node.childNodes.length; i++) walk_down(node.childNodes.item(i),func); } } function make_bw(doc, node) { walk_down(node, function (node) { if (node.tagName != 'A') { node.bgcolor = "white"; node.color = "black"; node.style.backgroundColor = "white"; node.style.color = "black"; node.style.backgroundImage = ""; }}); } function center_it(doc, node) { var center_node = doc.createElement ("CENTER"); node.parentNode.insertBefore(center_node, node); node.parentNode.removeChild(node); center_node.appendChild(node); return center_node; }; function erase_it(doc, node) { var offset_height = node.offsetHeight; var offset_width = node.offsetWidth; var replacement_div = doc.createElement ("DIV"); replacement_div.setAttribute('style', "height: "+offset_height+"; width: "+offset_width+";"); node.parentNode.insertBefore(replacement_div, node); node.style.display = "none"; return replacement_div; }; function smart_remove(doc, node) { if (node.parentNode.childNodes.length == 1) { smart_remove(doc, node.parentNode); } else { remove_it(doc, node); }; }; function remove_it(doc, node) { if (doc == null || node == null) return; if (!node.parentNode) return; node.style.display = "none"; doc.last_removed_node = node; }; function script_paste(doc, where, what) { var new_node = what.cloneNode(true); new_node.style.display = ""; where.parentNode.insertBefore(new_node, where); }; function isolate(doc, node) { if (!node.parentNode) return; node.parentNode.removeChild(node); while (doc.body.childNodes.length > 0) { doc.body.removeChild(doc.body.childNodes[0]); }; var replacement_div = doc.createElement ("DIV"); replacement_div.setAttribute('style', "margin: 0 2%; text-align: left"); replacement_div.appendChild(node); doc.body.appendChild(replacement_div); }; function set_style_script(doc, element, new_style) { element.setAttribute('style', new_style); }; function modify_single_url(doc, match_re, replace_string, node) { if (node.href) { node.href = node.href.replace(match_re, replace_string); }; }; function do_modify_url_it(doc, node, match_re, replace_string, global_flag) { match_re = new RegExp(match_re); if (global_flag) { var allurls = doc.getElementsByTagName('A'); for(var i = 0, url; url = allurls[i]; i++) modify_single_url(doc, match_re, replace_string, url); } else { modify_single_url(doc, match_re, replace_string, node); }; }; function do_modify_html_it(doc, element, match_re, replace_string) { match_re = new RegExp(match_re); if (element.innerHTML) { element.innerHTML = element.innerHTML.replace(match_re, replace_string); }; }; function relax(doc, node) { walk_down(node, function (node) { node.style.width = 'auto'; node.style.marginLeft = '0pt'; node.style.marginRight = '0pt'; if (node.width) node.width = null; }); } function fix_page_it(doc, node) { doc.background = null; doc.bgColor = "white"; if (doc.style) { doc.style.backgroundColor = "white"; doc.style.backgroundImage = "none"; if (doc.style.color == "white") { doc.style.color = "black"; }; if (doc.text == "white") { doc.text = "black"; }; }; doc.body.background = null; doc.body.bgColor = "white"; if (doc.body.style) { doc.body.style.backgroundColor = "white"; doc.body.style.backgroundImage = "none"; if (doc.body.style.color == "white") { doc.body.style.color = "black"; }; if (doc.body.text == "white") { doc.body.text = "black"; }; }; }; function insertAfter(newNode, target) { var parent = target.parentNode; var refChild = target.nextSibling; if(refChild != null) parent.insertBefore(newNode, refChild); else parent.appendChild(newNode); }; function html_insert_it(doc, element, new_html, before, insert_as_block) { var new_element; if (insert_as_block) { new_element = doc.createElement ("DIV"); } else { new_element = doc.createElement ("SPAN"); }; new_element.innerHTML = new_html; if (before) { element.parentNode.insertBefore(new_element, element); } else { insertAfter(new_element, element); }; }; function auto_repair_it(doc, node) { Dump("In auto_repair_it..."); var biggest_elem = find_biggest_elem(doc); Dump("biggest_elem = "+biggest_elem); isolate(doc, biggest_elem); Dump("After isolate."); relax(doc, biggest_elem); Dump("After relax."); make_bw(doc, biggest_elem); Dump("After make_bw."); fix_page_it(doc, biggest_elem); Dump("After fix_page_it."); }; function find_biggest_elem(doc) { const big_element_limit = 0.25; var size_of_doc = doc.documentElement.offsetHeight * doc.documentElement.offsetWidth; var body = doc.body; var size_of_body = body.offsetHeight * body.offsetWidth; if (size_of_body < (0.80 * size_of_doc)) { size_of_body = size_of_doc; }; var max_size = 0; var max_elem = doc; /* var allElems = doc("//*", doc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); Dump("allElems = "+allElems); Dump("allElems.snapshotLength = "+allElems.snapshotLength); for (var i = 0; i < allElems.snapshotLength; i++) { var thisElem = allElems.snapshotItem(i); */ var allElems = doc.getElementsByTagName("*"); Dump("allElems = "+allElems); Dump("allElems.snapshotLength = "+allElems.length); for (var i = 0; i < allElems.length; i++) { var thisElem = allElems[i]; var thisElem_size = thisElem.offsetHeight * thisElem.offsetWidth; if (thisElem_size < size_of_body && thisElem_size > max_size && !contains_big_element(thisElem, size_of_body * big_element_limit)) { max_size = thisElem_size; max_elem = thisElem; }; }; Dump("Max elem = "+max_elem.tagName); return max_elem; }; function contains_big_element(node, limit) { if (node.childNodes.length != 0) for (var i=0; i<node.childNodes.length; i++) { var child = node.childNodes.item(i); var child_size = child.offsetHeight * child.offsetWidth; if (child_size > limit) return true; }; return false; }; function platypus_do(win, func_name, o, other, other2, other3) { var func = eval(func_name); var doc = null; if (func == null) return; if (!o) { Warning(platypusplatypuscouldntfi1+ func_name+platypusthisusuallyhappens); }; doc = win.document; func(doc, o, other, other2, other3); }; })(); //.user.js
ソースはあるツールを使用して半自動で作成したのであまりよろしくないがとりあえず。
translationが翻訳テーブルとなる。このように特定要素内で正規表現による置換ができると翻訳の自由度がとても高くなり、極端な話なんでもできてしまう。原文に忠実でなくとも超意訳で日本語化できるはず。ページ毎に翻訳の判断をする必要があるかまだわからない。
このソースの正式版をgithubで公開し、userscripts.orgで配布する形になる予定。githubのソースはフォークして独自の物を作成してもよし、追加変更してプルリクエストもよし。手伝ってくれる人がいれば非常に助かる。
課題としてユーザーにスクリプトの更新をどうさせるか。自動で出来るか、何か良い方法があるか調査中。どなたかコメントを待っています。
スクリプトの更新が確実にできるようになれば、Mylingualのようにデータベース化せずスクリプトの配布だけで日本語化が出来ると見込んでいる。
将来の展望としてはブラウザ上で見ているサイトを直接翻訳可能にする機能拡張の作成、それで作成されたスクリプトを各自がgithubで公開できるようになればいいなぁ。
コメント
コメントを投稿