rack-i18n_locale_switcher 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ = 0.4.0 / 2011-11-26
2
+ Added support configuration of sources from which to determine the locale
3
+ as well as the name of the query parameter.
4
+ Drop support for TLD since locale can't be reliably inferred from it
5
+ Code refactoring and better test coverage.
6
+
1
7
  = 0.3.1 / 2011-11-25
2
8
  Code refactoring
3
9
  Don't "clean" then Rack environment
data/Rakefile CHANGED
@@ -2,12 +2,12 @@
2
2
  require 'rake'
3
3
  require 'echoe'
4
4
 
5
- Echoe.new('rack-i18n_locale_switcher', '0.3.1') do |p|
5
+ Echoe.new('rack-i18n_locale_switcher', '0.4.0') do |p|
6
6
 
7
- p.description = "Detects the current locale from host, top level domain, query parameter or Accept-Language header."
8
- p.url = "http://github.com/christoph-buente/rack-i18n_locale_switcher"
9
- p.author = ["Christoph Bünte", "Andreas Korth"]
10
- p.email = ["info@christophbuente.de", "andreas.korth@gmail.com"]
7
+ p.description = "Detects the current locale from query parameter, path prefix, host or accept header."
8
+ p.url = "http://github.com/christoph-buente/rack-i18n_locale_switcher"
9
+ p.author = ["Christoph Bünte", "Andreas Korth"]
10
+ p.email = ["info@christophbuente.de", "andreas.korth@gmail.com"]
11
11
 
12
12
  p.retain_gemspec = true
13
13
 
@@ -1,57 +1,72 @@
1
1
  require 'i18n'
2
- require 'domainatrix'
2
+ # require 'domainatrix'
3
3
 
4
4
  module Rack
5
5
  class I18nLocaleSwitcher
6
6
 
7
+ DEFAULT_OPTIONS = {
8
+ :param => 'locale',
9
+ :source => [ :param, :path, :host, :header ]
10
+ }.freeze
11
+
7
12
  def initialize(app, options = {})
8
13
  @app = app
14
+
15
+ invalid_options = (options.keys - DEFAULT_OPTIONS.keys)
16
+
17
+ if invalid_options.any?
18
+ raise ArgumentError, "Invalid option(s) #{ invalid_options.map(&:inspect).join(', ') }"
19
+ end
20
+
21
+ @options = DEFAULT_OPTIONS.merge(options)
22
+ @options[:source] = Array(@options[:source]) unless @options[:source].is_a?(Array)
9
23
  end
10
24
 
11
25
  def call(env)
12
- I18n.locale = extract_locale(env)
26
+ I18n.locale = I18n.default_locale
27
+
28
+ @options[:source].each do |source|
29
+ if locale = send(:"get_locale_from_#{source}", env)
30
+ I18n.locale = locale
31
+ break
32
+ end
33
+ end
34
+
13
35
  @app.call(env)
14
36
  end
15
37
 
16
38
  private
17
39
 
18
- def extract_locale(env)
19
- request = Rack::Request.new(env)
20
- uses = [ :param, :path, :subdomain, :tld, :client ]
21
- uses.each do |use|
22
- if locale = send(:"extract_locale_from_#{ use }", request)
23
- unless locale.empty?
24
- locale = locale.to_sym
25
- return locale if I18n.available_locales.include?(locale)
26
- end
27
- end
28
- end
29
- I18n.default_locale
30
- end
31
-
32
- def extract_locale_from_param(request)
33
- request.params["locale"]
40
+ def get_locale_from_param(env)
41
+ env['QUERY_STRING'] =~ /\b#{ @options[:param] }=([^&]+)\b/
42
+ to_available_locale($1)
34
43
  end
35
44
 
36
- def extract_locale_from_path(request)
37
- request.path_info =~ /^\/(\w{2,3})\b/ && $1
45
+ def get_locale_from_path(env)
46
+ env['PATH_INFO'] =~ /^\/([^\/]+)/
47
+ to_available_locale($1)
38
48
  end
39
49
 
40
- def extract_locale_from_tld(request)
41
- Domainatrix.parse(request.url).public_suffix rescue nil
50
+ def get_locale_from_host(env)
51
+ env['SERVER_NAME'] =~ /^([^.]+)\.[^.]+\.[^.]+/i
52
+ to_available_locale($1)
42
53
  end
43
54
 
44
- def extract_locale_from_subdomain(request)
45
- Domainatrix.parse(request.url).subdomain rescue nil
55
+ def get_locale_from_header(env)
56
+ locale = nil
57
+ if accept = env['HTTP_ACCEPT_LANGUAGE']
58
+ locales = accept.scan(/([^;,]+)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i)
59
+ locales.sort_by{ |loc| 1 - (loc.last || 1).to_f }.each do |loc|
60
+ break if locale = to_available_locale(loc.first)
61
+ end
62
+ end
63
+ locale
46
64
  end
