枕を欹てて聴く

香炉峰の雪は簾を撥げて看る

Ruby Curses + 日本語

cursesで日本語表示するときに初期化としてsetlocale(LC_ALL, "");してるのを見たけど, Rubyでどうやったらいいかわかんなかったので.
たぶん止めとけってお告げなんだろうなーと思いつつ.


  1. libncurseswを入れておく.(multibyte 文字列対応)

  2. /ext/curses - Repository - Backport87 - Ruby Issue Tracking System のソースを拾ってくる.

  3. Bug #975: ruby curses extension does not support multibyte characters - ruby-trunk - Ruby Issue Tracking System にあるpatchをextconf.rbにあてる. libncurseswがあるとそっちを使ってくれるようになる.

    ruby extconf.rb
    make

    で, できたcurses.soを使う.


  4. setlocaleが使えない => 無理やり作る.

    #include <locale.h>
    #include "ruby.h"
    
    static VALUE mLocale;
    
    static VALUE
    locale_set_locale_all(obj, str)
      VALUE str;
    {
      return rb_str_new2(setlocale(LC_ALL, StringValuePtr(str)));
    }
    
    void
    Init_locale()
    {
      mLocale = rb_define_module("Locale");
      rb_define_module_function(mLocale, "set_locale_all", locale_set_locale_all, 1);
    }
    

    で, extconf.rb

    require "mkmf"
    create_makefile("locale")
    

    で,

    ruby extconf.rb
    make

    ってやって, できたlocale.soを使う.



でためしに.

require "./curses.so"
require "./locale.so"

include Curses

begin
  ch = ""
  Locale.set_locale_all("")
  init_screen
  cbreak
  noecho
  stdscr.setpos(0, 0);
  stdscr.addstr("入力");
  stdscr.setpos(1, 0);
  loop do
    ch = stdscr.getch
    raise "TEST QUIT" if ch == ?q
    stdscr.setpos(1, 0);
    stdscr.addch(ch);
  end
rescue => e
  exit
ensure
  close_screen
end

f:id:Constellation:20090223004432p:image
わーい. 日本語出力できたよー. 見たいな感じで.

あと

きちんとしたやり方知ってる方は是非教えてほしいです.
ものすごく間違ってるっぽいので, setlocale(LC_ALL, "") がrubyからどうやったらいけるのかも是非.
pythonとかだと,

import locale
locale.setlocale(locale.LC_ALL, '')

ってやってるっぽいんだけどなあ(Google Code調べ)
どっか異変が起きそうなので, 使わなさそう.

追記

rubyのlibraryとかをgrepしてると, time.rbに

# * %a and %b are locale sensitive
# 
#   Since they are locale sensitive, they may be replaced to
#   invalid weekday/month name in some locales.
#   Since ruby-1.6 doesn't invoke setlocale by default,
#   the problem doesn't arise until some external library invokes setlocale.
#   Ruby/GTK is the example of such library.
# 

って記述がある. some localeのときはtime.rbは正確性を保てないよって話.
Ruby/GTKがthe example of such libraryだっていうので, Ruby/GTKのc部分のソースをUbuntu -- Error
あたりから引っ張ってきて, 中身をgrepすると,
rbglib.cあたりに

    setlocale (LC_CTYPE, "");
#if LC_MESSAGES
    setlocale (LC_MESSAGES, "");
#endif

って記述があって, あと,ChangeLog

2004-07-31  Masao Mutoh  <mutoh@highway.ne.jp>

	* src/rbglib.c: setlocale parameter was changed. 
	LC_CTYPE and LC_MESSAGES are set not LC_ALL.

ってなってる.
そこで, locale.cをもうめんどくさいのでそれっぽいのに変えて(正直setlocaleをRubyではこのためくらいしかつかわんだろうってことで)

#include <locale.h>
#include "ruby.h"

static VALUE mLocale;

static VALUE
locale_setlocale(obj)
{
  setlocale(LC_CTYPE, "");
#ifdef LC_MESSAGES
  setlocale(LC_MESSAGES, "");
#endif
  return Qnil;
}

void
Init_locale()
{
  mLocale = rb_define_module("Locale");
  rb_define_module_function(mLocale, "setlocale", locale_setlocale, 0);
}

にして, 上の要領でlocale.soにする.
そして, test.rb(上のtest用ruby script)を修正して,

require "./curses.so"
require "./locale.so"

include Curses

begin
  ch = ""
  Locale.setlocale
  init_screen
  cbreak
  noecho
  stdscr.setpos(0, 0);
  stdscr.addstr("入力");
  stdscr.setpos(1, 0);
  loop do
    ch = stdscr.getch
    raise "TEST QUIT" if ch == ?q
    stdscr.setpos(1, 0);
    stdscr.addch(ch);
  end
rescue => e
  exit
ensure
  close_screen
end

にすると, LC_ALLを変更する大雑把方式じゃなく日本語出力できる.
というか, 個人的にはCursesのInit_cursesにsetlocaleを突っ込みたいけど, 汎用性が著しく落ちそうなのでlocale.soに分けとく.