文書の過去の版を表示しています。


DokuWikiの日本語対応

DokuWikiのインストールマニュアル日本語版―作業中)にしたがって普通にインストールし、設定ファイルのconf/dokuwiki.phpconf/local.phpの中に

$conf['lang'] = 'ja'; 

と記述すると、ユーザーインターフェースの文字列などは日本語になる。 機能面での日本語対応を強化するために、次の改造を加える。

メールのエンコード

DokuWikiでは、文書を誰かが編集したときなどにお知らせのメールを送る機能があるが、そのメールのエンコードを自前のUTF-8化ルーチンで行っている。日本語のメールはUTF-8ではなくISO-2022-JPでエンコードするのが通例なので、内蔵のmb_send_mail関数が使える場合はそちらを使って$conf['lang']で指定したエンコードで送信するように変更する。

inc/mail.phpというファイルのfunction mail_sendという関数を変更。

function mail_send($to, $subject, $body, $from='', $cc='', $bcc='', $headers=null, $params=null){
  if(defined('MAILHEADER_ASCIIONLY')){
    $subject = utf8_deaccent($subject);
    $subject = utf8_strip($subject);
  }

の直後に

  global $conf;
  if (function_exists('mb_send_mail')) {
    $header  = "From: $from\nCc: $cc\nBcc: $bcc";
    $header .= $headers;
    $header  = trim($header);
    mb_language($conf['lang']);
    if($params == null){
      return @mb_send_mail($to,$subject,$body,$header);
    }else{
      return @mb_send_mail($to,$subject,$body,$header,$params);
    }
  }

を追加。

検索の日本語対応

DokuWikiでは自前の全文検索エンジンを搭載している。

新しいページを登録するときに、全文検索のインデックス(なんという単語がどのファイルにいくつ入っているかを記録した索引)ファイルの中に、登録したページの索引情報を記録するようになっているのだが、ページの中から単語を切り出すのに空白文字で区切られたところを単語と認識して処理している。

日本語のように、単語の間に空白文字を書かない文書をこのプログラムに通すと、日本語の文字が全部ひとつながりになって1単語と認識されて登録されてしまう。たとえば、“DokuWikiでは自前の全文検索エンジンを搭載している。”という文は“DokuWiki”と“では自前の全文検索エンジンを搭載している。”という2つの単語として索引に登録されてしまう。このため、例えば“全文検索”とか“エンジン”とかで検索してもこのページはヒットせず、“では自前の全文検索エンジンを搭載している。”で検索して初めてヒットする、ということになってしまう。

これを解決するために、索引に登録するところで日本語の文章の単語の間に空白を入れる処理を追加する。日本語の文章を分かち書きに変換するプログラムとしてはkakasiが有名だが、kakasiは標準ではUTF-8のテキストに対応していない。 nkf -wE | kakasi -wc | nkf -wE などと、前後に文字コード変換プログラムのnkfやiconvを使って無理やりUTF-8→EUC-JP→UTF-8 という変換をするという手もあるが、効率が悪いし、なによりかっこ悪い。そこで、UTF-8に対応していて、kakasiより数倍早いという触れ込みのMeCabを使ってみる。

MeCabのインストール

MeCabの説明ページの説明に従って必要なファイルをダウンロードしてインストールする。 別にダウンロードする辞書ファイルは「ipadic-2.4.4/2.5.0/2.5.1のいずれか」と説明されているが、どうやら2.6.1までは使えるようだ。2.6.2以降は辞書の構造が変わっているために使えない。

UTF-8の環境で使えるようにconfigureする。

$ ./configure --prefix=/usr/local/nls --disable-shared --with-charset=utf8

/usr/local/の下に自由にファイルやディレクトリを作れる場合は--prefix=/usr/local/nlsは不要。私が使っているWestHostのサーバーでは管理者アカウントは本物のroot権限ではなく/usr/local/manなど一部のディレクトリの下にアクセスできないため、別のディレクトリを指定している。ここではnlsというディレクトリを作ってそこに日本語対応用のプログラムやライブラリをまとめて入れているが、名前はなんでもよい。たとえば、/usr/mylocal/の下にlibやincludeなどを作り、/etc/ld.so.confに/usr/mylocal/lib/を追加するなどというのもあり。 --disable-sharedは、シェアードライブラリを使わない指定。このほうがパフォーマンスが少しよくなるはず。 --with-charset=utf8を指定しないとEUC-JPでビルドされる。

configureしたら、あとはmakeするだけ。

$ make
$ make install

インストールしたら、正しく分かち書きできるかどうか試してみる。

$ /usr/local/nls/bin/mecab -O wakati
上野発の夜行列車降りたときから青森駅は雪の中
上野 発 の 夜行 列車 降り た とき から 青森 駅 は 雪 の 中
^D

のように、入力した文字が分かち書きされて表示されればOK。

全文検索インデクサーの改造

MeCabで分かち書きをさせるには、mecab -O wakati。標準入力に元のテキストを入力すると標準出力に分かち書きしたテキストが出てくる。 PHP 4.3で追加されたproc_open()関数を使って、パイプ渡しでテキストを分かち書き処理させる。

inc/indexer.php

どこか先頭に近いところに

  define ('PRE_TOKENIZER', '/usr/local/nls/bin/mecab -O wakati');

を追加。これで、分かち書きをする外部プログラムの名前(シェルで呼び出すときのコマンド)を定数定義。kakasiを使うなら'nkf -We | kakasi -wc | nkf -Ew'。 本当は$conf['pre_tokenizer']か何か、globalな設定用変数に入れるようにしたほうがお行儀がいいけど、とりあえずquick hack。

あとは、検索対象の文字列をtoken(単語)に切り分ける部分の直前で、この外部プログラムを呼び出すようにするだけ。 function idx_getPageWords($page)の

      $body   = rawWiki($page);

の後ろに

     if(function_exists(proc_open) && defined('PRE_TOKENIZER')) {
         $dspec = array(
            0 => array("pipe", "r"),
            1 => array("pipe", "w"),
            2 => array("file", "/dev/null", "w")
        );
        $process = proc_open(PRE_TOKENIZER, $dspec, $pipes);
        if(is_resource($process)) {
            fwrite($pipes[0], $body . "\n");
            fclose($pipes[0]);
        }
        $body = '';
        while(!feof($pipes[1])) {
            $body .= fgets($pipes[1], 32768);
        }
        fclose($pipes[1]);
        proc_close($process);
    }

を追加

function idx_tokenizer($string,&$stopwords) の先頭

    $words = array();

の後ろに

    if(function_exists(proc_open) && defined('PRE_TOKENIZER')) {
        $dspec = array(
            0 => array("pipe", "r"),
            1 => array("pipe", "w"),
            2 => array("file", "/dev/null", "w")
        );
        $process = proc_open(PRE_TOKENIZER, $dspec, $pipes);
        if(is_resource($process)) {
            fwrite($pipes[0], $string . "\n");
            fclose($pipes[0]);
        }
        $string = '';
        while(!feof($pipes[1])) {
            $string .= fgets($pipes[1], 32768);
        }
        fclose($pipes[1]);
        proc_close($process);
    }

を追加。

全文検索インデクサーの不具合修正

以上で全文検索の日本語対応改造は完了だが、本日現在の最新バージョン(20050922)には、全文検索機能にもとからバグがあるようで、ASCII文字以外の文字が混ざった単語を検索すると、検索結果ページで表示されるヒット数が全部1になってしまう。 これに対する修正を加える。 上と同じinc/indexer.phpファイル、idx_getPageWords関数の中の

    $words[$w] = $c + (isset($words[$w]) ? $words[$w] : 0);

という行を

    $words[$w] = $c * $count + (isset($words[$w]) ? $words[$w] : 0);

に変更(‘* $count’という部分を追加)。

この変更後、修正したり新規に追加したりして保存しなおしたファイルは、検索結果のヒット数が正常に表示されるようになる。 http://bugs.splitbrain.org/?do=details&id=653 にバグ報告済み。

テンプレートの変更

検索の機能はこれで働くようになるのだが、ヒット数の多いサイトの場合、このままではサーバーの負荷がかなり重くなるはず。 DokuWikiでは、ページを誰かが表示するたびに、そのページの検索インデックスを作り直す仕組みになっている。もともと全文検索のインデックス更新は軽い仕事ではない上に、この改造で毎回Mecabを動かすことになって、負荷が大きくなっている。 FIXME あまりきれいな解決策ではないけれど、緊急のQuick Hackで、検索インデックスの更新を「ときどき」行うようにしておく。 表示のたびにインデックスを更新するしくみは、標準添付のテンプレートmain.phpの中にある

<?php tpl_indexerWebBug()?>

という行で呼び出している。画面上は1 Pixelの画像だが、これを表示するたびにindexer.phpが呼び出される仕組み。 この行を例えば

<?php if(time() % 10 == 0) tpl_indexerWebBug();?>

などと変更する。

dokuwiki/localize.1133757551.txt.gz · 最終更新: 2007/07/23 16:50 (外部編集)
CC Attribution-Noncommercial-Share Alike 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0