http_accept_language-2.1.1/0000755000004100000410000000000013136156333015710 5ustar www-datawww-datahttp_accept_language-2.1.1/Rakefile0000644000004100000410000000045213136156333017356 0ustar www-datawww-datarequire "bundler/gem_tasks" require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) require 'cucumber/rake/task' Cucumber::Rake::Task.new(:cucumber) Cucumber::Rake::Task.new(:wip, "Run features tagged with @wip") do |t| t.profile = "wip" end task :default => [:spec, :cucumber, :wip] http_accept_language-2.1.1/Gemfile0000644000004100000410000000004713136156333017204 0ustar www-datawww-datasource "https://rubygems.org" gemspec http_accept_language-2.1.1/features/0000755000004100000410000000000013136156333017526 5ustar www-datawww-datahttp_accept_language-2.1.1/features/steps/0000755000004100000410000000000013136156333020664 5ustar www-datawww-datahttp_accept_language-2.1.1/features/steps/rails.rb0000644000004100000410000000160013136156333022320 0ustar www-datawww-dataBefore "@rails" do @rails = RailsDriver.new end When /^I generate a new Rails app$/ do @rails.generate_rails end When /^I add http_accept_language to my Gemfile$/ do @rails.append_gemfile end Given /^I have installed http_accept_language$/ do @rails.install_gem end When /^I generate the following controller:$/ do |string| @rails.generate_controller "languages", string end When /^I access that action with the HTTP_ACCEPT_LANGUAGE header "(.*?)"$/ do |header| @rails.with_rails_running do @rails.request_with_http_accept_language_header(header, "/languages") end end Then /^the response should contain "(.*?)"$/ do |output| @rails.output_should_contain(output) end When /^I run `rake middleware`$/ do @rails.bundle_exec("rake middleware") end Then /^the output should contain "(.*?)"$/ do |expected| @rails.assert_partial_output(expected, @rails.all_output) end http_accept_language-2.1.1/features/rails_integration.feature0000644000004100000410000000165313136156333024625 0ustar www-datawww-data@rails Feature: Rails Integration To use http_accept_language inside a Rails application, just add it to your Gemfile and run `bundle install`. It is automatically added to your middleware. Scenario: Installing When I generate a new Rails app And I add http_accept_language to my Gemfile And I run `rake middleware` Then the output should contain "use HttpAcceptLanguage::Middleware" Scenario: Using Given I have installed http_accept_language When I generate the following controller: """ class LanguagesController < ApplicationController def index languages = http_accept_language.user_preferred_languages render :text => "Languages: #{languages.join(' : ')}" end end """ When I access that action with the HTTP_ACCEPT_LANGUAGE header "en-us,en-gb;q=0.8,en;q=0.6,es-419" Then the response should contain "Languages: en-US : es-419 : en-GB : en" http_accept_language-2.1.1/features/support/0000755000004100000410000000000013136156333021242 5ustar www-datawww-datahttp_accept_language-2.1.1/features/support/rails_driver.rb0000644000004100000410000000366313136156333024264 0ustar www-datawww-datarequire 'aruba/api' class RailsDriver include Aruba::Api def initialize @aruba_io_wait_seconds = 10 # @announce_stdout = true # @announce_stderr = true # @announce_cmd = true # @announce_dir = true # @announce_env = true end def app_name "foobar" end def install_gem if app_exists? cd app_name else generate_rails append_gemfile end end def app_exists? in_current_dir do File.exist?("#{app_name}/Gemfile") end end def bundle_exec(cmd) run_simple "bundle exec #{cmd}" end def generate_rails # install rails with as few things as possible, for speed! bundle_exec "rails new #{app_name} --force --skip-git --skip-active-record --skip-sprockets --skip-javascript --skip-test-unit --old-style-hash" cd app_name end def append_gemfile # Specifiy a path so cucumber will use the unreleased version of the gem append_to_file "Gemfile", "gem 'http_accept_language', :path => '#{gem_path}'" end def gem_path File.expand_path('../../../', __FILE__) end def generate_controller(name, content) bundle_exec "rails generate resource #{name} --force" write_file "app/controllers/#{name}_controller.rb", content end def request_with_http_accept_language_header(header, path) run_simple "curl --retry 10 -H 'Accept-language: #{header}' #{File.join(host, path)} -o #{response}" run_simple "cat out.html" end def host "http://localhost:13000" end def with_rails_running start_rails yield ensure stop_rails end def start_rails bundle_exec "rails server -p 13000 -d" end def stop_rails in_current_dir do `cat tmp/pids/server.pid | xargs kill -9` end end def response File.expand_path(File.join(current_dir, 'out.html')) end def output_should_contain(expected) actual = File.open(response, 'r:utf-8').read actual.should include expected end end http_accept_language-2.1.1/.rspec0000644000004100000410000000003213136156333017020 0ustar www-datawww-data--color --format progress http_accept_language-2.1.1/spec/0000755000004100000410000000000013136156333016642 5ustar www-datawww-datahttp_accept_language-2.1.1/spec/parser_spec.rb0000644000004100000410000000417413136156333021503 0ustar www-datawww-datarequire 'http_accept_language/parser' describe HttpAcceptLanguage::Parser do def parser @parser ||= HttpAcceptLanguage::Parser.new('en-us,en-gb;q=0.8,en;q=0.6,es-419') end it "should return empty array" do parser.header = nil expect(parser.user_preferred_languages).to eq [] end it "should properly split" do expect(parser.user_preferred_languages).to eq %w{en-US es-419 en-GB en} end it "should ignore jambled header" do parser.header = 'odkhjf89fioma098jq .,.,' expect(parser.user_preferred_languages).to eq [] end it "should properly respect whitespace" do parser.header = 'en-us, en-gb; q=0.8,en;q = 0.6,es-419' expect(parser.user_preferred_languages).to eq %w{en-US es-419 en-GB en} end it "should find first available language" do expect(parser.preferred_language_from(%w{en en-GB})).to eq "en-GB" end it "should find first compatible language" do expect(parser.compatible_language_from(%w{en-hk})).to eq "en-hk" expect(parser.compatible_language_from(%w{en})).to eq "en" end it "should find first compatible from user preferred" do parser.header = 'en-us,de-de' expect(parser.compatible_language_from(%w{de en})).to eq 'en' end it "should accept symbols as available languages" do parser.header = 'en-us' expect(parser.compatible_language_from([:"en-HK"])).to eq :"en-HK" end it "should accept and ignore wildcards" do parser.header = 'en-US,en,*' expect(parser.compatible_language_from([:"en-US"])).to eq :"en-US" end it "should sanitize available language names" do expect(parser.sanitize_available_locales(%w{en_UK-x3 en-US-x1 ja_JP-x2 pt-BR-x5 es-419-x4})).to eq ["en-UK", "en-US", "ja-JP", "pt-BR", "es-419"] end it "should accept available language names as symbols and return them as strings" do expect(parser.sanitize_available_locales([:en, :"en-US", :ca, :"ca-ES"])).to eq ["en", "en-US", "ca", "ca-ES"] end it "should find most compatible language from user preferred" do parser.header = 'ja,en-gb,en-us,fr-fr' expect(parser.language_region_compatible_from(%w{en-UK en-US ja-JP})).to eq "ja-JP" end end http_accept_language-2.1.1/spec/auto_locale_spec.rb0000644000004100000410000000312213136156333022466 0ustar www-datawww-datarequire 'i18n' require 'http_accept_language/auto_locale' require 'http_accept_language/parser' require 'http_accept_language/middleware' describe HttpAcceptLanguage::AutoLocale do let(:controller_class) do Class.new do def initialize(header = nil) super() @header = header end def self.prepend_before_action(dummy) # dummy method end def self.prepend_before_filter(dummy) # dummy method end def http_accept_language @http_accept_language ||= HttpAcceptLanguage::Parser.new(@header) end include HttpAcceptLanguage::AutoLocale end end let(:controller) { controller_class.new("ja,en-us;q=0.7,en;q=0.3") } context "available languages includes accept_languages" do before do I18n.available_locales = [:en, :ja] end it "take a suitable locale" do controller.send(:set_locale) expect(I18n.locale).to eq(:ja) end end context "available languages do not include accept_languages" do before do I18n.available_locales = [:es] I18n.default_locale = :es end it "set the locale to default" do no_accept_language_controller.send(:set_locale) expect(I18n.locale).to eq(:es) end end let(:no_accept_language_controller) { controller_class.new() } context "default locale is ja" do before do I18n.available_locales = [:en, :ja] I18n.default_locale = :ja end it "set the locale to default" do no_accept_language_controller.send(:set_locale) expect(I18n.locale).to eq(:ja) end end end http_accept_language-2.1.1/spec/middleware_spec.rb0000644000004100000410000000317713136156333022326 0ustar www-datawww-datarequire 'http_accept_language' require 'rack/test' require 'json' class TestRackApp def call(env) request = Rack::Request.new(env) http_accept_language = env.http_accept_language result = { :user_preferred_languages => http_accept_language.user_preferred_languages, } if request.params['preferred'] result[:preferred_language_from] = http_accept_language.preferred_language_from(request.params['preferred']) end [ 200, {}, [ JSON.generate(result) ]] end end describe "Rack integration" do include Rack::Test::Methods def app Rack::Builder.new do use HttpAcceptLanguage::Middleware run TestRackApp.new end.to_app end it "handles reuse of the env instance" do env = { "HTTP_ACCEPT_LANGUAGE" => "en" } app = lambda { |env| env } middleware = HttpAcceptLanguage::Middleware.new(app) middleware.call(env) expect(env.http_accept_language.user_preferred_languages).to eq %w{en} env["HTTP_ACCEPT_LANGUAGE"] = "de" middleware.call(env) expect(env.http_accept_language.user_preferred_languages).to eq %w{de} end it "decodes the HTTP_ACCEPT_LANGUAGE header" do request_with_header 'en-us,en-gb;q=0.8,en;q=0.6,es-419' expect(r['user_preferred_languages']).to eq %w{en-US es-419 en-GB en} end it "finds the first available language" do request_with_header 'en-us,en-gb;q=0.8,en;q=0.6,es-419', :preferred => %w(en en-GB) expect(r['preferred_language_from']).to eq 'en-GB' end def request_with_header(header, params = {}) get "/", params, 'HTTP_ACCEPT_LANGUAGE' => header end def r JSON.parse(last_response.body) end end http_accept_language-2.1.1/.travis.yml0000644000004100000410000000016413136156333020022 0ustar www-datawww-datalanguage: ruby cache: bundler sudo: false rvm: - 2.0.0 - 2.1 - 2.2.5 - 2.3.1 script: bundle exec rspec spec http_accept_language-2.1.1/lib/0000755000004100000410000000000013136156333016456 5ustar www-datawww-datahttp_accept_language-2.1.1/lib/http_accept_language/0000755000004100000410000000000013136156333022617 5ustar www-datawww-datahttp_accept_language-2.1.1/lib/http_accept_language/parser.rb0000644000004100000410000000717213136156333024447 0ustar www-datawww-datamodule HttpAcceptLanguage class Parser attr_accessor :header def initialize(header) @header = header end # Returns a sorted array based on user preference in HTTP_ACCEPT_LANGUAGE. # Browsers send this HTTP header, so don't think this is holy. # # Example: # # request.user_preferred_languages # # => [ 'nl-NL', 'nl-BE', 'nl', 'en-US', 'en' ] # 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 # Sets the user languages preference, overriding the browser # def user_preferred_languages=(languages) @user_preferred_languages = languages end # Finds the locale specifically requested by the browser. # # Example: # # request.preferred_language_from I18n.available_locales # # => 'nl' # def preferred_language_from(array) (user_preferred_languages & array.map(&:to_s)).first end # Returns the first of the user_preferred_languages that is compatible # with the available locales. Ignores region. # # Example: # # request.compatible_language_from I18n.available_locales # 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 # Returns a supplied list of available locals without any extra application info # that may be attached to the locale for storage in the application. # # Example: # [ja_JP-x1, en-US-x4, en_UK-x5, fr-FR-x3] => [ja-JP, en-US, en-UK, fr-FR] # def sanitize_available_locales(available_languages) available_languages.map do |available| available.to_s.split(/[_-]/).reject { |part| part.start_with?("x") }.join("-") end end # Returns the first of the user preferred languages that is # also found in available languages. Finds best fit by matching on # primary language first and secondarily on region. If no matching region is # found, return the first language in the group matching that primary language. # # Example: # # request.language_region_compatible(available_languages) # def language_region_compatible_from(available_languages) available_languages = sanitize_available_locales(available_languages) user_preferred_languages.map do |preferred| #en-US preferred = preferred.downcase preferred_language = preferred.split('-', 2).first lang_group = available_languages.select do |available| # en preferred_language == available.downcase.split('-', 2).first end lang_group.find { |lang| lang.downcase == preferred } || lang_group.first #en-US, en-UK end.compact.first end end end http_accept_language-2.1.1/lib/http_accept_language/version.rb0000644000004100000410000000006213136156333024627 0ustar www-datawww-datamodule HttpAcceptLanguage VERSION = '2.1.1' end http_accept_language-2.1.1/lib/http_accept_language/middleware.rb0000644000004100000410000000052213136156333025260 0ustar www-datawww-datamodule 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 http_accept_language-2.1.1/lib/http_accept_language/auto_locale.rb0000644000004100000410000000070513136156333025435 0ustar www-datawww-datarequire 'active_support/concern' module HttpAcceptLanguage module AutoLocale extend ActiveSupport::Concern included do if respond_to?(:prepend_before_action) prepend_before_action :set_locale else prepend_before_filter :set_locale end end private def set_locale I18n.locale = http_accept_language.compatible_language_from(I18n.available_locales) || I18n.default_locale end end end http_accept_language-2.1.1/lib/http_accept_language/railtie.rb0000644000004100000410000000071013136156333024573 0ustar www-datawww-datamodule 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 http_accept_language-2.1.1/lib/http_accept_language.rb0000644000004100000410000000027613136156333023151 0ustar www-datawww-datarequire 'http_accept_language/auto_locale' require 'http_accept_language/parser' require 'http_accept_language/middleware' require 'http_accept_language/railtie' if defined?(Rails::Railtie) http_accept_language-2.1.1/cucumber.yml0000644000004100000410000000013013136156333020232 0ustar www-datawww-datadefault: --format progress --strict --tags ~@wip wip: --format pretty --wip --tags @wip http_accept_language-2.1.1/.gitignore0000644000004100000410000000006713136156333017703 0ustar www-datawww-data*.gem .bundle Gemfile.lock pkg/* .rvmrc log/*.log tmp/ http_accept_language-2.1.1/http_accept_language.gemspec0000644000004100000410000000244313136156333023421 0ustar www-datawww-data# -*- encoding: utf-8 -*- $:.push File.expand_path("../lib", __FILE__) require "http_accept_language/version" Gem::Specification.new do |s| s.name = "http_accept_language" s.version = HttpAcceptLanguage::VERSION s.authors = ["iain"] s.email = ["iain@iain.nl"] s.homepage = "https://github.com/iain/http_accept_language" s.summary = %q{Find out which locale the user preferes by reading the languages they specified in their browser} s.description = %q{Find out which locale the user preferes by reading the languages they specified in their browser} s.license = "MIT" s.rubyforge_project = "http_accept_language" s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] s.add_development_dependency 'rake' s.add_development_dependency 'rspec' s.add_development_dependency 'rack-test' s.add_development_dependency 'guard-rspec' s.add_development_dependency 'listen', '< 3.1.0' if RUBY_VERSION < '2.2.5' s.add_development_dependency 'rails', ['>= 3.2.6', *('< 5' if RUBY_VERSION < '2.2.2')] s.add_development_dependency 'cucumber' s.add_development_dependency 'aruba' end http_accept_language-2.1.1/README.md0000644000004100000410000000746013136156333017176 0ustar www-datawww-data# HttpAcceptLanguage [![Build Status](https://travis-ci.org/iain/http_accept_language.svg?branch=master)](https://travis-ci.org/iain/http_accept_language) A gem which helps you detect the users preferred language, as sent by the "Accept-Language" HTTP header. The algorithm is based on [RFC 2616](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html), with one exception: when a user requests "en-US" and "en" is an available language, "en" is deemed compatible with "en-US". The RFC specifies that the requested language must either exactly match the available language or must exactly match a prefix of the available language. This means that when the user requests "en" and "en-US" is available, "en-US" would be compatible, but not the other way around. This is usually not what you're looking for. Since version 2.0, this gem is Rack middleware. ## Example The `http_accept_language` method is available in any controller: ```ruby class SomeController < ApplicationController def some_action http_accept_language.user_preferred_languages # => %w(nl-NL nl-BE nl en-US en) available = %w(en en-US nl-BE) http_accept_language.preferred_language_from(available) # => 'nl-BE' http_accept_language.user_preferred_languages # => %w(en-GB) available = %w(en-US) http_accept_language.compatible_language_from(available) # => 'en-US' http_accept_language.user_preferred_languages # => %w(nl-NL nl-BE nl en-US en) available = %w(en nl de) # This could be from I18n.available_locales http_accept_language.preferred_language_from(available) # => 'nl' end end ``` You can easily set the locale used for i18n in a before-filter: ```ruby class SomeController < ApplicationController before_filter :set_locale private def set_locale I18n.locale = http_accept_language.compatible_language_from(I18n.available_locales) end end ``` If you want to enable this behavior by default in your controllers, you can just include the provided concern: ```ruby class ApplicationController < ActionController::Base include HttpAcceptLanguage::AutoLocale #... end ``` Then set available locales in `config/application.rb`: ```ruby config.i18n.available_locales = %w(en nl de fr) ``` To use the middleware in any Rack application, simply add the middleware: ``` ruby require 'http_accept_language' use HttpAcceptLanguage::Middleware run YourAwesomeApp ``` Then you can access it from `env`: ``` ruby class YourAwesomeApp def initialize(app) @app = app end def call(env) available = %w(en en-US nl-BE) language = env.http_accept_language.preferred_language_from(available) [200, {}, ["Oh, you speak #{language}!"]] end end ``` ## Available methods * **user_preferred_languages**: Returns a sorted array based on user preference in HTTP_ACCEPT_LANGUAGE, sanitized and all. * **preferred_language_from(languages)**: Finds the locale specifically requested by the browser * **compatible_language_from(languages)**: Returns the first of the user_preferred_languages that is compatible with the available locales. Ignores region. * **sanitize_available_locales(languages)**: Returns a supplied list of available locals without any extra application info that may be attached to the locale for storage in the application. * **language_region_compatible_from(languages)**: Returns the first of the user preferred languages that is also found in available languages. Finds best fit by matching on primary language first and secondarily on region. If no matching region is found, return the first language in the group matching that primary language. ## Installation ### Without Bundler Install the gem `http_accept_language` ### With Bundler Add the gem to your Gemfile: ``` ruby gem 'http_accept_language' ``` Run `bundle install` to install it. --- Released under the MIT license http_accept_language-2.1.1/Guardfile0000644000004100000410000000027713136156333017543 0ustar www-datawww-data#!/usr/bin/env ruby guard 'rspec', :cli => "-fd", :version => 2 do watch(%r{^spec/.+_spec\.rb$}) watch(%r{^lib/(.+)\.rb$}) { "spec" } watch('spec/spec_helper.rb') { "spec" } end