なげやりろぐ

フテイキコウシン

Google Chrome風のアレを作ってみた『その2』

今度のはGraniでも導入が簡単だよ!

設置方法は最後の方にありますので駄文は飛ばしていただいても結構ですよ〜(自虐)


前回、自分のスキルに見合った方法で適当にでっち上げたGoogle翻訳Dockみたいなものはあまりにも稚拙であったため、ハッキリ言って導入の手間に見合うだけの判定精度も無く使い勝手もいまいちな物でした。

そこでせめて導入の手間だけでも減らせるようにしたいなと慣れないjavaScriptと格闘しつつ、Seahorseスクリプトだけで完結できる物を作ってみようと手を付けはじめてつぶやき処のTLへ習作のスクショを上げた矢先、多分前作のあまりのつたなさを見かねたのでしょう(笑)、Sleipnirのスクリプトをいろいろ作って我々ユーザーに提供してくれているあの8th_713さんがチャチャッと作ってコレでどう? てな感じでgistにアップしてくれました。
その後しばらくしてよりいっそう作り込まれた物も上がってきましていやもうコレは出る幕はないなと。

  • こんな感じに表示されます


ただ、gistの性質上rawから落としたファイルの文字コードutf-8なのでそのままだとShift-Jisが前提のSeahorseではユーザースクリプトととして利用できません。
そこで、オリジナルのスクリプトの文字コードだけを変えて保存し直した物をこちらへ上げておきます。

見た目にちとこだわりたい

スクリプトとしては上記の物で一応完成されているのですが、作者さんからライセンスフリーで提供するから見た目などを弄って公開してもいいよと言うお許しがでましたのでワタシなりに弄ってやろうといろいろと手を加えてみました。
当初は以前のデザインを踏襲しつつ、Seahorse版ということでワタシが勝手にイメージしているライトスカイブルーを背景色にしたものを作っていたんです。

  • 最初はこんな感じで行こうかなと・・・


ただココでちょっと待てよ、と。


実はGoogle Chromeのアレみたいに除外サイトをワンタッチで登録できる機能がついているわけではないので、あまりビューを占有しすぎるのもそのうちうざくなってきそうだなと。
そこで、できるだけ目立たない、というかバーが出現していてもビューの閲覧に邪魔にならないサイズでいて使い勝手も損なわない方が良いのでは無いかなと考え、さらにはバーに表示している文言が長いとオマケのボタンにも被っちゃう場合があるな・・・てなわけでワンアクションで済むところをあえて2アクションで翻訳ボタンが出現するようにしてみたり。
最初に出現するバーは普通サイズの文字がとりあえず表示しきれる高さに抑え、右側にはクローズボタンを配置。
バーそのものをクリックすることで翻訳ボタンなどが並ぶちょいと太めのバーが出現するといった作りになっています。
またバーそのものにはIE(Trident)エンジンのみが使えるDirectX利用の透過効果を与えてありますので、ビューをスクロールするとバーの下のコンテンツが透けて見えるようになっています。*1

  • 最初に出現するバーはこのくらい。透過率は85%とチョット濃いめで必要ないときはこの時点で右のxを押して消せます。

  • 最初のバーをクリックして切り替わるバーは透過率70%。後ろの文字がしっかりと確認出来ます。

さてその中身は・・・

まぁ興味のない方は読み飛ばして貰って結構です(笑)

例によってコードを晒しておきますね、スクリプター諸氏からみたらなんて無駄なことを・・・と叱られそうでコワイのですが・・・

  • これはGrani用に用意した物です
