rack-i18n_locale_switcher 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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: []