Index: [Article Count Order] [Thread]

Date: Wed, 06 Oct 2004 16:28:27 +0900
From: Satoru Takabayashi <satoru@namazu.org>
Subject: [webrickja:118] Accept-Language: 	の対応
To: webrickja@notwork.org
Message-Id: <m33c0swbj8.wl@namazu.org>
X-Mail-Count: 00118

高林と申します

クライアントから渡される Accept-Language: の値に従って、サー
バが返すファイルを切り替える機能 (Apache の Multiviews に相当)
を実現するために、メール末尾のコードを書きました。

たとえば、foo.html へのアクセスに対して、 Accept-Language:
ja を渡すブラウザに対しては、foo.html.ja を返し、en を渡すブ
ラウザには foo.html.en を返す、といったことができます。

WEBrick::HTTPUtils::mime_type と
WEBRick::HTTPServlet::FileHander::set_filename を置き換える
というやや強引な方法を取っています。

もしよければ、WEBrick 本体の FileHandler で Accept-Language:
に対応していただけると助かります。FileHandler のオプションに
:AcceptLanguage => true を指定するとAccept-Language を見る、
といった感じで指定できると便利だと思います。


module WEBrick::HTTPUtils
  # FIXME: Apache のように AddLanguage した言語だけを認識す
  # る、という実装の方がいいかも
  def mime_type(filename, mime_tab)
    suffix1 = (/\.(\w+)$/ =~ filename && $1.downcase)
    # ex. index.html.en, foo.html.ja
    suffix2 = (/\.(\w+)\.[\w-]+$/ =~ filename && $1.downcase)
    mime_tab[suffix1] || mime_tab[suffix2] || "application/octet-stream"
  end
  module_function :mime_type
end

class MultilingualFileHandler < HTTPServlet::FileHandler
  def get_accept_languages (req)
    if req["Accept-Language"]
      tmp = []
      parts = req["Accept-Language"].split(/,\s*/)
      parts.each {|part|
        if m = /^([\w-]+)(?:;q=([\d]+(?:\.[\d]+)))?$/.match(part)
          lang = m[1]
          q = (m[2] or 1).to_f
          tmp.push([lang, q])
        end
      }
      return tmp.sort_by {|lang, q| q }.map {|lang, q| lang}.reverse
    else
      return []
    end
  end

  def set_filename(req, res)
    original_path_info = req.path_info
    begin
      return super(req, res)
    rescue HTTPStatus::NotFound => e
      accept_languages = get_accept_languages(req)
      # FIXME: Accept-Language が渡されないときの優先順位は
      # Apache の LanguagePriority のように指定できる方がいいかも
      accept_languages = ["en"] if accept_languages.empty?
      accept_languages.each {|lang|
        begin
          req.path_info = original_path_info + "." + lang
          return super(req, res)
        rescue HTTPStatus::NotFound
        end
      }
      req.path_info = original_path_info
      raise e
    end
  end
end