// このスクリプトが反応して欲しくないサイトがある場合には
// 下の例にならってそのサイトのドメインを除外登録してください。
// まず、スラッシュふたつ // のあとへ半角スペースあけて @exclude を追加し
// そのあとへ続けて半角スペースあけてドメイン名までのアドレス+アスタリスク[*]
// を追加するとそのサイトは除外されます。
// 
// ==UserScript==
// @name Google_Translate_for_Seahorse
// @description 日本語じゃないページを見つけるとGoogle_Translateで翻訳するか聞いてくる
// @include http*
// @exclude http://translate.google.*
// @exclude http://google.*/*
// @exclude http://twitpic.com/*
// @exclude http://seesmic.com/*
// @exclude http://www.tumblr.com/*
// @auther org by 8th_713 / design mod by Kei_F
// @licence free license
// ==/UserScript==
(function(_d){

// ここで画像ディレクトリの設定を行います。パスの記述はローカルファイルへのフルパスとなります。
// Graniユーザーの方で、インストーラーでの設定変更なしにインストールされた方はそのまま何もせずに利用可能なはずです。
// しかし初期に配布されていたGraniの場合、設置ディレクトリ名が”Fenrir & Co”の物も存在するようですのでその場合は
// ”Fenrir Inc”を”Fenrir & Co”へと書きかえてください。
// それ以外にインストールパスを変更している方は適宜、書き換えをお願いします。
// ディレクトリの区切りは\1個ではなく\\2個であることにご注意ください。
// 最後の二つはシングルクォートの中にダブルクォートが入れ子になっています。(HTMLへ値を渡しているため)

// Grani用設定ここから (32bit版OS)

var clsm = 'C:\\Program Files\\Fenrir Inc\\Grani\\plugins\\seahorse\\Google_Translate_for_SH\\close_m.png';
var clsmhot = 'C:\\Program Files\\Fenrir Inc\\Grani\\plugins\\seahorse\\Google_Translate_for_SH\\close_m_hot.png';
var cls = 'C:\\Program Files\\Fenrir Inc\\Grani\\plugins\\seahorse\\Google_Translate_for_SH\\close.png';
var clshot = 'C:\\Program Files\\Fenrir Inc\\Grani\\plugins\\seahorse\\Google_Translate_for_SH\\close_hot.png';
var trans = 'C:\\Program Files\\Fenrir Inc\\Grani\\plugins\\seahorse\\Google_Translate_for_SH\\translate.png';
var transhot = 'C:\\Program Files\\Fenrir Inc\\Grani\\plugins\\seahorse\\Google_Translate_for_SH\\translate_hot.png';
var hnt = '"C:\\Program Files\\Fenrir Inc\\Grani\\plugins\\seahorse\\Google_Translate_for_SH\\hint.png"';
var rpt = '"C:\\Program Files\\Fenrir Inc\\Grani\\plugins\\seahorse\\Google_Translate_for_SH\\report.png"';

// Grani用設定ここまで

	if(_d.body.innerText.length===0) return;
	var html = _d.getElementsByTagName('html')[0];
	if(html && html.lang==='ja') return;
	
	// Advanced rate check
	var ADVANSE = false;
	// Threshold of translate
	var RATE = 0.1; // ひらがな/カタカナ取得レートの閾値。たとえば0.3なら30%未満で翻訳バーが出現
	
	var result;
	if(ADVANSE)
		result = checkRateA();
	else
		result = checkRateN();
	
	if(result.rate <= RATE) 
		translate(result);
	
	function checkRateN(){
		var ts,te;
		ts = new Date * 1;
		var arr = _d.body.innerText.split('\n');
		var len = arr.length;
		var jp = 0;
		for(var i=0; i<len; i++){
			if( /[ぁ-んァ-ンァ-ン]/.test(arr[i]) )
				jp++;
		};
		te = new Date * 1;
		return { rate: jp / len, time: te - ts };
	};
	function checkRateA(){
		var ts = new Date * 1;
		var text = _d.body.innerText;
		var len = text.length;
		var jp = 0;
		for(var i=0; i<len; i++){
			if( /[ぁ-んァ-ンァ-ン]/.test(text[i]) )
				jp++;
		};
		var te = new Date * 1;
		return { rate: jp / len, time: te - ts };
	};
	function translate(result){
		var isOldMode = ((typeof document.body.style.maxHeight == "undefined") || (document.compatMode != 'CSS1Compat'));

	var css = 'body{margin:0 !important;padding:0 !important;}'
						+'span#toggle1{display:none;width:150px;position:absolute;top:35px;right:10px;text-align:left;'
						+'padding-left:2em;background-color:#fff3e5;border:1px solid #ccc;font-size:10px;}'
						+'span#toggle2{display:none;width:350px;position:absolute;top:35px;left:20px;text-align:left;'
						+'background-color:#ecf4ff;border:1px solid #ccc;font-size:10px;}'
						+'img#cm{cursor:hand;position:absolute !important;top:0px;right:5px;width:16px;height:16px;text-decoration:none;margin:0 !important;}'
						+'img#rate{cursor:hand;position:absolute !important;top:2px ;right:5px;width:40px;height:15px;margin:0 !important;}'
						+'img#hint{cursor:hand;position:absolute !important;top:2px ;left:5px;width:30px;height:19px;margin:0 !important;}'
						+'img#trns{display:none;cursor:hand;position:absolute;top:0px;border:none;left:' 
						+ (_d.body.clientWidth/2 - 35) 
						+ 'px;width:55px;height:20px;margin-top:0px;margin-left:auto;margin-right:auto:margin-bottom:0px;}'
						+'img#cls{display:none;cursor:hand;position:absolute;top:0px;border:none;left:' 
						+ (_d.body.clientWidth/2 + 35) 
						+ 'px;width:55px;height:20px;margin-top:0px;margin-left:auto;margin-right:auto:margin-bottom:0px;}';
		_d.createStyleSheet().cssText = css;

// 翻訳バー用DOM追加
		var head = _d.createElement('div');
			head.id = 'Sleipnir_Google_Translate';
			head.style.cssText = 'position:fixed; width:100%;height:100px !important;top:0px; left:0px;text-align:center;z-index:9999;';
			if(isOldMode) head.style.position = 'absolute';
			_d.body.appendChild(head);

// 1stバーtext
		var text = 'おや、どうやら日本語のページではないようです...でも判定ミスかも! (><;)  ページによってはうまくいかない場合がありますがGoogle翻訳を試みる場合はこのバーをクリック!'; // ココの文言は適当に書きかえてね♪

// HTML
		var htmltext1 = '<img src=' + rpt + ' id="rate" title="クリックでページの解析結果を表示" /><span id="toggle1">' + ['ひらがな/カタカナ出現率 (',	Math.round(result.rate*1000) /10,'%)<br />判定所要時間 : ',result.time,'?秒'].join('') + '</span>';
		var htmltext2 = '<img src=' + hnt + ' id="hint" title="クリックでヒントを表示" /><span id="toggle2">● そのまま翻訳ボタンを押すとページ全体の翻訳を行います。<br>● 単語や文章を選択反転させてから翻訳ボタンを押すとその範囲のみの翻訳になります。<br>● 翻訳後のページから前のページに戻るには、ツールバーの『戻る』ボタンを利用するか<br>  あるいはマウスジェスチャーなどのアクションをご利用ください。</span>';

// inner 1stバー
		var wrapper = _d.createElement('div');
			wrapper.id = 'wpr';
			wrapper.style.cssText = 'width:100%;height:18px;background-color:gray;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=85);';
			head.appendChild(wrapper);

		var bar = _d.createElement('a');
			bar.id ='bar';
			bar.href = '#';
			bar.style.cssText = 'cursor:hand;text-decoration:none;';
			wrapper.appendChild(bar);
			bar.onclick = function (){
										_d.getElementById('wpr').style.display = 'none';
										_d.getElementById('cm').style.display = 'none';
										_d.getElementById('parent').style.display = 'block';
										_d.getElementById('trns').style.display = 'block';
										_d.getElementById('cls').style.display = 'block';
										_d.getElementById('p2').style.display = 'block';
										_d.getElementById('p3').style.display = 'block';
										};

		var p = _d.createElement('p');
			p.id = 'disc';
				var cw = _d.body.clientWidth - 50;
			p.style.cssText = 'cursor:hand;margin:0px auto;width:' + cw + 'px;font-size:12px;color:#fff;text-align:center;';
			p.innerText = text;
			bar.appendChild(p);

		var cm = _d.createElement('img');
			cm.id = 'cm';
			cm.src = clsm;
			cm.onmouseover = function(){cm.src = clsmhot;};
			cm.onmouseout = function(){cm.src = clsm;};
			head.appendChild(cm);
			cm.onclick = close;

// inner 2ndバー
		var parentdiv = _d.createElement('div');
			parentdiv.id = 'parent';
			parentdiv.style.cssText = 'display:none;position:relative;top:0px;margin:0px auto:0;height:20px !important;background-color:gray;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=70);';
			head.appendChild(parentdiv);

		var p2 = _d.createElement('p');
			p2.id = 'p2';
			p2.style.cssText = 'display:none;cursor:hand;';
			p2.innerHTML = htmltext1;
			head.appendChild(p2);
			p2.onclick= toggle1;

		var p3 = _d.createElement('p');
			p3.id = 'p3';
			p3.style.cssText = 'display:none;cursor:hand;';
			p3.innerHTML = htmltext2;
			head.appendChild(p3);
			p3.onclick = toggle2;

// buttons
		var btn1 = _d.createElement('a');
			btn1.href = "#";
			btn1.style.cssText = 'text-decoration:none;';
			head.appendChild(btn1);
			btn1.onclick = click;

		var div1 = _d.createElement('img');
			div1.id = 'trns';
			div1.src = trans;
			div1.onmouseover = function(){div1.src = transhot;};
			div1.onmouseout = function(){div1.src = trans;};
			div1.setAttribute('title','Google翻訳を実行');
			btn1.appendChild(div1);

		var btn2 = _d.createElement('a');
			btn2.href = "#";
			btn2.style.cssText = 'text-decoration:none;';
			head.appendChild(btn2);
			btn2.onclick = close;

		var div2 = _d.createElement('img');
			div2.id = 'cls';
			div2.src = cls;
			div2.onmouseover = function(){div2.src = clshot;};
			div2.onmouseout = function(){div2.src = cls;};
			div2.setAttribute('title','翻訳バーを閉じる');
			btn2.appendChild(div2);
	};

// 翻訳ブックマークレット展開部
	function click(){
		var t = ((window.getSelection&&window.getSelection())||(document.getSelection&&document.getSelection())||(document.selection&&document.selection.createRange&&document.selection.createRange().text));
		if(t!=''){
			var e = (document.charset||document.characterSet);
			location.href = 'http://translate.google.com/?text='
			+ t
			+ '&hl=ja&langpair=auto|ja&tbb=1&ie='
			+ e;
		}
		else{
			location.href='http://translate.google.com/translate?u='
			+ encodeURIComponent(location.href)
			+ '&hl=ja&langpair=auto|ja&tbb=1&ie=';
		};
	};
	function close(){
		document.getElementById('Sleipnir_Google_Translate').style.display = 'none';
		document.getElementById('parent').style.display = 'none';
	};

// ヒント/リポートボタンのトグル
	var flag1 = true;
	var flag2 = true;
	function toggle1(){
		if (flag1){
			document.getElementById('toggle1').style.display = 'block';
		}
		else{
			document.getElementById('toggle1').style.display = 'none';
		};
	flag1 = !flag1;
	};
	function toggle2(){
		if (flag2){
			document.getElementById('toggle2').style.display = 'block';
		}
		else{
			document.getElementById('toggle2').style.display = 'none';
		};
	flag2 = !flag2;
	};
})(document);