47
-
48
- def extract_locale_from_client(request)
49
- if lang = request.env["HTTP_ACCEPT_LANGUAGE"]
50
- lang = lang.split(",").map { |l|
51
- l += ';q=1.0' unless l =~ /;q=\d+\.\d+$/
52
- l.split(';q=')
53
- }.first
54
- lang.first.split("-").first
65
+
66
+ def to_available_locale(locale)
67
+ if locale =~ /^([a-z]{1,8})(-[a-z]{1,8})?$/i
68
+ locale = :"#{ $1.downcase }#{ ($2 || '').upcase }"
69
+ locale if I18n.available_locales.include?(locale)
55
70
  end
56
71
  end
57
72
  end
@@ -2,12 +2,12 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "rack-i18n_locale_switcher"
5
- s.version = "0.3.1"
5
+ s.version = "0.4.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Christoph B\u{fc}nte, Andreas Korth"]
9
- s.date = "2011-11-25"
10
- s.description = "Detects the current locale from host, top level domain, query parameter or Accept-Language header."
9
+ s.date = "2011-11-26"
10
+ s.description = "Detects the current locale from query parameter, path prefix, host or accept header."
11
11
  s.email = ["info@christophbuente.de", "andreas.korth@gmail.com"]
12
12
  s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README.md", "lib/rack/i18n_locale_switcher.rb"]
13
13
  s.files = ["CHANGELOG", "LICENSE", "Manifest", "README.md", "Rakefile", "lib/rack/i18n_locale_switcher.rb", "rack-i18n_locale_switcher.gemspec", "spec/i18n_locale_switcher_spec.rb"]
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
16
16
  s.require_paths = ["lib"]
17
17
  s.rubyforge_project = "rack-i18n_locale_switcher"
18
18
  s.rubygems_version = "1.8.11"
19
- s.summary = "Detects the current locale from host, top level domain, query parameter or Accept-Language header."
19
+ s.summary = "Detects the current locale from query parameter, path prefix, host or accept header."
20
20
 
21
21
  if s.respond_to? :specification_version then
22
22
  s.specification_version = 3
@@ -5,13 +5,19 @@ describe Rack::I18nLocaleSwitcher do
5
5
 
6
6
  include Rack::Test::Methods
7
7
 
8
- def app
9
- Rack::Builder.new {
8
+ let :options do
9
+ {}
10
+ end
11
+
12
+ let :app do
13
+ opts = options
14
+ rack = Rack::Builder.new do
10
15
  map "/" do
11
- use Rack::I18nLocaleSwitcher
16
+ use Rack::I18nLocaleSwitcher, opts
12
17
  run lambda { |env| [200, {}, "Coolness"] }
13
18
  end
14
- }.to_app
19
+ end
20
+ rack.to_app
15
21
  end
16
22
 
17
23
  before do
@@ -19,54 +25,107 @@ describe Rack::I18nLocaleSwitcher do
19
25
  I18n.default_locale = :en
20
26
  end
21
27
 
22
- it "should set the locale to default locale" do
23
- get '/'
28
+ it "should fall back to the locale to default locale" do
29
+ get 'http://example.com/some/path'
30
+ I18n.locale.should eql(I18n.default_locale)
31
+
32
+ get 'http://www.example.com/'
24
33
  I18n.locale.should eql(I18n.default_locale)
25
34
  end
35
+
36
+ it "should not accept invalid options" do
37
+ expect {
38
+ Rack::I18nLocaleSwitcher.new('app', :not_an_option => 'foo')
39
+ }.to raise_error(ArgumentError, "Invalid option(s) :not_an_option")
40
+ end
41
+
42
+ context 'with custom sources' do
43
+
44
+ let :options do
45
+ { :source => [ :header, :host ] }
46
+ end
47
+
48
+ it "should honor the sequence" do
49
+ get 'http://de.example.com' , {}, {'HTTP_ACCEPT_LANGUAGE' => "es"}
50
+ I18n.locale.should eql(:es)
26
51
 
27
- context 'request param' do
52
+ get 'http://de.example.com' , {}, {'HTTP_ACCEPT_LANGUAGE' => "foo"}
53
+ I18n.locale.should eql(:de)
28
54
 
29
- it "should set the I18n locale" do
30
- get '/', :locale => 'de'
31
- last_request.url.should include('?locale=de')
55
+ get 'http://de.example.com'
32
56
  I18n.locale.should eql(:de)
33
57
  end
34
58
 
35
- it "should only allow available locales" do
36
- get '/', :locale => 'xx'
37
- I18n.locale.should eql(I18n.default_locale)
59
+ it "should ignore other sources" do
60
+ get 'http://example.com?locale=de'
61
+ I18n.locale.should eql(:en)
62
+
63
+ get 'http://example.com/de'
64
+ I18n.locale.should eql(:en)
38
65
  end
