rack-i18n_best_langs 0.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -6,7 +6,12 @@ understanding what are the best languages for a site visitor.
6
6
 
7
7
  If you manage a site that has content many languages and also localized URLs,
8
8
  you will find `rack-i18n_best_langs` very useful, especially when used in
9
- conjunction with `rack-i18n_routes`.
9
+ conjunction with [`rack-i18n_routes`](https://github.com/gioele/rack-i18n_routes).
10
+
11
+ Differently from other similar Rack middleware components,
12
+ `rack-i18n_best_langs` returns a list of languages in order of guessed
13
+ importance, not a single language. Also, it does not require Rails or
14
+ the `i18n` gem.
10
15
 
11
16
 
12
17
  Features
@@ -22,10 +27,40 @@ All these clues are taken into account and evaluated against the list
22
27
  of languages available and their preferred order. It is possible to configure
23
28
  which of these clues is the most important.
24
29
 
25
- An additional clue is available when `AliasMapping` is used as the mapping
26
- function: the language in which the path is written. For
27
- example, `/articles/the-victory` is English, `/artículos/la-victoria`, is
28
- Spanish, `/articles/la-victoire` is French.
30
+ An additional clue is available when `AliasMapping` (part of
31
+ [rack-i18n_routes](http://rubydoc.info/gems/rack-i18n_routes)) is used as the
32
+ mapping function: the language in which the path is written. For example,
33
+ `/articles/the-victory` add preference of English, `/artículos/la-victoria`
34
+ for Spanish and `/articles/la-victoire` for French.
35
+
36
+
37
+ How it works
38
+ ------------
39
+
40
+ Each request is analysed in search for possible clues that can expose what
41
+ are the best languages to use in the content served in response. Each clue
42
+ contributes part of the final score of each language; the score is amplified
43
+ by the weight associated to the clue.
44
+
45
+ For example, if a person requests the URL `/article/vittoria/ita` from
46
+ a browser in a German internet point (thus sending `Accept-Language: de-DE`),
47
+ their request would lead to the following scores:
48
+
49
+ * `ita`: 315 = 0 (not in header) + 15 (partially language of URL) + 300 (path
50
+ ends in `/ita`);
51
+ * `eng`: 15 = 0 (not in header) + 15 (partially language of URL) + 0 (path
52
+ does not end in `/eng`);
53
+ * `ger`: 3 = 3 (in header) + 0 (not language of URL) + 0 (path does not end
54
+ in `/ger`).
55
+
56
+ The downstream application will find the guessed languages in the
57
+ `rack.i18n_best_langs` env variable, in order of importance.
58
+
59
+ env['rack.i18n_best_langs'] # => [ 'ita', 'eng', 'ger' ]
60
+
61
+ At this point, it is up to the application to choose what to do, either change
62
+ the locale using the `i18n` gem, set up its own locale management system or
63
+ just keep track of this information.
29
64
 
30
65
 
31
66
  Examples
@@ -102,6 +137,7 @@ You can change these weight with the `:weights` option.
102
137
 
103
138
  use Rack::I18nBestLangs, FAVORITE_LANGUAGES, :weights => WEIGHTS
104
139
 
140
+ To disable the use of any of the clues, set its weight to zero.
105
141
 
106
142
  ### Using `AliasMapping`
107
143
 
data/Rakefile CHANGED
@@ -16,7 +16,7 @@ Bones {
16
16
  email 'gioele@svario.it'
17
17
  url 'https://github.com/gioele/rack-i18n_best_langs'
18
18
 
19
- version '0.2'
19
+ version '0.3'
20
20
 
21
21
  ignore_file '.gitignore'
22
22
 
@@ -3,13 +3,19 @@
3
3
  # See the `COPYING` file or <http://creativecommons.org/publicdomain/zero/1.0/>
4
4
  # for more details.
5
5
 
6
+ require 'rack'
6
7
 
7
8
  require 'rack/language_tag.rb'
8
9
 
9
- module Rack
10
+ class Rack::I18nBestLangs
11
+ RACK_VARIABLE = 'rack.i18n_best_langs'.freeze
10
12
 
11
- class I18nBestLangs
12
- RACK_VARIABLE = 'rack.i18n_best_langs'
13
+ DEFAULT_WEIGHTS = {
14
+ :header => 1,
15
+ :aliases_path => 2,
16
+ :path => 3,
17
+ :cookie => 4,
18
+ }.freeze
13
19
 
14
20
  # Create a new I18nBestLangs middleware component.
15
21
  #
@@ -31,16 +37,11 @@ class I18nBestLangs
31
37
 
32
38
  score_base = avail_languages.length
33
39
 
34
- weights = opts[:weights] || {}
35
- weight_header = weights[:header] || 1
36
- weight_aliases_path = weights[:aliases_path] || 2
37
- weight_path = weights[:path] || 3
38
- weight_cookie = weights[:cookie] || 4
39
-
40
- @score_for_header = score_base * (10 ** weight_header)
41
- @score_for_aliases_path = score_base * (10 ** weight_aliases_path)
42
- @score_for_path = score_base * (10 ** weight_path)
43
- @score_for_cookie = score_base * (10 ** weight_cookie)
40
+ weights = opts[:weights] || DEFAULT_WEIGHTS
41
+ @score_for_header = score_base * (10 ** weights[:header])
42
+ @score_for_aliases_path = score_base * (10 ** weights[:aliases_path])
43
+ @score_for_path = score_base * (10 ** weights[:path])
44
+ @score_for_cookie = score_base * (10 ** weights[:cookie])
44
45
 
45
46
  @avail_languages = {}
46
47
  avail_languages.each_with_index do |lang, i|
@@ -49,15 +50,17 @@ class I18nBestLangs
49
50
 
50
51
  @avail_languages[code] = score
51
52
  end
53
+ @avail_languages.freeze
52
54
 
53
- @language_path_regex = regex_for_languages_in_path
55
+ @language_path_regex = regex_for_languages_in_path.freeze
54
56
 
55
57
  @path_mapping_fn = opts[:path_mapping_fn]
56
58
  end
57
59
 
58
60
  def call(env)
59
61
  lang_info = find_best_languages(env)
60
- env[I18nBestLangs::RACK_VARIABLE] = lang_info[:languages]
62
+
63
+ env[RACK_VARIABLE] = lang_info[:languages]
61
64
  env['PATH_INFO'] = lang_info[:path_info]
62
65
 
63
66
  return @app.call(env)
@@ -89,16 +92,16 @@ class I18nBestLangs
89
92
  def extract_language_header(env)
90
93
  header = env['HTTP_ACCEPT_LANGUAGE']
91
94
 
92
- if (header =~ HEADER_FORMAT)
93
- return header
94
- else
95
- # FIXME: env.warn
96
- return ""
95
+ if !(header =~ HEADER_FORMAT)
96
+ env["rack.errors"].puts("Warning: malformed Accept-Language header")
97
+ return nil
97
98
  end
99
+
100
+ return header
98
101
  end
99
102
 
100
103
  def extract_language_cookie(env)
101
- return Rack::Request.new(env).cookies[I18nBestLangs::RACK_VARIABLE]
104
+ return Rack::Request.new(env).cookies[RACK_VARIABLE]
102
105
  end
103
106
 
104
107
  def remove_language_from_path(path)
@@ -151,11 +154,11 @@ class I18nBestLangs
151
154
  end
152
155
 
153
156
  def add_score_for_aliases_path(path, langs)
154
- if !@path_mapping_fn.respond_to?(:map_with_langs)
157
+ if !@path_mapping_fn.respond_to?(:path_analysis)
155
158
  return
156
159
  end
157
160
 
158
- ph, aliases_langs = @path_mapping_fn.map_with_langs(path)
161
+ ph, translation, aliases_langs = @path_mapping_fn.path_analysis(path)
159
162
  aliases_langs.map! { |tag| LanguageTag.parse(tag) }
160
163
 
161
164
  lang_uses = aliases_langs.inject(Hash.new(0)) {|freq, lang| freq[lang] += 1; freq }
@@ -207,7 +210,5 @@ class I18nBestLangs
207
210
  return Regexp.new("\\A#{lang}#{qvalue}?(, ?#{lang}#{qvalue}?)*\\Z")
208
211
  end
209
212
 
210
- HEADER_FORMAT = self.accept_language_format
211
- end
212
-
213
+ HEADER_FORMAT = self.accept_language_format.freeze
213
214
  end
@@ -29,7 +29,7 @@ describe Rack::I18nBestLangs do
29
29
  i18n_opts << extra_opts
30
30
  end
31
31
 
32
- session = Rack::Test::Session.new(Rack::MockSession.new(app(*i18n_opts)))
32
+ session = Rack::Test::Session.new(app(*i18n_opts))
33
33
  session.request(path, env_opts)
34
34
 
35
35
  return session.last_request
@@ -128,5 +128,15 @@ describe Rack::I18nBestLangs do
128
128
  languages.first.should == 'fra'
129
129
  end
130
130
  end
131
+
132
+ context "with malformed headers" do
133
+ it "warns of malformed ACCEPT_LANGUAGE" do
134
+ env = request_with('/test', { 'HTTP_ACCEPT_LANGUAGE' => 'fobar/1a' }).env
135
+
136
+ # FIXME: simplify code, https://github.com/brynary/rack-test/issues/61
137
+ errors = env['rack.errors'].instance_variable_get(:@error).instance_variable_get(:@error).string
138
+ errors.should include('malformed Accept-Language')
139
+ end
140
+ end
131
141
  end
132
142
 
metadata CHANGED
@@ -1,12 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-i18n_best_langs
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15
4
+ hash: 13
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 2
9
- version: "0.2"
8
+ - 3
9
+ version: "0.3"
10
10
  platform: ruby
11
11
  authors:
12
12
  - Gioele Barabucci
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2012-08-07 00:00:00 Z
17
+ date: 2012-08-10 00:00:00 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: rack
@@ -56,7 +56,7 @@ dependencies:
56
56
  requirements:
57
57
  - - ">="
58
58
  - !ruby/object:Gem::Version
59
- hash: 959707343
59
+ hash: -701993856
60
60
  segments:
61
61
  - 0
62
62
  - 3