日本語対応 wdiff で単語ごとの差分表示 - 拡張 POSIX シェルスクリプト Advent Calendar 2013 - ダメ出し Blog

2013-12-12(Thu) [sh][shell][translation] [更新履歴]

Doc-ja Advent Calendar 2013拡張 POSIX シェルスクリプト Advent Calendar 2013、12日目の記事です。

今日は翻訳作業に役立つツールを 2つ紹介します。 単語ごとの差分を表示する wdiff コマンドと、 その日本語対応版 jwdiff コマンドです。

wdiff の紹介

wdiff はテキストデータの差分を単語ごとに示してくれるツールです。

一般的によく利用されるテキストデータ差分表示ツールである diff は、 行ごとに差分を表示します。

$ diff -u en.txt.old en.txt
--- en.txt.old	2013-12-12 02:16:42.309525648 +0900
+++ en.txt	2013-12-12 02:44:11.417561919 +0900
@@ -1,6 +1,6 @@
-Hello, guys
+Hello, guys.
 
-I have an pen. That is a pen.
+I have a pen. That is a pen too.
 Regards,
 
-  --Boo Bar
+  --Foo Bar

これに対し wdiff は単語ごとに差分を表示します。 このため、文章の一部のみが書き変わったテキストの違いを見つけるのに適しています。

$ wdiff en.txt.old en.txt
Hello, [-guys-] {+guys.+}

I have [-an-] {+a+} pen. That is a [-pen.-] {+pen too.+}
Regards,

  [---Boo-]

  {+--Foo+} Bar

git の git diff --word-diff のように内蔵の差分表示機能に wdiff 相当の機能も持ったものもあります。 Mercurial の hg diff のように wdiff 相当の機能がないものでも、 unified 形式の差分が出力可能であれば、それを wdiff + -d (--diff-input) オプションに通すことで、単語ごとの差分に変換することもできます。

$ hg diff |wdiff --diff-input
…省略…

wdiff の問題点

wdiff コマンドは、翻訳作業で利用するには致命的な 「日本語に対応していない」という大きな問題があります。

上の wdiff の実行例の出力をよく見るとわかりますが、実は wdiff は「単語ごとの差分の表示」ではなく、 「空白区切りの語ごとの差分を表示」しているに過ぎません。 日本語のように単語区切りなどで空白文字が入らないテキストに対しては、 あまり威力を発揮できません。

$ diff -u ja.txt.old ja.txt
--- ja.txt.old	2013-12-12 02:33:03.437888390 +0900
+++ ja.txt	2013-12-12 02:32:58.325800783 +0900
@@ -1,6 +1,6 @@
-よう、野郎ども
+よう、野郎ども。
 
-俺はペんを持っている。あれはペンだ。
+俺はペんを持っている。あれもペンだ。
 じゃあの。
 
-  --ブー バー
+  --フー バー
$ wdiff ja.txt.old ja.txt
[-よう、野郎ども

俺はペんを持っている。あれはペンだ。-]{+よう、野郎ども。

俺はペんを持っている。あれもペンだ。+}
じゃあの。

  [---ブー-]

  {+--フー+} バー

jwdiff を作ってみた

ずいぶん前から日本語の文章に対しても wdiff を使いたい欲求があったのですが、 なんとなく放置してはや数年が経過…。そしてつい先々月、急に思い立ち、 ようやくこの欲求を叶えるコマンドを作りました。 それが jwdiff です。

jwdiff

MeCab を利用して日本語の文章を分かち書きに変換し、それに対して wdiff を適用、 最後に分かち書きを解くだけの、簡単な bash スクリプトです (ksh でも動きますが、現在のところ zsh は未対応)。 動作には wdiff コマンドのほかに MeCab の mecab コマンドが必要になりますので、実行前に適宜インストールしてください。

jwdiff を利用すれば、日本語の文章に対してもいい感じで単語ごとの差分を表示できます。

$ jwdiff ja.txt.old ja.txt
よう、野郎ども{+。+}

俺はペんを持っている。あれ[-は-]{+も+}ペンだ。
じゃあの。

  --[-ブー-]{+フー+} バー

そして英語の文章に対しても威力を発揮します。 jwdiff なら空白区切りでない単語ごとの差分を表示できます。

$ jwdiff en.txt.old en.txt
Hello, guys{+.+}

I have [-an-] {+a+} pen. That is a pen {+too+}.
Regards,

  --[-Boo-]{+Foo+} Bar

jwdiff の問題点と回避策 (colordiff)

wdiff には --less-mode (-l) というちょっと気持ち悪い名前のオプションが あります。これを指定すると、差分情報を色付きで出力するようになります。 何故か出力先が端末の場合は機能しないので、 ページャーなどを通す必要があります。

$ wdiff --less-mode en.txt.old en.txt |less -R
…省略…
$ jwdiff --less-mode en.txt.old en.txt |less -R
…省略…

残念なことに、日本語文章で --less-mode を使うと文字が化けます。 色付けしたいときは代わりに colordiff コマンドを利用しましょう。

`jwdiff ja.txt.old ja.txt |colordiff` の実行例

colordiff は行を跨いだ wdiff の差分の色付けに対応していないようなので、 jwdiff-n (--avoid-wraps) オプションを指定したほうがよいです。

zsh ユーザーなら次のようなグローバルエイリアスを設定すると便利かもしれません。

alias -g D='|colordiff |$PAGER'
alias -g WD='|jwdiff --diff-input --avoid-wraps |colordiff --difftype=wdiff |$PAGER'

使用例:

$ git diff WD
…省略…
$ git log -p WD
…省略…

colordiff の問題点

現在の colordiff は、「入力をすべて読み終わってから色付け処理する」というちょっと困った仕様になっています。 大量の差分情報が含まれた git リポジトリで git log -p |colordiff すると、 長い時間帰ってきません。 機会があれば修正したいと思っていますが、いつになるやら…。

DocDiff

今回 jwdiff を作ったのですが、すでに同等+αの実装が存在しました。

ネタ募集

拡張 POSIX シェルスクリプト Advent Calendar 2013の今後のネタですが、 相変らず 10日分くらい足らなそうな見込みです。

もしよろしければ、 シェルスクリプトの関する疑問・質問、スクリプトの添削依頼、やって欲しいネタをください!! 記事に反映できるかどうかは内容や私の実力次第ですし、お礼は特にご用意できませんが、 よろしくお願いします。orz


ところで、12月25日はクリスマスな上に、 OSS 界隈で地味に活躍されているふみやすさんの誕生日ですね!
http://www.amazon.co.jp/registry/wishlist/27M7TV8CEEF6G?sort=priority

逆に、あなたの書いた OSS や Blog や Advent Calendar が気に入ったら何か送りたく なってしまうかもしれないので、プロフィールや Web サイトに あなたの Amazon 欲しいものリストの URL を貼っておいてくださいね!


私が勤める OSSTech っていう某弊社で社員募集しているようです。 人材紹介会社を介さなければ、入社後に 20万円のボーナス! 「ふみやすっていう人に紹介された」と言ってもらえると私にもボーナス!! → https://www.osstech.co.jp/recruit/


よろしければ、これまで参加した/参加予定のほかの Advent Calendar もどうぞ。