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 +41 -5
- data/Rakefile +1 -1
- data/lib/rack/i18n_best_langs.rb +27 -26
- data/spec/i18n-best-langs_spec.rb +11 -1
- metadata +5 -5
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`
|
26
|
-
|
27
|
-
|
28
|
-
|
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
data/lib/rack/i18n_best_langs.rb
CHANGED
@@ -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
|
-
|
10
|
+
class Rack::I18nBestLangs
|
11
|
+
RACK_VARIABLE = 'rack.i18n_best_langs'.freeze
|
10
12
|
|
11
|
-
|
12
|
-
|
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
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
-
|
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
|
-
|
94
|
-
|
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[
|
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?(:
|
157
|
+
if !@path_mapping_fn.respond_to?(:path_analysis)
|
155
158
|
return
|
156
159
|
end
|
157
160
|
|
158
|
-
ph, aliases_langs = @path_mapping_fn.
|
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(
|
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:
|
4
|
+
hash: 13
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: "0.
|
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-
|
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:
|
59
|
+
hash: -701993856
|
60
60
|
segments:
|
61
61
|
- 0
|
62
62
|
- 3
|