追記 (2009/02/09):
Using Unicode in Catalyst Applicationsを参考に一部改変しました。
以前、Catalystでマルチバイトを取り扱うときのまとめという記事を書いたのですが、情報が少し古いので、最近僕がやっている方法を紹介します。
この記事では
- Catalyst 5.7015
- Catalyst::View::TT 0.27
- Catalyst::Plugin::ConfigLoader 0.22
- Catalyst::Plugin::FillInForm 0.10
- Catalyst::Plugin::Static::Simple 0.20
- Catalyst::Plugin::Unicode 0.8
の環境で紹介しています。
また、基本的にDBICに関しては以前の記事の通りDBIx::Class::UTF8Columnsを使うという感じです。
追記:
Using Unicode in Catalyst ApplicationsによるとDBDのオプションで指定する方法が紹介されています。ここに補足記事を書きました。
まず、こんな感じのアプリケーションを用意します。
MyApp
package MyApp;
use strict;
use warnings;
use Catalyst::Runtime '5.70';
use parent qw/Catalyst/;
use Catalyst qw/
ConfigLoader
Static::Simple
FillInForm
/;
our $VERSION = '0.01';
__PACKAGE__->config( name => 'MyApp' );
__PACKAGE__->setup();
1;
MyApp::View::TT
package MyApp::View::TT;
use strict;
use base 'Catalyst::View::TT';
__PACKAGE__->config(TEMPLATE_EXTENSION => '.tt');
1;
MyApp::Controller::Root
package MyApp::Controller::Root;
use strict;
use warnings;
use parent 'Catalyst::Controller';
__PACKAGE__->config(namespace => '');
sub index : Path : Args(0) {
my ($self, $c) = @_;
my $foo = $c->req->param('foo');
$c->stash->{foo} = $foo;
$c->stash->{length} = length $foo;
$c->stash->{template} = 'index.tt';
$c->forward($c->view('TT'));
$c->fillform;
}
1;
root/index.tt
<html>
<body>
<h1>日本語テスト</h1>
<form method="post">
<input type="text" name="foo" />
<input type="submit" name="submit" />
</form>
<hr/>
[% foo %] ([% length %])
</body>
</html>
このまま立ち上げてブラウザでアクセスしても特に文字化けすることはありません。フォームにマルチバイト文字を入力しても化けません。が、$lengthの値が意図しない値になってます。これは、デフォルトではリクエストパラメータにUTF8フラグが立っていないためバイト数になっていることが原因です。
都度decodeしてUTF8フラグを立てるのもいいですが、僕は面倒なのでCatalyst::Plugin::Unicodeを使っています。
MyApp
-- snip --
use Catalyst qw/
ConfigLoader
Static::Simple
FillInForm
Unicode
/;
-- snip --
これによって$c->req->paramで取れる値には全てUTF8フラグが立ちます。これでいちいちdecodeする手間が省けて、$c->stash->{length}にも文字数が入るようになりました。が、実行するとわかる通り、フォームにマルチバイト文字を入力すると「日本語テスト」の部分が文字化けします。TTで読み込まれた文字列にはUTF8フラグが立っていないのにUTF8フラグが立っている[% foo %]を評価したためです。
これを回避するために以前ではCatalyst::View::TT::ForceUTF8を使っていましたが、Catalyst::View::TT 0.21からTemplate::Providerをサポートするようになったのでこれを利用します。この設定にはTemplate::Provider::Encodingのインストールが必要です。
MyApp::View::TT
-- snip --
__PACKAGE__->config(
TEMPLATE_EXTENSION => '.tt',
DEFAULT_ENCODING => 'utf8',
PROVIDERS => [
{
name => 'Encoding',
copy_config => [qw(DEFAULT_ENCODING INCLUDE_PATH PRE_CHOMP POST_CHOMP)]
}
],
);
-- snip --
追記:
Using Unicode in Catalyst ApplicationsによるとTTのENCODINGオプションを指定するだけで良いみたいです。seems to be undocumentedとありますが、TTのFAQに書いてありました。
__PACKAGE__->config(
TEMPLATE_EXTENSION => '.tt',
ENCODING => 'utf8',
);
これで解消されます。
今度はMyApp::Controller::Rootで明示的に$c->stash->{foo}にマルチバイト文字を指定してみます。
MyApp::Controller::Root
-- snip --
sub index : Path : Args(0) {
my ($self, $c) = @_;
my $foo = 'ほげ';
$c->stash->{foo} = $foo;
$c->stash->{length} = length $foo;
-- snip --
化けます。
これは
MyApp::Controller::Root
package MyApp::Controller::Root;
use strict;
use warnings;
use utf8;
use parent 'Catalyst::Controller';
-- snip --
これで回避できます。ちなみにこうすることによって
$c->req->params->{foo} = 'ほげ';
のように、Controll内で直接リクエストパラメータを指定してFillInFormしても化けないのでCatalyst::Plugin::FillInForm::ForceUTF8などは必要なくなります。
また、Catalyst::Plugin::ConfigLoaderを使っていてconfigファイルにマルチバイト文字を使用している場合、$c->config->{'bar}で取れる値にはUTF8フラグは立っていないため、TTで表示しようとすると文字化けします。
$c->stash->{'bar'} = $c->config->{bar};
これを回避するにはConfig::Generalベースのconfigの場合は
MyApp
-- snip --
__PACKAGE__->config(
name => 'MyApp' ,
'Plugin::ConfigLoader' => {
driver => {
'General' => { -UTF8 => 1 },
}
},
);
-- snip --
とします。
YAMLベースの場合はYAML::Syck 0.70以上があればYAML::Syck、なければYAMLが使われます。YAML::Syckが入っている場合は
MyApp
-- snip --
$YAML::Syck::ImplicitUnicode = 1;
__PACKAGE__->config( name => 'MyApp' );
-- snip --
とします。
YAML::Syckが入っていなくてYAMLを使う場合は
MyApp
-- snip --
use YAML;
__PACKAGE__->config(
'name' => 'MyApp',
);
__PACKAGE__->setup();
sub finalize_config {
my $c = shift;
my $yaml = YAML::Dump($c->config);
utf8::decode($yaml);
$c->config(YAML::Load($yaml));
$c->next::method(@_);
}
-- snip --
のようにすると文字化けしません。