如何在RoR中侦测用户在浏览器中设置的语言? 我将有一个选择框供用户随时使用,但我希望默认为他们的浏览器语言。
如何在RoR中侦测用户在浏览器中设置的语言? 我将有一个选择框供用户随时使用,但我希望默认为他们的浏览器语言。
在相关控制器中查询 request
对象:
request.env['HTTP_ACCEPT_LANGUAGE'] #returns nil or a string, e.g.:
# => "en-AU,en-US;q=0.7,en;q=0.3"
这只是简单的部分。要在Rails中充分利用它,您需要解析该字符串(或者可能是nil
?)。
因此,看一下上面的示例字符串,有些语言具有“q值” - 相对质量因子,介于0和1之间。较高的q值意味着客户端更喜欢该语言。缺乏q值实际上是最高的q值 - 隐式的1.0
(就像上面的en-AU
一样)。稍微复杂一点 - 浏览器可能会发送具有q值为0的语言,我认为这意味着如果可能,您应该拒绝那些语言。
记住这一点,我看了几种不同但类似的方法,将这样的字符串解析为按其q值排序的语言列表。使用简单的Ruby:
# to_s to deal with possible nil value, since nil.to_s => ""
langs = request.env['HTTP_ACCEPT_LANGUAGE'].to_s.split(",").map do |lang|
l, q = lang.split(";q=")
[l, (q || '1').to_f]
end
# => [["en-AU", 1.0], ["en-US", 0.7], ["en", 0.3]]
如果您擅长使用正则表达式,您可以实现与上述相同的功能,并且可能会改进我的“大屠杀”正则表达式:
rx = /([A-Za-z]{2}(?:-[A-Za-z]{2})?)(?:;q=(1|0?\.[0-9]{1,3}))?/
langs = request.env['HTTP_ACCEPT_LANGUAGE'].to_s.scan(rx).map do |lang, q|
[lang, (q || '1').to_f]
end
无论哪种方式,您都可以根据需要进行后续跟进,比如:
# return array of just languages, ordered by q-value
langs.sort_by(&:last).map(&:first).reverse
# => ["en-AU", "en-US", "en"]
en-AU
变成了en
,zh-TW
变成了zh
。我尝试通过修改该正则表达式来纠正这一问题。2.6 从客户端提供的信息设置区域设置
在特定情况下,从客户端提供的信息设置区域设置是有意义的,即不是从URL中设置。这些信息可以来自用户首选语言(在其浏览器中设置),可以基于从其IP推断出的用户地理位置,或者用户可以通过选择您应用程序界面中的区域设置并将其保存到其个人资料中来提供该信息。这种方法更适用于基于Web的应用程序或服务,而不是网站 - 请参见上面关于会话,cookie和RESTful架构的框...
客户端提供信息的一个来源是Accept-Language HTTP标头。人们可能会在他们的浏览器或其他客户端(例如curl)中设置...
另一种从客户端信息选择区域设置的方法是使用数据库将客户端IP映射到该地区,例如GeoIP Lite Country...
您还可以为应用程序的用户提供设置(并可能覆盖)区域设置的手段,如此等...
这是一个非常老的问题,但我想提一下Rails指南中如何检测用户浏览器语言的描述。
以下代码基于Ruby on Rails指南中的解决方案,并进行了改进:
代码:
def set_locale_based_on_browser
locale = extract_locale_from_accept_language_header
I18n.locale =
if locale_valid?(locale)
locale
else
I18n.default_locale
end
end
private
def locale_valid?(locale)
I18n.available_locales.map(&:to_s).include?(locale)
end
def extract_locale_from_accept_language_header
accept_language = request.env['HTTP_ACCEPT_LANGUAGE']
return unless accept_language
accept_language.scan(/^[a-z]{2}/).first
end
end
关于本地化检测的更多信息,请参阅Ruby on Rails指南: https://guides.rubyonrails.org/i18n.html#choosing-an-implied-locale
I18n.default_locale
而不是 :en
。 - RorrimI18n.default_locale
而不是硬编码语言是个好主意。 - Michał Zalewski昨晚我完成了这个小巧的宝石:accept_language
它可以像下面这样集成到Rails应用程序中:
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :best_locale_from_request!
def best_locale_from_request!
I18n.locale = best_locale_from_request
end
def best_locale_from_request
return I18n.default_locale unless request.headers.key?("HTTP_ACCEPT_LANGUAGE")
string = request.headers.fetch("HTTP_ACCEPT_LANGUAGE")
locale = AcceptLanguage.parse(string).match(*I18n.available_locales)
# If the server cannot serve any matching language,
# it can theoretically send back a 406 (Not Acceptable) error code.
# But, for a better user experience, this is rarely done and more
# common way is to ignore the Accept-Language header in this case.
return I18n.default_locale if locale.nil?
locale
end
end
我希望这可以有所帮助。
这里有一个经过充分测试的 Ruby gem,可以完全满足您的需求:https://github.com/ioquatix/http-accept
languages = HTTP::Accept::Language.parse("da, en-gb;q=0.8, en;q=0.7")
expect(languages[0].locale).to be == "da"
expect(languages[1].locale).to be == "en-gb"
expect(languages[2].locale).to be == "en"
它在各种输入情况下都有100%的测试覆盖率。它按照相关的RFC规范返回语言。
此外,如果您想将用户语言与特定区域设置中可用的内容匹配,可以执行以下操作:
available_localizations = HTTP::Accept::Languages::Locales.new(["en-nz", "en-us"])
# Given the languages that the user wants, and the localizations available, compute the set of desired localizations.
desired_localizations = available_localizations & languages