39
66
  end
40
67
 
41
- context 'from path prefix ' do
68
+ context 'request param' do
42
69
 
43
- it "should set the I18n locale" do
44
- get '/de/'
70
+ it "should set the locale" do
71
+ get 'http://example.com?locale=de'
45
72
  I18n.locale.should eql(:de)
73
+
74
+ get 'http://example.com?locale=en-US'
75
+ I18n.locale.should eql(:'en-US')
76
+
77
+ get 'http://example.com/some/path?foo=bar&locale=es&param=value'
78
+ I18n.locale.should eql(:es)
79
+ end
80
+
81
+ it "should not set an unavailable locale" do
82
+ get 'http://example.com?locale=xx'
83
+ I18n.locale.should eql(I18n.default_locale)
84
+ end
85
+
86
+ context 'name' do
87
+
88
+ let :options do
89
+ { :param => 'lang' }
90
+ end
91
+
92
+ it "should be configurable" do
93
+ get 'http://example.com?lang=de'
94
+ I18n.locale.should eql(:de)
95
+ end
46
96
  end
47
97
  end
48
98
 
49
- context 'from subdomain' do
99
+ context 'from path prefix ' do
50
100
 
51
101
  it "should set the I18n locale" do
52
- get 'http://de.example.com/'
102
+ get 'http://example.com/de/some/path/'
53
103
  I18n.locale.should eql(:de)
104
+
105
+ get 'http://example.com/en-us'
106
+ I18n.locale.should eql(:'en-US')
54
107
  end
55
108
  end
56
109
 
57
- context 'from top level domain' do
110
+ context 'from host' do
58
111
 
59
112
  it "should set the I18n locale" do
60
- get 'http://example.de/'
113
+ get 'http://de.example.com/'
61
114
  I18n.locale.should eql(:de)
115
+
116
+ get 'http://de-de.example.com/'
117
+ I18n.locale.should eql(:'de-DE')
62
118
  end
63
119
  end
64
120
 
65
121
  context 'from accept-language header' do
66
122
 
67
123
  it "should override the client requested locale" do
68
- get '/' , {}, {'HTTP_ACCEPT_LANGUAGE' => "de, de-de,en;q=0.5"}
69
- I18n.locale.should eql(:de)
124
+ get 'http://example.com' , {}, {'HTTP_ACCEPT_LANGUAGE' => "de-de,de,en;q=0.5"}
125
+ I18n.locale.should eql(:'de-DE')
126
+
127
+ get 'http://example.com' , {}, {'HTTP_ACCEPT_LANGUAGE' => "en;q=0.5,en-US;q=0.8,es;q=0.7"}
128
+ I18n.locale.should eql(:'en-US')
70
129
  end
71
130
  end
72
131
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-i18n_locale_switcher
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-11-25 00:00:00.000000000 Z
12
+ date: 2011-11-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
16
- requirement: &2154168640 !ruby/object:Gem::Requirement
16
+ requirement: &2158734100 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2154168640
24
+ version_requirements: *2158734100
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: i18n
27
- requirement: &2154168180 !ruby/object:Gem::Requirement
27
+ requirement: &2158733640 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *2154168180
35
+ version_requirements: *2158733640
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: domainatrix
38
- requirement: &2154167720 !ruby/object:Gem::Requirement
38
+ requirement: &2158733180 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *2154167720
46
+ version_requirements: *2158733180
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: echoe
49
- requirement: &2154167260 !ruby/object:Gem::Requirement
49
+ requirement: &2158732720 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *2154167260
57
+ version_requirements: *2158732720
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: rspec
60
- requirement: &2154166800 !ruby/object:Gem::Requirement
60
+ requirement: &2158732260 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *2154166800
68
+ version_requirements: *2158732260
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rack-test
71
- requirement: &2154166340 !ruby/object:Gem::Requirement
71
+ requirement: &2158731800 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,9 +76,9 @@ dependencies:
76
76
  version: '0'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *2154166340
80
- description: Detects the current locale from host, top level domain, query parameter
81
- or Accept-Language header.
79
+ version_requirements: *2158731800
80
+ description: Detects the current locale from query parameter, path prefix, host or
81
+ accept header.
82
82
  email:
83
83
  - info@christophbuente.de
84
84
  - andreas.korth@gmail.com
@@ -127,6 +127,6 @@ rubyforge_project: rack-i18n_locale_switcher
127
127
  rubygems_version: 1.8.11
128
128
  signing_key:
129
129
  specification_version: 3
130
- summary: Detects the current locale from host, top level domain, query parameter or
131
- Accept-Language header.
130
+ summary: Detects the current locale from query parameter, path prefix, host or accept
131
+ header.
132
132
  test_files: []