AcceptLanguage
A lightweight, thread-safe Ruby library for parsing Accept-Language HTTP headers as defined in RFC 2616.
Features
- Thread-safe
- No framework dependencies
- Case-insensitive matching
- BCP 47 language tag support
- Wildcard and exclusion handling
Installation
gem "accept_language"
Usage
AcceptLanguage.parse("en-GB, en;q=0.9").match(:en, :"en-GB")
# => :"en-GB"
Quality values
Quality values (q-values) indicate preference order from 0 to 1:
parser = AcceptLanguage.parse("da, en-GB;q=0.8, en;q=0.7")
parser.match(:en, :da) # => :da
parser.match(:en, :"en-GB") # => :"en-GB"
parser.match(:fr) # => nil
Language variants
A generic language tag matches its regional variants, but not the reverse:
AcceptLanguage.parse("fr").match(:"fr-CH") # => :"fr-CH"
AcceptLanguage.parse("fr-CH").match(:fr) # => nil
Wildcards and exclusions
The wildcard * matches any language. A q-value of 0 explicitly excludes a language:
AcceptLanguage.parse("de-DE, *;q=0.5").match(:fr) # => :fr
AcceptLanguage.parse("*, en;q=0").match(:en) # => nil
AcceptLanguage.parse("*, en;q=0").match(:fr) # => :fr
Case sensitivity
Matching is case-insensitive but preserves the case of the available language tag:
AcceptLanguage.parse("en-GB").match("en-gb") # => "en-gb"
AcceptLanguage.parse("en-gb").match("en-GB") # => "en-GB"
Rails integration
# 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
# HTTP_ACCEPT_LANGUAGE is the standardized key for the Accept-Language header in Rack/Rails
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
Documentation
Versioning
This library follows Semantic Versioning 2.0.0.
License
Available as open source under the terms of the MIT License.