雑記

2000|01|
2003|05|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|
2007|01|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|09|11|
2009|02|03|05|06|07|08|10|11|12|
2010|01|03|04|05|06|07|08|09|10|
2011|05|06|09|10|
2012|03|07|09|12|
2013|01|02|04|05|07|08|10|11|
2014|04|05|08|10|12|
2015|01|05|
2016|09|

2010-09-26 [長年日記]

[Ruby] SinatraでI18nライブラリを使う

Rubyの軽量フレームワークSinatraI18nライブラリを使う方法のメモです。I18nについては別の実装としてR18nライブラリというのもあるようです。

まずはライブラリのデフォルトに従い、最小のコードで最低限の機能を実現することを目標にします。

ライブラリのインストール

# gem install i18n
# gem install sinatra-i18n
# gem install rack-contrib
i18n
I18nライブラリ
sinatra-i18n
SinatraからI18nライブラリを使うためのライブラリ
rack-contrib
Rackのミドルウェアとユーティリティを集めたライブラリ。今回はRack::Localeを使います

プロジェクトのディレクトリ作成

# mkdir i18n-sample
# cd i18n-sample

ロケールファイルの作成

# mkdir -p config/locales

config/locales/の下にen.ymlというファイルを作る。ファイルはYAMLフォーマットでロケールをトップレベルのキーとしたハッシュの形で記述します。

en:
  message:
    hello: hello!

ja:
  message:
    hello: こんにちは!

このサンプルでは、message.hello というラベルに、:en ロケールでは"hello!"、:ja ロケールでは"こんにちは!"という文字列が対応するようになります。

サンプルコード

i18n-sampleディレクトリにstart.rbを作る。

require 'rubygems'
require 'sinatra'
require 'sinatra/i18n'
require 'rack/contrib'

Sinatra.register Sinatra::I18n
use Rack::Locale

get '/?' do
   t('message.hello')
end

実行

# ruby start.rb
== Sinatra/1.0 has taken the stage on 4567 for development with backup from Mongrel

この状態でブラウザから http://sample-host:4567/ にアクセスすると、ブラウザの言語設定にしたがって"Hello!"または"こんにちは!"と表示されます。

問題点

というわけで、非常に簡単にI18n対応できたのですが、次のような問題もあります。

  • ロケールファイルが1つしか指定できない(のでen.ymlに対応しようとする全てのロケールの設定を書く必要がある)
  • 対応していないロケールに設定されているブラウザからアクセスするとエラーになる

ということで、次のエントリではこの問題に対処します。

参考情報

[Ruby] SinatraでI18nライブラリを使う(続き)

SinatraとI18n、Rack::Localeライブラリを組み合わせると簡単にI18n対応することができますが、以下のような問題がありました。

  • ロケールファイルが1つしか指定できない(のでen.ymlに対応しようとする全てのロケールの設定を書く必要がある)
  • 対応していないロケールに設定されているブラウザからアクセスするとエラーになる

このエントリではこれらの問題の解決を図ります。

ライブラリのコピー

libというディレクトリを作り、その下に変更するライブラリをコピーします。

# mkdir -p lib/sinatra
# mkdir -p lib/rack/contrib

# cp /GEM/LIBRARY/ROOT/sinatra-i18n-0.1.0/lib/sinatra/i18n.rb lib/sinatra/
# cp /GEM/LIBRARY/ROOT/rack-contrib-1.0.1/lib/rack/contrib/locale.rb lib/rack/contrib

このようにディレクトリ階層を再現することにより、元のソースの変更を最小限で済ませることができ、またオリジナルのライブラリで問題が解決した際にはコピーしたファイルを消すだけで元に戻せるようにできます。

ライブラリの修正

コピーしたライブラリファイルを以下のように修正します。

lib/sinatra/i18n.rb

Sinatra の :locales 設定に複数のファイルを指定できるようにします。

% diff -U0 i18n.rb.old i18n.rb
--- i18n.rb.old 2010-09-26 18:40:05.000000000 +0900
+++ i18n.rb     2010-09-26 18:40:26.000000000 +0900
@@ -18 +18 @@
-      ::I18n.backend.load_translations(app.locales)
+      ::I18n.backend.load_translations(*app.locales)
lib/rack/contrib/locale.rb

ブラウザのACCEPT_LANGUAGEを優先順に評価し、アプリ側で有効なロケールが見つかったら設定します。見つからなかった場合はI18n.default_localeに設定します。

% diff -u locale.rb.old locale.rb
--- locale.rb.old       2010-09-26 18:54:43.000000000 +0900
+++ locale.rb   2010-09-26 19:40:04.000000000 +0900
@@ -12,14 +12,14 @@

       # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
       if lang = env["HTTP_ACCEPT_LANGUAGE"]
-        lang = lang.split(",").map { |l|
+        lang.split(",").each { |l|
           l += ';q=1.0' unless l =~ /;q=\d+\.\d+$/
-          l.split(';q=')
-        }.first
-        locale = lang.first.split("-").first
-      else
-        locale = I18n.default_locale
+          locale = l.split(';q=').first.split("-").first.to_sym
+          break if I18n.available_locales.include?(locale)
+          locale = nil
+        }
       end
+      locale ||= I18n.default_locale

       locale = env['rack.locale'] = I18n.locale = locale.to_s
       status, headers, body = @app.call(env)

サンプルコードの修正

サンプルコードを次のように修正します。

$LOAD_PATH.unshift './lib'
require 'rubygems'
require 'sinatra'
require 'sinatra/i18n'
require 'rack/contrib'

set :locales, Dir[ "config/locales/*.yml" ]
I18n.default_locale = :ja

Sinatra.register Sinatra::I18n
use Rack::Locale

get '/?' do
   t('message.hello')
end
1行目
$LOAD_PATHの先頭に./libを追加して、lib以下のライブラリが優先的にロードされるようにします。
7行目
ロケールの設定ファイルとして、config/localesディレクトリ以下の*.ymlを読み込むようにします。
8行目
デフォルトロケールを"ja"にします。

ロケールファイルの分割

config/locales/ の下のen.ymlをja.ymlにコピーし、ロケールの設定を分けます。

en.yml
en:
  message:
    hello: hello!
ja.yml
ja:
  message:
    hello: こんにちは!

実行

# ruby start.rb
== Sinatra/1.0 has taken the stage on 4567 for development with backup from Mongrel
本日のツッコミ(全2件) [ツッコミを入れる]
- aparadekto (2010-10-26 22:13)

Hey, I can't view your site properly within Opera, I actually hope you look into fixing this.

- hs (2010-10-28 19:05)

Hmm...<br>I checked with Opera 10.63 and it looks no problem for me.