2006年3月7日火曜日

SledgeでUTF8なサイトを作る。

Sledgeはプログラム内で扱うデータがEUC-JPにエンコードされたバイナリデータを前提としていて、テンプレートもEUC-JPで作成する必要があります。
これだけが原因ではないのですが、UTF-8なサイトを作ろうとすると、結構いろいろなワナがあります。
このことはSledgeのメーリングリストでも、過去にいろいろ議論されています。
http://lists.sourceforge.jp/mailman/archives/sledge-users/2004-March/000281.html
http://lists.sourceforge.jp/mailman/archives/sledge-users/2004-March/000292.html
http://lists.sourceforge.jp/mailman/archives/sledge-users/2004-March/000294.html

これらの議論は2004年の3月に行われているので、もう2年も前。これだけ時間がたっているので、SledgeでUTF-8をガンガン使っている人もたくさんいると思うのですが、最近ふと、SledgeでUTF8なサイトを作ろうと思い、情報集めのためにググッてみた際、「SledgeでUTF-8扱うにはこうすればいい!」というようなそのものずばりな解決法が見つからなかったので、僕がSledgeでUTF8なサイトを作ったその過程と成果物をさらしてみたいと思います。

* 過程

まず、SledgeでUTF-8を扱うにあたって、以下のような暗黙のルールを定義しました。

- プログラム内で扱うデータはFlagged UTF-8
- テンプレートの文字コードはUTF-8
- RequestクラスでインプットデータをFlagged UTF-8にデコード
- アウトプット時にUnflagged UTF-8にエンコード
- mod_perlで運用すること前提。CGIでの動作は無視


次に上記を実現するようなモジュールを書いてみました。

*** Sledge::Pages::Apache::I18N

まずは、インプットデータをFlagged UTF-8にデコードするRequestクラスを作ってみました。これはSledgeに標準でついているSledge::Charset::UTF8を使うとできそうですが、Sledge::Charset::UTF8でインプットデータをFlagged UTF-8にデコードしてもApache::RequestがUnflaggedなデータにしてしまうため、うまくいかないようです。
なので、Apache::Requestをどうにかしないといけないのですが、CPANを眺めていたら、Apache::Request::I18Nというまさにそのものずばりなモジュールがあったのでこいつを使ってみました。
こいつを使って書いたSledge::Pages::Apacheの代替モジュールはこんな感じです。

>

package Sledge::Pages::Apache::I18N;
use strict;
use base qw(Sledge::Pages::Base);

use Apache;
use Apache::Request::I18N;

sub create_request {
my ($self, $r) = @_;
my $req = Apache::Request::I18N->new(
$r || Apache->request,
DECODE_PARMS => 'utf-8',
);
# $req->param; do parse here
return $req;
}

1;
<


*** Sledge::Charset::UTF8::I18N

次に、Sledge::Pages::Apache::I18Nでインプットデータのデコードをやってくれるようになったので、
アウトプットの際のエンコードのみを行うSledge::Charsetサブクラスを作りました。

>

package Sledge::Charset::UTF8::I18N;
use strict;
use base qw(Sledge::Charset::Null);

use vars qw($VERSION);
$VERSION = '0.01';

use Encode;

sub content_type {
return 'text/html; charset=UTF-8';
}

sub output_filter {
my($self, $content) = @_;
return Encode::encode("UTF-8", $content);
}


1;
<

*** Sledge::Template::TT::I18N

最後に、UTF-8なテンプレートを読み込んで内部ではFlagged UTF-8として扱ってくれるSledge::Template::TTの代替モジュールを作成しました。Sledge::Template::TTだとUTF-8なテンプレートを読み込んでも内部では、UnflaggedなUTF-8として扱われるので、Flagged UTF-8な文字列を埋め込むと文字化けを起こしてしまいます。

>

package Sledge::Template::TT::I18N;
use strict;
use base qw(Sledge::Template::TT);

use vars qw($VERSION);
$VERSION = '0.01';

sub output {
my $self = shift;
my %config = %{$self->{_options}};
my $input = delete $config{filename};
$config{LOAD_TEMPLATES} = [Sledge::Template::TT::I18N::Provider->new(\%config)];
my $template = Template->new(\%config);
unless (-e $input) {
Sledge::Exception::TemplateNotFound->throw(
"No template file detected. Check your template path.",
);
}
$template->process($input, $self->{_params}, \my $output)
or Sledge::Exception::TemplateParseError->throw($template->error);
return $output;
}

package Sledge::Template::TT::I18N::Provider;
use strict;
use base qw(Template::Provider);

sub _load {
my $self = shift;

my ($data, $error) = $self->SUPER::_load(@_);

if(defined $data) {
$data->{text} = utf8_upgrade($data->{text});
}

return ($data, $error);
}

sub utf8_upgrade {
my @list = map pack('U*', unpack 'U0U*', $_), @_;
return wantarray ? @list : $list[0];
}

1;
<

*** MyProj::Pagesクラスに組み込む

今まで作成したモジュールはこんな感じでPagesクラスに組み込みました。

>


package YourProj::Pages;
use strict;
use base qw(Sledge::Pages::Apache::I18N);
use Sledge::Template::TT::I18N;
use Sledge::Charset::UTF8::I18N;

....

sub create_charset {
my $self = shift;
Sledge::Charset::UTF8::I18N->new($self);
}

<

これでとりあえず、SledgeでUTF-8を扱うことができました。

* 成果物

今回作成したモジュールたちです。

- Sledge-Pages-Apache-I18N-0.01
- Sledge-Template-TT-I18N-0.01
- Sledge-Charset-UTF8-I18N-0.01

0 件のコメント:

コメントを投稿