Vimで和英、英和翻訳をする(GENE95を使う場合)

# 概要(やりたいこと)
Vim からローカルの辞書ファイルで和英・英和翻訳をしたいので簡単な関数を書いてみたという話です。
だいたいこういうことがしたかったです。

* Vim でプログラミング時に変数名考えるときに「和英」翻訳したい
* 逆に英単語を「英和」翻訳したい
* ブラウザ立ち上げて翻訳サイト行くのはめんどい
* ネットワーク通信せずローカルのファイルで一瞬で翻訳してほしい


## 実現イメージ
![lice1.gif](https://qiita-image-store.s3.amazonaws.com/0/33349/44257532-6ef1-b50d-27f3-a9b121d991d1.gif)

## 代替方法
Vim で翻訳するには以下のような素晴らしいプラグインがすでにあります。用途が合えばこれらのほうが断然洗練されています。

* kaoriya さんの [dicwin.vim](http://nanasi.jp/articles/vim/dicwin_vim.html) のほうが使いやすいが「和英」翻訳は対応していない
* kaoriya さんの [codic-vim](http://www.kaoriya.net/blog/2014/01/04/) なら和英英和両方できるし使いやすいが、codicは対応していない単語がまだ多い
* mattn さんの [excitetranslate-vim](https://github.com/mattn/excitetranslate-vim) なら和英英和両方できるが、Exciteでの翻訳なのでちょっと待たされる

## 必要環境
* GNU grep 2.21 (-A, --after-context オプションが使える必要がある)
* GENE 辞書

## 確認環境
* Linux Fedora 21
* VIM - Vi IMproved 7.4 (2013 Aug 10, compiled Oct 13 2014 08:58:48) Included patches: 1-207, 209-475

# 基本編
## 1. GENE 辞書取得
GENE辞書が前提となります。取得方法については [dicwin.vimでvimエディタに英和辞書を組み込む。 &mdash; 名無しのvim使い](http://nanasi.jp/articles/vim/dicwin_vim.html) がわかりやすいです。

## 2. Function 定義
以下を.vimrcファイルに記載します。

```vim:.vimrc
function! s:DictionaryTranslate(word)
	let l:gene_path = '~/.vim/dict/gene.txt'
	let l:output_option = a:word =~? '^[a-z_]\+$' ? '-A 1' : '-B 1' " 和英 or 英和
	silent pedit Translate\ Result
	wincmd P
	%delete " 前の結果が残っていることがあるため
	setlocal buftype=nofile noswapfile modifiable
	silent execute 'read !grep -ihw' l:output_option a:word l:gene_path
	silent wincmd p
endfunction
command! -nargs=1 -complete=command DictionaryTranslate call <SID>DictionaryTranslate(<f-args>)
```

以上です。「:DictionaryTranslate hogehoge」もしくは「:DictionaryTranslate ほげほげ」を実行すると翻訳結果がプレビューウィンドウに表示されます。
引数で指定した単語を含む候補が表示されます。

GENE辞書のフォーマットは以下のように、英語と対応する日本語が1行ごとに記載されているので、grep の-A, --after-context」「-B, --before-context」オプションを利用し表示しています。

```text:gene.txt
slack time
余分な時間
slacken
ゆるむ,減じる,をゆるめる,を減ずる,不活発になる,弱まる
・・・
```

# 応用編
ちょっと簡単すぎるので以下の機能を追加してみます。

* カーソル位置の単語を翻訳する
* コマンド履歴に実行履歴を残す
* 検索するときに完全一致する単語を優先的に表示する

```vim:.vimrc(改良後)
function! s:DictionaryTranslate(...)
	let l:word = a:0 == 0 ? expand('<cword>') : a:1
	call histadd('cmd', 'DictionaryTranslate '  . l:word)
	if l:word ==# '' | return | endif
	let l:gene_path = '~/.vim/dict/gene.txt'
	let l:jpn_to_eng = l:word !~? '^[a-z_]\+$'
	let l:output_option = l:jpn_to_eng ? '-B 1' : '-A 1' " 和英 or 英和

	silent pedit Translate\ Result | wincmd P | %delete " 前の結果が残っていることがあるため
	setlocal buftype=nofile noswapfile modifiable
	silent execute 'read !grep -ihw' l:output_option l:word l:gene_path
	silent 0delete
	let l:esc = @z
	let @z = ''
	while search("^" . l:word . "$", "Wc") > 0 " 完全一致したものを上部に移動
		silent execute line('.') - l:jpn_to_eng . "delete Z 2"
	endwhile
	silent 0put z
	let @z = l:esc
	silent call append(line('.'), '==')
	silent 1delete
	silent wincmd p
endfunction
command! -nargs=? -complete=command DictionaryTranslate call <SID>DictionaryTranslate(<f-args>)
```

死ぬほど汚くなりました。。一応以下簡単に解説します。。

## カーソル位置の単語を翻訳する
```vim:
function! s:DictionaryTranslate(...)
	let l:word = a:0 == 0 ? expand('<cword>') : a:1
	・・・
endfunction
command! -nargs=? -complete=command DictionaryTranslate call <SID>DictionaryTranslate(<f-args>)
```
まず、可変長引数を受け取るようにし、もし与えられなかった場合カーソル位置の単語を取得するようにしています。

## コマンド履歴に実行履歴を残す
```vim:
call histadd('cmd', 'DictionaryTranslate '  . l:word)
```
Vim の動きとして「完全にマッピングから実行されたコマンドは履歴に残らない(:help history)」のですが、
このコマンドにおいては単語を修正して再実行したいことがあるので、自前でコマンド履歴に追加をしています。

## 検索するときに完全一致する単語を優先的に表示する
翻訳対象の語は複数に一致することがあるのですが、完全一致したものを優先したいので、先頭に移動しています。
もっと綺麗に書けるはずなので解説はなしです。

# さいごに
良いVim lifeを！