世界上最伟大的投资就是投资自己的教育
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
这个文件里。
具体的代码就不分析了,只要原理懂了,代码层次的东西也是比较简单的。
完结。
本站文章均为原创内容,如需转载请注明出处,谢谢。
© 汕尾市求知科技有限公司 | Rails365 Gitlab | 知乎 | b 站 | csdn
粤公网安备 44152102000088号 | 粤ICP备19038915号
Top