How do you detect the users language (in RoR) as set in the browser? I will have a select box the user can use at anytime, but I\'d like to default to whatever their browser
This is an old question, but I came across it as a soul in search of answers and the only available answer was a link without any context. So here's a bit more depth, based on my subsequent digging.
Query the request
object in the relevant controller:
request.env['HTTP_ACCEPT_LANGUAGE'] #returns nil or a string, e.g.:
# => "en-AU,en-US;q=0.7,en;q=0.3"
That's the easy part though. To make good use of it in Rails, you'll need to parse that string (or maybe it's nil
?).
So, looking at the example string above, some languages have "q-values" - the relative quality factor, between 0 and 1. Higher q-values mean that language is preferred by the client. Lack of a q-value is really the highest q-value - an implicit 1.0
(as with en-AU
in the above string). A slight complication though - the browser may send you languages with q-values that are, say, 0 - and I gather this means you should reject those languages if possible.
Bearing that in mind, here's a couple of different yet similar approaches I've looked at for parsing such a string into a list of languages, ordered by their q-values. With straightforward 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]]
Or if you're proficient at regular expressions, you can achieve the same as above, and probably improve upon my butchered regex at the same time:
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
Either way, you can follow up as needed with something like:
# return array of just languages, ordered by q-value
langs.sort_by(&:last).map(&:first).reverse
# => ["en-AU", "en-US", "en"]
I started out my parsing by looking at this gist, but ended up modifying it fairly significantly. The regex especially was throwing away perfectly good secondary locales, e.g. en-AU
became en
, zh-TW
became zh
. I've attempted to rectify this with my modifications to that regex.