本当はパスの指定にSleipnirScriptでsleipnir.ScriptFullName()*2を利用しようかと考えたのですがそうなるとスクリプト本体も手直ししなくちゃいけないしGraniユーザーにもわかりやすいかなとあえてフルパスで。

設置

上記のスクリプトをコピペして環境に合わせて編集してもいいのですが、結局画像を別に用意することになってしまったのでとりあえずインストーラーでブラウザを標準的にインストールしてあると仮定したファイルを4種用意しました。
普通のPCであればまだほとんどが32bit版のWindowsでしょうけれど、ワタシみたいにメインを64bit版Windowsにしているヒトのことも考えてのことです。
ファイルはここ↓からどうぞ。
Google_Translate_for_SheahosePack.zip

4種類をひとつのパッケージにしてzip圧縮してあります。*3
内容は以下のようになっています。

Google_Translate_for_SheahosePack

展開したフォルダのうち、自分の使用OSとブラウザにあうフォルダの中にあるpluginsフォルダをコピーしてブラウザのインストールフォルダにある同名のフォルダへ上書き保存します。
ブラウザのインストールパスが標準ではない場合、つまり C:\program files あるいは C:\program files (x86) 以降でない場合はスクリプトファイルを編集してパスを書きかえてやる必要があります。
また、ブラウザ本体の上位ディレクトリ名も Fenrir & Co の場合と Fenrir Inc の2種類存在する場合もあるようなので、設置後に動かなかった場合はその辺をチェックしてみて、もしも違っていたならスクリプトのパス指定部分を編集して書きかえてください。


翻訳スクリプトの大元はGoogle翻訳ブックマークレットなので、ページ翻訳や選択翻訳などもできてとっても便利です。
設置もなるべく容易になるようにしてみましたが、もし上手く動作しないようでしたらコメントください。できる範囲でサポートさせていただきます。

*1:当初はIE8がインストールされている環境向けに作っていたので画像をdataスキームつかって読み込ませるべくbase64エンコードした文字列をスクリプトファイル内に記していました。ただ、いろんな環境対応を考えたときマズイなとおもったのであえて画像を別ファイルにしたという経緯があります。

*2:Graniだとgrani.ScriptFullName()になる。

*3:個人的には7zipが好みなのですが、展開ツールを持っていない方のためにWindowsXP以降なら標準機能で展開出来るzipにしました。