首页ruby
海外散仙厉飞雨 · 真仙

gem 介绍及源码分析之 http_accept_language (四)

海外散仙厉飞雨发布于

1.介绍

之所以要介绍这个http_accept_language,是我在翻看ruby-china源码中无意中看到的,它可以让页面根据浏览器所使用的语言来自动调整网站的语言,比如你的浏览器的语言是en,网站就会自动用en这个locale。把它拿来介绍,也是因为它简单,并且里面涉及到一些有意义的知识,比如中间件(middleware)、HTTP协议等。

2. 安装及使用

添加下面这行到Gemfile文件中:

gem 'http_accept_language'

在使用之前,来看看我的应用的一些关于locale的配置。

# config/application.rb
config.i18n.default_locale = :'zh-CN'

我们试着先在浏览器发送一个请求,然后用pry来调试。

我的浏览器所使用的语言是en。

这是HTTP请求的头部信息,显示的正是en语言。然后用pry来得到这个HTTP的请求头部,一共有两种方法:

[1] pry(#<HomeController>)> request.headers["HTTP_ACCEPT_LANGUAGE"]
"en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4"
[2] pry(#<HomeController>)> env["HTTP_ACCEPT_LANGUAGE"]
"en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4"

http_accept_language正是利用这个en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4信息来处理的,它就是解析这个字符串来得到真正的locale,再去rails中找到所有可用的locale进行匹配,并加载最终的locale。

[3] pry(#<HomeController>)> http_accept_language.compatible_language_from(I18n.available_locales)
:en

果然,输出的就是正确的en

关于具体的使用方法,就得查看官方的readme文档了。

3. 源码解析

在看源码之前,先来看rake middleware指令的输出:

  rails365 git:(master)  rake middleware
...
use HttpAcceptLanguage::Middleware
use ExceptionNotification::Rack
run Rails365::Application.routes

可以发现多了一个middleware。

来看下源码:

# https://github.com/iain/http_accept_language/blob/master/lib/http_accept_language/railtie.rb
module HttpAcceptLanguage
  class Railtie < ::Rails::Railtie
    initializer "http_accept_language.add_middleware" do |app|
      app.middleware.use Middleware

      ActiveSupport.on_load :action_controller do
        include EasyAccess
      end
    end
  end

  module EasyAccess
    def http_accept_language
      @http_accept_language ||= request.env["http_accept_language.parser"] || Parser.new(request.env["HTTP_ACCEPT_LANGUAGE"])
    end
  end
end

Railtie是rails的一个组件,是在启动的时候加载和扩展各种组件,比如active_record, active_view等,rails的源码到处都有它的身影,在这里理解为启动的时候,就能加载组件就可以了。app.middleware.use Middleware表示的是使用了这个Middleware的rack中间件。

先来看下这个Middleware中间件的源码:

# https://github.com/iain/http_accept_language/blob/master/lib/http_accept_language/middleware.rb
module HttpAcceptLanguage
  class Middleware
    def initialize(app)
      @app = app
    end

    def call(env)
      env["http_accept_language.parser"] = Parser.new(env["HTTP_ACCEPT_LANGUAGE"])

      def env.http_accept_language
        self["http_accept_language.parser"]
      end

      @app.call(env)
    end
  end
end

其实整个rails应用就是实现的rack中间件,默认情况下就自动加载了各种中间件的,在这里只不过插件多了一个,中间件就是得到HTTP请求,再把请求的数据,得到再处理,再按照规定的格式响应,比如rack规定了,响应必须包含状态码,头部信息,和响应的内容。

具体的关于rack中间件的内容可以参考下面两个链接:

上面中间件的内容就是把env["http_accept_language.parser"]的信息读取了,之后进行解析,我们来看下是如何解析的。

def compatible_language_from(available_languages)
  user_preferred_languages.map do |preferred| #en-US
    preferred = preferred.downcase
    preferred_language = preferred.split('-', 2).first

    available_languages.find do |available| # en
      available = available.to_s.downcase
      preferred == available || preferred_language == available.split('-', 2).first
    end
  end.compact.first
end

def user_preferred_languages
  @user_preferred_languages ||= begin
    header.to_s.gsub(/\s+/, '').split(',').map do |language|
      locale, quality = language.split(';q=')
      raise ArgumentError, 'Not correctly formatted' unless locale =~ /^[a-z\-0-9]+|\*$/i

      locale  = locale.downcase.gsub(/-[a-z0-9]+$/i, &:upcase) # Uppercase territory
      locale  = nil if locale == '*' # Ignore wildcards

      quality = quality ? quality.to_f : 1.0

      [locale, quality]
    end.sort do |(_, left), (_, right)|
      right <=> left
    end.map(&:first).compact
  rescue ArgumentError # Just rescue anything if the browser messed up badly.
    []
  end
end

所有相关的代码都在https://github.com/iain/http_accept_language/blob/master/lib/http_accept_language/parser.rb这个文件里。

具体的代码就不分析了,只要原理懂了,代码层次的东西也是比较简单的。

完结。

本站帖子均为原创内容,如需转载请注明出处,谢谢。

0 条回复
暂无回复~~
喜欢

© 汕尾市求知科技有限公司 | 粤ICP备19038915号 | 在线学员:92

Top