i18n 0.7.0 → 0.8.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of i18n might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +3 -3
- data/gemfiles/Gemfile.rails-3.2.x +1 -0
- data/gemfiles/Gemfile.rails-3.2.x.lock +6 -1
- data/gemfiles/Gemfile.rails-4.0.x +1 -0
- data/gemfiles/Gemfile.rails-4.0.x.lock +8 -4
- data/gemfiles/Gemfile.rails-4.1.x +1 -0
- data/gemfiles/Gemfile.rails-4.1.x.lock +8 -4
- data/gemfiles/Gemfile.rails-4.2.x +2 -1
- data/gemfiles/Gemfile.rails-4.2.x.lock +11 -7
- data/gemfiles/Gemfile.rails-5.0.x +9 -0
- data/gemfiles/Gemfile.rails-5.0.x.lock +37 -0
- data/gemfiles/Gemfile.rails-master +1 -0
- data/gemfiles/Gemfile.rails-master.lock +11 -8
- data/lib/i18n.rb +5 -3
- data/lib/i18n/backend/base.rb +61 -20
- data/lib/i18n/backend/cache.rb +23 -5
- data/lib/i18n/backend/chain.rb +3 -3
- data/lib/i18n/backend/fallbacks.rb +23 -9
- data/lib/i18n/backend/gettext.rb +25 -16
- data/lib/i18n/backend/metadata.rb +6 -2
- data/lib/i18n/backend/simple.rb +1 -1
- data/lib/i18n/backend/transliterator.rb +10 -4
- data/lib/i18n/config.rb +3 -1
- data/lib/i18n/core_ext/hash.rb +3 -3
- data/lib/i18n/exceptions.rb +3 -3
- data/lib/i18n/gettext/helpers.rb +9 -0
- data/lib/i18n/interpolate/ruby.rb +1 -1
- data/lib/i18n/tests/defaults.rb +8 -0
- data/lib/i18n/tests/interpolation.rb +20 -0
- data/lib/i18n/tests/localization/date.rb +4 -0
- data/lib/i18n/tests/localization/procs.rb +5 -5
- data/lib/i18n/tests/procs.rb +9 -9
- data/lib/i18n/version.rb +1 -1
- data/test/backend/cache_test.rb +23 -1
- data/test/backend/fallbacks_test.rb +46 -0
- data/test/core_ext/hash_test.rb +12 -0
- data/test/gettext/api_test.rb +7 -0
- data/test/i18n/exceptions_test.rb +9 -0
- data/test/i18n/interpolate_test.rb +11 -0
- data/test/i18n_test.rb +4 -0
- data/test/test_data/locales/plurals.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '06029cd6886cf5d01ec5c508ee83ec2e5f3ef8c8'
|
4
|
+
data.tar.gz: 8b359de40823d4a14757157320c3496d671e8900
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1bf61e5f2cd5d4af1f4665eb61fcb67b15313480248697fd15a54a314ab512c0880d785c3b3b8ddcbd8d8d5b6af1b1d8cbe65e797f22fabc8821aa661bb871a1
|
7
|
+
data.tar.gz: '06319f0d7005c3db6cdc12885f9f319cdde28dec8c132f8746ac03cf180df5c85590d3dd34057de1cbd60874c3192c96120dbb15cc0d1dd7e551dc5024947c01'
|
data/README.md
CHANGED
@@ -22,7 +22,7 @@ Pluggable features:
|
|
22
22
|
* Cache
|
23
23
|
* Pluralization: lambda pluralizers stored as translation data
|
24
24
|
* Locale fallbacks, RFC4647 compliant (optionally: RFC4646 locale validation)
|
25
|
-
* Gettext support
|
25
|
+
* [Gettext support](https://github.com/svenfuchs/i18n/wiki/Gettext)
|
26
26
|
* Translation metadata
|
27
27
|
|
28
28
|
Alternative backends:
|
@@ -31,7 +31,7 @@ Alternative backends:
|
|
31
31
|
* ActiveRecord (optionally: ActiveRecord::Missing and ActiveRecord::StoreProcs)
|
32
32
|
* KeyValue (uses active_support/json and cannot store procs)
|
33
33
|
|
34
|
-
For more information and lots of resources see
|
34
|
+
For more information and lots of resources see [the 'Resources' page on the wiki](https://github.com/svenfuchs/i18n/wiki/Resources).
|
35
35
|
|
36
36
|
## Installation
|
37
37
|
|
@@ -71,7 +71,7 @@ follow the usual test setup and should be easy to grok.
|
|
71
71
|
* [Joshua Harvey](http://www.workingwithrails.com/person/759-joshua-harvey)
|
72
72
|
* [Stephan Soller](http://www.arkanis-development.de)
|
73
73
|
* [Saimon Moore](http://saimonmoore.net)
|
74
|
-
* [Matt Aimonetti](
|
74
|
+
* [Matt Aimonetti](https://matt.aimonetti.net/)
|
75
75
|
|
76
76
|
## Contributors
|
77
77
|
|
@@ -10,9 +10,10 @@ GEM
|
|
10
10
|
i18n (~> 0.6, >= 0.6.4)
|
11
11
|
multi_json (~> 1.0)
|
12
12
|
metaclass (0.0.4)
|
13
|
+
minitest (5.9.1)
|
13
14
|
mocha (1.1.0)
|
14
15
|
metaclass (~> 0.0.1)
|
15
|
-
multi_json (1.
|
16
|
+
multi_json (1.11.0)
|
16
17
|
rake (10.4.2)
|
17
18
|
test_declarative (0.0.5)
|
18
19
|
|
@@ -22,6 +23,10 @@ PLATFORMS
|
|
22
23
|
DEPENDENCIES
|
23
24
|
activesupport (~> 3.2.0)
|
24
25
|
i18n!
|
26
|
+
minitest
|
25
27
|
mocha
|
26
28
|
rake
|
27
29
|
test_declarative
|
30
|
+
|
31
|
+
BUNDLED WITH
|
32
|
+
1.13.5
|
@@ -6,7 +6,7 @@ PATH
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
-
activesupport (4.0.
|
9
|
+
activesupport (4.0.13)
|
10
10
|
i18n (~> 0.6, >= 0.6.9)
|
11
11
|
minitest (~> 4.2)
|
12
12
|
multi_json (~> 1.3)
|
@@ -16,11 +16,11 @@ GEM
|
|
16
16
|
minitest (4.7.5)
|
17
17
|
mocha (1.1.0)
|
18
18
|
metaclass (~> 0.0.1)
|
19
|
-
multi_json (1.
|
19
|
+
multi_json (1.11.0)
|
20
20
|
rake (10.4.2)
|
21
21
|
test_declarative (0.0.5)
|
22
|
-
thread_safe (0.3.
|
23
|
-
tzinfo (0.3.
|
22
|
+
thread_safe (0.3.5)
|
23
|
+
tzinfo (0.3.43)
|
24
24
|
|
25
25
|
PLATFORMS
|
26
26
|
ruby
|
@@ -28,6 +28,10 @@ PLATFORMS
|
|
28
28
|
DEPENDENCIES
|
29
29
|
activesupport (~> 4.0.0)
|
30
30
|
i18n!
|
31
|
+
minitest
|
31
32
|
mocha
|
32
33
|
rake
|
33
34
|
test_declarative
|
35
|
+
|
36
|
+
BUNDLED WITH
|
37
|
+
1.13.5
|
@@ -6,20 +6,20 @@ PATH
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
-
activesupport (4.1.
|
9
|
+
activesupport (4.1.10)
|
10
10
|
i18n (~> 0.6, >= 0.6.9)
|
11
11
|
json (~> 1.7, >= 1.7.7)
|
12
12
|
minitest (~> 5.1)
|
13
13
|
thread_safe (~> 0.1)
|
14
14
|
tzinfo (~> 1.1)
|
15
|
-
json (1.8.
|
15
|
+
json (1.8.2)
|
16
16
|
metaclass (0.0.4)
|
17
|
-
minitest (5.5.
|
17
|
+
minitest (5.5.1)
|
18
18
|
mocha (1.1.0)
|
19
19
|
metaclass (~> 0.0.1)
|
20
20
|
rake (10.4.2)
|
21
21
|
test_declarative (0.0.5)
|
22
|
-
thread_safe (0.3.
|
22
|
+
thread_safe (0.3.5)
|
23
23
|
tzinfo (1.2.2)
|
24
24
|
thread_safe (~> 0.1)
|
25
25
|
|
@@ -29,6 +29,10 @@ PLATFORMS
|
|
29
29
|
DEPENDENCIES
|
30
30
|
activesupport (~> 4.1.0)
|
31
31
|
i18n!
|
32
|
+
minitest
|
32
33
|
mocha
|
33
34
|
rake
|
34
35
|
test_declarative
|
36
|
+
|
37
|
+
BUNDLED WITH
|
38
|
+
1.13.5
|
@@ -6,20 +6,20 @@ PATH
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
-
activesupport (4.2.
|
10
|
-
i18n (
|
9
|
+
activesupport (4.2.1)
|
10
|
+
i18n (~> 0.7)
|
11
11
|
json (~> 1.7, >= 1.7.7)
|
12
12
|
minitest (~> 5.1)
|
13
|
-
thread_safe (~> 0.
|
13
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
14
14
|
tzinfo (~> 1.1)
|
15
|
-
json (1.8.
|
15
|
+
json (1.8.2)
|
16
16
|
metaclass (0.0.4)
|
17
|
-
minitest (5.5.
|
17
|
+
minitest (5.5.1)
|
18
18
|
mocha (1.1.0)
|
19
19
|
metaclass (~> 0.0.1)
|
20
20
|
rake (10.4.2)
|
21
21
|
test_declarative (0.0.5)
|
22
|
-
thread_safe (0.3.
|
22
|
+
thread_safe (0.3.5)
|
23
23
|
tzinfo (1.2.2)
|
24
24
|
thread_safe (~> 0.1)
|
25
25
|
|
@@ -27,8 +27,12 @@ PLATFORMS
|
|
27
27
|
ruby
|
28
28
|
|
29
29
|
DEPENDENCIES
|
30
|
-
activesupport (~> 4.2.0
|
30
|
+
activesupport (~> 4.2.0)
|
31
31
|
i18n!
|
32
|
+
minitest
|
32
33
|
mocha
|
33
34
|
rake
|
34
35
|
test_declarative
|
36
|
+
|
37
|
+
BUNDLED WITH
|
38
|
+
1.13.5
|
@@ -0,0 +1,37 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ..
|
3
|
+
specs:
|
4
|
+
i18n (0.7.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
activesupport (5.0.0.1)
|
10
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
11
|
+
i18n (~> 0.7)
|
12
|
+
minitest (~> 5.1)
|
13
|
+
tzinfo (~> 1.1)
|
14
|
+
concurrent-ruby (1.0.2)
|
15
|
+
metaclass (0.0.4)
|
16
|
+
minitest (5.9.1)
|
17
|
+
mocha (1.2.1)
|
18
|
+
metaclass (~> 0.0.1)
|
19
|
+
rake (11.3.0)
|
20
|
+
test_declarative (0.0.5)
|
21
|
+
thread_safe (0.3.5)
|
22
|
+
tzinfo (1.2.2)
|
23
|
+
thread_safe (~> 0.1)
|
24
|
+
|
25
|
+
PLATFORMS
|
26
|
+
ruby
|
27
|
+
|
28
|
+
DEPENDENCIES
|
29
|
+
activesupport (~> 5.0.0)
|
30
|
+
i18n!
|
31
|
+
minitest
|
32
|
+
mocha
|
33
|
+
rake
|
34
|
+
test_declarative
|
35
|
+
|
36
|
+
BUNDLED WITH
|
37
|
+
1.13.6
|
@@ -1,13 +1,12 @@
|
|
1
1
|
GIT
|
2
2
|
remote: git://github.com/rails/rails.git
|
3
|
-
revision:
|
3
|
+
revision: da23e125f8d755917b08f5cca1f7fe1ff38c8b7e
|
4
4
|
branch: master
|
5
5
|
specs:
|
6
|
-
activesupport (5.
|
7
|
-
|
8
|
-
|
6
|
+
activesupport (5.1.0.alpha)
|
7
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
8
|
+
i18n (~> 0.7)
|
9
9
|
minitest (~> 5.1)
|
10
|
-
thread_safe (~> 0.1)
|
11
10
|
tzinfo (~> 1.1)
|
12
11
|
|
13
12
|
PATH
|
@@ -18,14 +17,14 @@ PATH
|
|
18
17
|
GEM
|
19
18
|
remote: https://rubygems.org/
|
20
19
|
specs:
|
21
|
-
|
20
|
+
concurrent-ruby (1.0.2)
|
22
21
|
metaclass (0.0.4)
|
23
|
-
minitest (5.5.
|
22
|
+
minitest (5.5.1)
|
24
23
|
mocha (1.1.0)
|
25
24
|
metaclass (~> 0.0.1)
|
26
25
|
rake (10.4.2)
|
27
26
|
test_declarative (0.0.5)
|
28
|
-
thread_safe (0.3.
|
27
|
+
thread_safe (0.3.5)
|
29
28
|
tzinfo (1.2.2)
|
30
29
|
thread_safe (~> 0.1)
|
31
30
|
|
@@ -35,6 +34,10 @@ PLATFORMS
|
|
35
34
|
DEPENDENCIES
|
36
35
|
activesupport!
|
37
36
|
i18n!
|
37
|
+
minitest
|
38
38
|
mocha
|
39
39
|
rake
|
40
40
|
test_declarative
|
41
|
+
|
42
|
+
BUNDLED WITH
|
43
|
+
1.13.6
|
data/lib/i18n.rb
CHANGED
@@ -9,10 +9,10 @@ module I18n
|
|
9
9
|
autoload :Locale, 'i18n/locale'
|
10
10
|
autoload :Tests, 'i18n/tests'
|
11
11
|
|
12
|
-
RESERVED_KEYS = [:scope, :default, :separator, :resolve, :object, :fallback, :format, :cascade, :throw, :raise]
|
12
|
+
RESERVED_KEYS = [:scope, :default, :separator, :resolve, :object, :fallback, :format, :cascade, :throw, :raise, :deep_interpolation]
|
13
13
|
RESERVED_KEYS_PATTERN = /%\{(#{RESERVED_KEYS.join("|")})\}/
|
14
14
|
|
15
|
-
|
15
|
+
module Base
|
16
16
|
# Gets I18n configuration object.
|
17
17
|
def config
|
18
18
|
Thread.current[:i18n_config] ||= I18n::Config.new
|
@@ -337,5 +337,7 @@ module I18n
|
|
337
337
|
def normalized_key_cache
|
338
338
|
@normalized_key_cache ||= Hash.new { |h,k| h[k] = {} }
|
339
339
|
end
|
340
|
-
|
340
|
+
end
|
341
|
+
|
342
|
+
extend Base
|
341
343
|
end
|
data/lib/i18n/backend/base.rb
CHANGED
@@ -25,20 +25,32 @@ module I18n
|
|
25
25
|
raise InvalidLocale.new(locale) unless locale
|
26
26
|
entry = key && lookup(locale, key, options[:scope], options)
|
27
27
|
|
28
|
-
if options.
|
29
|
-
entry =
|
28
|
+
if entry.nil? && options.key?(:default)
|
29
|
+
entry = default(locale, key, options[:default], options)
|
30
30
|
else
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
entry = resolve(locale, key, entry, options)
|
32
|
+
end
|
33
|
+
|
34
|
+
if entry.nil?
|
35
|
+
if (options.key?(:default) && !options[:default].nil?) || !options.key?(:default)
|
36
|
+
throw(:exception, I18n::MissingTranslation.new(locale, key, options))
|
37
|
+
end
|
35
38
|
end
|
36
39
|
|
37
|
-
throw(:exception, I18n::MissingTranslation.new(locale, key, options)) if entry.nil?
|
38
40
|
entry = entry.dup if entry.is_a?(String)
|
39
41
|
|
42
|
+
count = options[:count]
|
40
43
|
entry = pluralize(locale, entry, count) if count
|
41
|
-
|
44
|
+
|
45
|
+
deep_interpolation = options[:deep_interpolation]
|
46
|
+
values = options.except(*RESERVED_KEYS)
|
47
|
+
if values
|
48
|
+
entry = if deep_interpolation
|
49
|
+
deep_interpolate(locale, entry, values)
|
50
|
+
else
|
51
|
+
interpolate(locale, entry, values)
|
52
|
+
end
|
53
|
+
end
|
42
54
|
entry
|
43
55
|
end
|
44
56
|
|
@@ -50,6 +62,9 @@ module I18n
|
|
50
62
|
# format string. Takes a key from the date/time formats translations as
|
51
63
|
# a format argument (<em>e.g.</em>, <tt>:short</tt> in <tt>:'date.formats'</tt>).
|
52
64
|
def localize(locale, object, format = :default, options = {})
|
65
|
+
if object.nil? && options.include?(:default)
|
66
|
+
return options[:default]
|
67
|
+
end
|
53
68
|
raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime)
|
54
69
|
|
55
70
|
if Symbol === format
|
@@ -59,18 +74,7 @@ module I18n
|
|
59
74
|
format = I18n.t(:"#{type}.formats.#{key}", options)
|
60
75
|
end
|
61
76
|
|
62
|
-
|
63
|
-
format = format.to_s.gsub(/%[aAbBpP]/) do |match|
|
64
|
-
case match
|
65
|
-
when '%a' then I18n.t(:"date.abbr_day_names", :locale => locale, :format => format)[object.wday]
|
66
|
-
when '%A' then I18n.t(:"date.day_names", :locale => locale, :format => format)[object.wday]
|
67
|
-
when '%b' then I18n.t(:"date.abbr_month_names", :locale => locale, :format => format)[object.mon]
|
68
|
-
when '%B' then I18n.t(:"date.month_names", :locale => locale, :format => format)[object.mon]
|
69
|
-
when '%p' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format).upcase if object.respond_to? :hour
|
70
|
-
when '%P' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format).downcase if object.respond_to? :hour
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
77
|
+
format = translate_localization_format(locale, object, format, options)
|
74
78
|
object.strftime(format)
|
75
79
|
end
|
76
80
|
|
@@ -155,6 +159,30 @@ module I18n
|
|
155
159
|
end
|
156
160
|
end
|
157
161
|
|
162
|
+
# Deep interpolation
|
163
|
+
#
|
164
|
+
# deep_interpolate { people: { ann: "Ann is %{ann}", john: "John is %{john}" } },
|
165
|
+
# ann: 'good', john: 'big'
|
166
|
+
# #=> { people: { ann: "Ann is good", john: "John is big" } }
|
167
|
+
def deep_interpolate(locale, data, values = {})
|
168
|
+
return data if values.empty?
|
169
|
+
|
170
|
+
case data
|
171
|
+
when ::String
|
172
|
+
I18n.interpolate(data, values)
|
173
|
+
when ::Hash
|
174
|
+
data.each_with_object({}) do |(k, v), result|
|
175
|
+
result[k] = deep_interpolate(locale, v, values)
|
176
|
+
end
|
177
|
+
when ::Array
|
178
|
+
data.map do |v|
|
179
|
+
deep_interpolate(locale, v, values)
|
180
|
+
end
|
181
|
+
else
|
182
|
+
data
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
158
186
|
# Loads a single translations file by delegating to #load_rb or
|
159
187
|
# #load_yml depending on the file extension and directly merges the
|
160
188
|
# data to the existing translations. Raises I18n::UnknownFileType
|
@@ -184,6 +212,19 @@ module I18n
|
|
184
212
|
raise InvalidLocaleData.new(filename, e.inspect)
|
185
213
|
end
|
186
214
|
end
|
215
|
+
|
216
|
+
def translate_localization_format(locale, object, format, options)
|
217
|
+
format.to_s.gsub(/%[aAbBpP]/) do |match|
|
218
|
+
case match
|
219
|
+
when '%a' then I18n.t(:"date.abbr_day_names", :locale => locale, :format => format)[object.wday]
|
220
|
+
when '%A' then I18n.t(:"date.day_names", :locale => locale, :format => format)[object.wday]
|
221
|
+
when '%b' then I18n.t(:"date.abbr_month_names", :locale => locale, :format => format)[object.mon]
|
222
|
+
when '%B' then I18n.t(:"date.month_names", :locale => locale, :format => format)[object.mon]
|
223
|
+
when '%p' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format).upcase if object.respond_to? :hour
|
224
|
+
when '%P' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format).downcase if object.respond_to? :hour
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
187
228
|
end
|
188
229
|
end
|
189
230
|
end
|
data/lib/i18n/backend/cache.rb
CHANGED
@@ -13,9 +13,13 @@
|
|
13
13
|
# You can use any cache implementation you want that provides the same API as
|
14
14
|
# ActiveSupport::Cache (only the methods #fetch and #write are being used).
|
15
15
|
#
|
16
|
-
# The cache_key implementation assumes
|
17
|
-
#
|
18
|
-
# http://www.ruby-doc.org/core/classes/Object.html#M000337).
|
16
|
+
# The cache_key implementation by default assumes you pass values that return
|
17
|
+
# a valid key from #hash (see
|
18
|
+
# http://www.ruby-doc.org/core/classes/Object.html#M000337). However, you can
|
19
|
+
# configure your own digest method via which responds to #hexdigest (see
|
20
|
+
# http://ruby-doc.org/stdlib/libdoc/digest/rdoc/index.html):
|
21
|
+
#
|
22
|
+
# I18n.cache_key_digest = Digest::MD5.new
|
19
23
|
#
|
20
24
|
# If you use a lambda as a default value in your translation like this:
|
21
25
|
#
|
@@ -37,6 +41,7 @@ module I18n
|
|
37
41
|
class << self
|
38
42
|
@@cache_store = nil
|
39
43
|
@@cache_namespace = nil
|
44
|
+
@@cache_key_digest = nil
|
40
45
|
|
41
46
|
def cache_store
|
42
47
|
@@cache_store
|
@@ -54,6 +59,14 @@ module I18n
|
|
54
59
|
@@cache_namespace = namespace
|
55
60
|
end
|
56
61
|
|
62
|
+
def cache_key_digest
|
63
|
+
@@cache_key_digest
|
64
|
+
end
|
65
|
+
|
66
|
+
def cache_key_digest=(key_digest)
|
67
|
+
@@cache_key_digest = key_digest
|
68
|
+
end
|
69
|
+
|
57
70
|
def perform_caching?
|
58
71
|
!cache_store.nil?
|
59
72
|
end
|
@@ -76,7 +89,8 @@ module I18n
|
|
76
89
|
end
|
77
90
|
|
78
91
|
def _fetch(cache_key, &block)
|
79
|
-
result = I18n.cache_store.read(cache_key)
|
92
|
+
result = I18n.cache_store.read(cache_key)
|
93
|
+
return result unless result.nil?
|
80
94
|
result = catch(:exception, &block)
|
81
95
|
I18n.cache_store.write(cache_key, result) unless result.is_a?(Proc)
|
82
96
|
result
|
@@ -84,13 +98,17 @@ module I18n
|
|
84
98
|
|
85
99
|
def cache_key(locale, key, options)
|
86
100
|
# This assumes that only simple, native Ruby values are passed to I18n.translate.
|
87
|
-
"i18n/#{I18n.cache_namespace}/#{locale}/#{key
|
101
|
+
"i18n/#{I18n.cache_namespace}/#{locale}/#{digest_item(key)}/#{USE_INSPECT_HASH ? digest_item(options.inspect) : digest_item(options)}"
|
88
102
|
end
|
89
103
|
|
90
104
|
private
|
91
105
|
# In Ruby < 1.9 the following is true: { :foo => 1, :bar => 2 }.hash == { :foo => 2, :bar => 1 }.hash
|
92
106
|
# Therefore we must use the hash of the inspect string instead to avoid cache key colisions.
|
93
107
|
USE_INSPECT_HASH = RUBY_VERSION <= "1.9"
|
108
|
+
|
109
|
+
def digest_item(key)
|
110
|
+
I18n.cache_key_digest ? I18n.cache_key_digest.hexdigest(key.to_s) : key.hash
|
111
|
+
end
|
94
112
|
end
|
95
113
|
end
|
96
114
|
end
|
data/lib/i18n/backend/chain.rb
CHANGED
@@ -17,7 +17,7 @@ module I18n
|
|
17
17
|
class Chain
|
18
18
|
module Implementation
|
19
19
|
include Base
|
20
|
-
|
20
|
+
|
21
21
|
attr_accessor :backends
|
22
22
|
|
23
23
|
def initialize(*backends)
|
@@ -46,7 +46,7 @@ module I18n
|
|
46
46
|
translation = backend.translate(locale, key, options)
|
47
47
|
if namespace_lookup?(translation, options)
|
48
48
|
namespace = _deep_merge(translation, namespace || {})
|
49
|
-
elsif !translation.nil?
|
49
|
+
elsif !translation.nil? || (options.key?(:default) && options[:default].nil?)
|
50
50
|
return translation
|
51
51
|
end
|
52
52
|
end
|
@@ -75,7 +75,7 @@ module I18n
|
|
75
75
|
def namespace_lookup?(result, options)
|
76
76
|
result.is_a?(Hash) && !options.has_key?(:count)
|
77
77
|
end
|
78
|
-
|
78
|
+
|
79
79
|
private
|
80
80
|
# This is approximately what gets used in ActiveSupport.
|
81
81
|
# However since we are not guaranteed to run in an ActiveSupport context
|
@@ -38,18 +38,21 @@ module I18n
|
|
38
38
|
return super if options[:fallback]
|
39
39
|
default = extract_non_symbol_default!(options) if options[:default]
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
41
|
+
begin
|
42
|
+
options[:fallback] = true
|
43
|
+
I18n.fallbacks[locale].each do |fallback|
|
44
|
+
begin
|
45
|
+
catch(:exception) do
|
46
|
+
result = super(fallback, key, options)
|
47
|
+
return result if (result.nil? && options.key?(:default) && options[:default].nil?) || !result.nil?
|
48
|
+
end
|
49
|
+
rescue I18n::InvalidLocale
|
50
|
+
# we do nothing when the locale is invalid, as this is a fallback anyways.
|
47
51
|
end
|
48
|
-
rescue I18n::InvalidLocale
|
49
|
-
# we do nothing when the locale is invalid, as this is a fallback anyways.
|
50
52
|
end
|
53
|
+
ensure
|
54
|
+
options.delete(:fallback)
|
51
55
|
end
|
52
|
-
options.delete(:fallback)
|
53
56
|
|
54
57
|
return super(locale, nil, options.merge(:default => default)) if default
|
55
58
|
throw(:exception, I18n::MissingTranslation.new(locale, key, options))
|
@@ -64,6 +67,17 @@ module I18n
|
|
64
67
|
return first_non_symbol_default
|
65
68
|
end
|
66
69
|
|
70
|
+
def exists?(locale, key)
|
71
|
+
I18n.fallbacks[locale].each do |fallback|
|
72
|
+
begin
|
73
|
+
return true if super(fallback, key)
|
74
|
+
rescue I18n::InvalidLocale
|
75
|
+
# we do nothing when the locale is invalid, as this is a fallback anyways.
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
false
|
80
|
+
end
|
67
81
|
end
|
68
82
|
end
|
69
83
|
end
|
data/lib/i18n/backend/gettext.rb
CHANGED
@@ -1,24 +1,33 @@
|
|
1
1
|
require 'i18n/gettext'
|
2
2
|
require 'i18n/gettext/po_parser'
|
3
3
|
|
4
|
-
# Experimental support for using Gettext po files to store translations.
|
5
|
-
#
|
6
|
-
# To use this you can simply include the module to the Simple backend - or
|
7
|
-
# whatever other backend you are using.
|
8
|
-
#
|
9
|
-
# I18n::Backend::Simple.include(I18n::Backend::Gettext)
|
10
|
-
#
|
11
|
-
# Now you should be able to include your Gettext translation (*.po) files to
|
12
|
-
# the I18n.load_path so they're loaded to the backend and you can use them as
|
13
|
-
# usual:
|
14
|
-
#
|
15
|
-
# I18n.load_path += Dir["path/to/locales/*.po"]
|
16
|
-
#
|
17
|
-
# Following the Gettext convention this implementation expects that your
|
18
|
-
# translation files are named by their locales. E.g. the file en.po would
|
19
|
-
# contain the translations for the English locale.
|
20
4
|
module I18n
|
21
5
|
module Backend
|
6
|
+
# Experimental support for using Gettext po files to store translations.
|
7
|
+
#
|
8
|
+
# To use this you can simply include the module to the Simple backend - or
|
9
|
+
# whatever other backend you are using.
|
10
|
+
#
|
11
|
+
# I18n::Backend::Simple.include(I18n::Backend::Gettext)
|
12
|
+
#
|
13
|
+
# Now you should be able to include your Gettext translation (*.po) files to
|
14
|
+
# the +I18n.load_path+ so they're loaded to the backend and you can use them as
|
15
|
+
# usual:
|
16
|
+
#
|
17
|
+
# I18n.load_path += Dir["path/to/locales/*.po"]
|
18
|
+
#
|
19
|
+
# Following the Gettext convention this implementation expects that your
|
20
|
+
# translation files are named by their locales. E.g. the file en.po would
|
21
|
+
# contain the translations for the English locale.
|
22
|
+
#
|
23
|
+
# To translate text <b>you must use</b> one of the translate methods provided by
|
24
|
+
# I18n::Gettext::Helpers.
|
25
|
+
#
|
26
|
+
# include I18n::Gettext::Helpers
|
27
|
+
# puts _("some string")
|
28
|
+
#
|
29
|
+
# Without it strings containing periods (".") will not be translated.
|
30
|
+
|
22
31
|
module Gettext
|
23
32
|
class PoData < Hash
|
24
33
|
def set_comment(msgid_or_sym, comment)
|
@@ -21,11 +21,15 @@ module I18n
|
|
21
21
|
def included(base)
|
22
22
|
Object.class_eval do
|
23
23
|
def translation_metadata
|
24
|
-
|
24
|
+
unless self.frozen?
|
25
|
+
@translation_metadata ||= {}
|
26
|
+
else
|
27
|
+
{}
|
28
|
+
end
|
25
29
|
end
|
26
30
|
|
27
31
|
def translation_metadata=(translation_metadata)
|
28
|
-
@translation_metadata = translation_metadata
|
32
|
+
@translation_metadata = translation_metadata unless self.frozen?
|
29
33
|
end
|
30
34
|
end unless Object.method_defined?(:translation_metadata)
|
31
35
|
end
|
data/lib/i18n/backend/simple.rb
CHANGED
@@ -63,7 +63,7 @@ module I18n
|
|
63
63
|
end
|
64
64
|
|
65
65
|
# Looks up a translation from the translations hash. Returns nil if
|
66
|
-
#
|
66
|
+
# either key is nil, or locale, scope or key do not exist as a key in the
|
67
67
|
# nested translations hash. Splits keys or scopes containing dots
|
68
68
|
# into multiple keys, i.e. <tt>currency.format</tt> is regarded the same as
|
69
69
|
# <tt>%w(currency format)</tt>.
|
@@ -71,13 +71,13 @@ module I18n
|
|
71
71
|
|
72
72
|
def initialize(rule = nil)
|
73
73
|
@rule = rule
|
74
|
-
|
74
|
+
add_default_approximations
|
75
75
|
add rule if rule
|
76
76
|
end
|
77
77
|
|
78
|
-
def transliterate(string, replacement =
|
78
|
+
def transliterate(string, replacement = DEFAULT_REPLACEMENT_CHAR)
|
79
79
|
string.gsub(/[^\x00-\x7f]/u) do |char|
|
80
|
-
approximations[char] || replacement
|
80
|
+
approximations[char] || replacement
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
@@ -87,6 +87,12 @@ module I18n
|
|
87
87
|
@approximations ||= {}
|
88
88
|
end
|
89
89
|
|
90
|
+
def add_default_approximations
|
91
|
+
DEFAULT_APPROXIMATIONS.each do |key, value|
|
92
|
+
approximations[key] = value
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
90
96
|
# Add transliteration rules to the approximations hash.
|
91
97
|
def add(hash)
|
92
98
|
hash.each do |key, value|
|
@@ -96,4 +102,4 @@ module I18n
|
|
96
102
|
end
|
97
103
|
end
|
98
104
|
end
|
99
|
-
end
|
105
|
+
end
|
data/lib/i18n/config.rb
CHANGED
@@ -5,7 +5,7 @@ module I18n
|
|
5
5
|
# The only configuration value that is not global and scoped to thread is :locale.
|
6
6
|
# It defaults to the default_locale.
|
7
7
|
def locale
|
8
|
-
@locale
|
8
|
+
defined?(@locale) && @locale ? @locale : default_locale
|
9
9
|
end
|
10
10
|
|
11
11
|
# Sets the current locale pseudo-globally, i.e. in the Thread.current hash.
|
@@ -124,6 +124,8 @@ module I18n
|
|
124
124
|
# behave like a Ruby Array.
|
125
125
|
def load_path=(load_path)
|
126
126
|
@@load_path = load_path
|
127
|
+
@@available_locales_set = nil
|
128
|
+
backend.reload!
|
127
129
|
end
|
128
130
|
|
129
131
|
# Whether or not to verify if locales are in the list of available locales.
|
data/lib/i18n/core_ext/hash.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Hash
|
2
2
|
def slice(*keep_keys)
|
3
|
-
h =
|
4
|
-
keep_keys.each { |key| h[key] = fetch(key) }
|
3
|
+
h = self.class.new
|
4
|
+
keep_keys.each { |key| h[key] = fetch(key) if has_key?(key) }
|
5
5
|
h
|
6
6
|
end unless Hash.method_defined?(:slice)
|
7
7
|
|
@@ -21,7 +21,7 @@ class Hash
|
|
21
21
|
MERGER = proc do |key, v1, v2|
|
22
22
|
Hash === v1 && Hash === v2 ? v1.merge(v2, &MERGER) : v2
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
def deep_merge!(data)
|
26
26
|
merge!(data, &MERGER)
|
27
27
|
end unless Hash.method_defined?(:deep_merge!)
|
data/lib/i18n/exceptions.rb
CHANGED
@@ -38,12 +38,12 @@ module I18n
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
class MissingTranslation
|
41
|
+
class MissingTranslation < ArgumentError
|
42
42
|
module Base
|
43
43
|
attr_reader :locale, :key, :options
|
44
44
|
|
45
|
-
def initialize(locale, key, options =
|
46
|
-
@key, @locale, @options = key, locale, options.dup
|
45
|
+
def initialize(locale, key, options = {})
|
46
|
+
@key, @locale, @options = key, locale, options.dup
|
47
47
|
options.each { |k, v| self.options[k] = v.inspect if v.is_a?(Proc) }
|
48
48
|
end
|
49
49
|
|
data/lib/i18n/gettext/helpers.rb
CHANGED
@@ -7,6 +7,15 @@ module I18n
|
|
7
7
|
#
|
8
8
|
# include I18n::Gettext::Helpers
|
9
9
|
module Helpers
|
10
|
+
# Makes dynamic translation messages readable for the gettext parser.
|
11
|
+
# <tt>_(fruit)</tt> cannot be understood by the gettext parser. To help the parser find all your translations,
|
12
|
+
# you can add <tt>fruit = N_("Apple")</tt> which does not translate, but tells the parser: "Apple" needs translation.
|
13
|
+
# * msgid: the message id.
|
14
|
+
# * Returns: msgid.
|
15
|
+
def N_(msgsid)
|
16
|
+
msgsid
|
17
|
+
end
|
18
|
+
|
10
19
|
def gettext(msgid, options = {})
|
11
20
|
I18n.t(msgid, { :default => msgid, :separator => '|' }.merge(options))
|
12
21
|
end
|
data/lib/i18n/tests/defaults.rb
CHANGED
@@ -24,6 +24,14 @@ module I18n
|
|
24
24
|
assert_equal 'bar', I18n.t(:does_not_exist, :default => [:does_not_exist_2, :'foo.bar'])
|
25
25
|
end
|
26
26
|
|
27
|
+
test "defaults: given false it returns false" do
|
28
|
+
assert_equal false, I18n.t(:does_not_exist, :default => false)
|
29
|
+
end
|
30
|
+
|
31
|
+
test "defaults: given nil it returns nil" do
|
32
|
+
assert_equal nil, I18n.t(:does_not_exist, :default => nil)
|
33
|
+
end
|
34
|
+
|
27
35
|
test "defaults: given an array of missing keys it raises a MissingTranslationData exception" do
|
28
36
|
assert_raise I18n::MissingTranslationData do
|
29
37
|
I18n.t(:does_not_exist, :default => [:does_not_exist_2, :does_not_exist_3], :raise => true)
|
@@ -104,6 +104,26 @@ module I18n
|
|
104
104
|
assert_raise(I18n::ReservedInterpolationKey) { interpolate(:default => '%{separator}', :foo => :bar) }
|
105
105
|
end
|
106
106
|
|
107
|
+
test "interpolation: deep interpolation for default string" do
|
108
|
+
assert_equal 'Hi %{name}!', interpolate(:default => 'Hi %{name}!', :deep_interpolation => true)
|
109
|
+
end
|
110
|
+
|
111
|
+
test "interpolation: deep interpolation for interpolated string" do
|
112
|
+
assert_equal 'Hi Ann!', interpolate(:default => 'Hi %{name}!', :name => 'Ann', :deep_interpolation => true)
|
113
|
+
end
|
114
|
+
|
115
|
+
test "interpolation: deep interpolation for Hash" do
|
116
|
+
people = { :people => { :ann => 'Ann is %{ann}', :john => 'John is %{john}' } }
|
117
|
+
interpolated_people = { :people => { :ann => 'Ann is good', :john => 'John is big' } }
|
118
|
+
assert_equal interpolated_people, interpolate(:default => people, :ann => 'good', :john => 'big', :deep_interpolation => true)
|
119
|
+
end
|
120
|
+
|
121
|
+
test "interpolation: deep interpolation for Array" do
|
122
|
+
people = { :people => ['Ann is %{ann}', 'John is %{john}'] }
|
123
|
+
interpolated_people = { :people => ['Ann is good', 'John is big'] }
|
124
|
+
assert_equal interpolated_people, interpolate(:default => people, :ann => 'good', :john => 'big', :deep_interpolation => true)
|
125
|
+
end
|
126
|
+
|
107
127
|
protected
|
108
128
|
|
109
129
|
def capture(stream)
|
@@ -51,6 +51,10 @@ module I18n
|
|
51
51
|
assert_nothing_raised { I18n.l(@date, options.freeze) }
|
52
52
|
end
|
53
53
|
|
54
|
+
test "localize Date: given nil with default value it returns default" do
|
55
|
+
assert_equal 'default', I18n.l(nil, :default => 'default')
|
56
|
+
end
|
57
|
+
|
54
58
|
test "localize Date: given nil it raises I18n::ArgumentError" do
|
55
59
|
assert_raise(I18n::ArgumentError) { I18n.l(nil) }
|
56
60
|
end
|
@@ -52,19 +52,19 @@ module I18n
|
|
52
52
|
test "localize Time: given a format that resolves to a Proc it calls the Proc with the object" do
|
53
53
|
setup_time_proc_translations
|
54
54
|
time = ::Time.utc(2008, 3, 1, 6, 0)
|
55
|
-
assert_equal inspect_args([time, {}]), I18n.l(time, :format => :proc, :locale => :ru)
|
55
|
+
assert_equal I18n::Tests::Localization::Procs.inspect_args([time, {}]), I18n.l(time, :format => :proc, :locale => :ru)
|
56
56
|
end
|
57
57
|
|
58
58
|
test "localize Time: given a format that resolves to a Proc it calls the Proc with the object and extra options" do
|
59
59
|
setup_time_proc_translations
|
60
60
|
time = ::Time.utc(2008, 3, 1, 6, 0)
|
61
61
|
options = { :foo => 'foo' }
|
62
|
-
assert_equal inspect_args([time, options]), I18n.l(time, options.merge(:format => :proc, :locale => :ru))
|
62
|
+
assert_equal I18n::Tests::Localization::Procs.inspect_args([time, options]), I18n.l(time, options.merge(:format => :proc, :locale => :ru))
|
63
63
|
end
|
64
64
|
|
65
65
|
protected
|
66
66
|
|
67
|
-
def inspect_args(args)
|
67
|
+
def self.inspect_args(args)
|
68
68
|
args = args.map do |arg|
|
69
69
|
case arg
|
70
70
|
when ::Time, ::DateTime
|
@@ -85,12 +85,12 @@ module I18n
|
|
85
85
|
I18n.backend.store_translations :ru, {
|
86
86
|
:time => {
|
87
87
|
:formats => {
|
88
|
-
:proc => lambda { |*args| inspect_args(args) }
|
88
|
+
:proc => lambda { |*args| I18n::Tests::Localization::Procs.inspect_args(args) }
|
89
89
|
}
|
90
90
|
},
|
91
91
|
:date => {
|
92
92
|
:formats => {
|
93
|
-
:proc => lambda { |*args| inspect_args(args) }
|
93
|
+
:proc => lambda { |*args| I18n::Tests::Localization::Procs.inspect_args(args) }
|
94
94
|
},
|
95
95
|
:'day_names' => lambda { |key, options|
|
96
96
|
(options[:format] =~ /^%A/) ?
|
data/lib/i18n/tests/procs.rb
CHANGED
@@ -4,28 +4,29 @@ module I18n
|
|
4
4
|
module Tests
|
5
5
|
module Procs
|
6
6
|
test "lookup: given a translation is a proc it calls the proc with the key and interpolation values" do
|
7
|
-
I18n.backend.store_translations(:en, :a_lambda => lambda { |*args| filter_args(*args) })
|
7
|
+
I18n.backend.store_translations(:en, :a_lambda => lambda { |*args| I18n::Tests::Procs.filter_args(*args) })
|
8
8
|
assert_equal '[:a_lambda, {:foo=>"foo"}]', I18n.t(:a_lambda, :foo => 'foo')
|
9
9
|
end
|
10
10
|
|
11
11
|
test "defaults: given a default is a Proc it calls it with the key and interpolation values" do
|
12
|
-
proc = lambda { |*args| filter_args(*args) }
|
12
|
+
proc = lambda { |*args| I18n::Tests::Procs.filter_args(*args) }
|
13
13
|
assert_equal '[nil, {:foo=>"foo"}]', I18n.t(nil, :default => proc, :foo => 'foo')
|
14
14
|
end
|
15
15
|
|
16
16
|
test "defaults: given a default is a key that resolves to a Proc it calls it with the key and interpolation values" do
|
17
|
-
|
17
|
+
the_lambda = lambda { |*args| I18n::Tests::Procs.filter_args(*args) }
|
18
|
+
I18n.backend.store_translations(:en, :a_lambda => the_lambda)
|
18
19
|
assert_equal '[:a_lambda, {:foo=>"foo"}]', I18n.t(nil, :default => :a_lambda, :foo => 'foo')
|
19
20
|
assert_equal '[:a_lambda, {:foo=>"foo"}]', I18n.t(nil, :default => [nil, :a_lambda], :foo => 'foo')
|
20
21
|
end
|
21
22
|
|
22
23
|
test "interpolation: given an interpolation value is a lambda it calls it with key and values before interpolating it" do
|
23
|
-
proc = lambda { |*args| filter_args(*args) }
|
24
|
+
proc = lambda { |*args| I18n::Tests::Procs.filter_args(*args) }
|
24
25
|
assert_match %r(\[\{:foo=>#<Proc.*>\}\]), I18n.t(nil, :default => '%{foo}', :foo => proc)
|
25
26
|
end
|
26
27
|
|
27
28
|
test "interpolation: given a key resolves to a Proc that returns a string then interpolation still works" do
|
28
|
-
proc = lambda { |*args| "%{foo}: " + filter_args(*args) }
|
29
|
+
proc = lambda { |*args| "%{foo}: " + I18n::Tests::Procs.filter_args(*args) }
|
29
30
|
assert_equal 'foo: [nil, {:foo=>"foo"}]', I18n.t(nil, :default => proc, :foo => 'foo')
|
30
31
|
end
|
31
32
|
|
@@ -37,17 +38,16 @@ module I18n
|
|
37
38
|
end
|
38
39
|
|
39
40
|
test "lookup: given the option :resolve => false was passed it does not resolve proc translations" do
|
40
|
-
I18n.backend.store_translations(:en, :a_lambda => lambda { |*args| filter_args(*args) })
|
41
|
+
I18n.backend.store_translations(:en, :a_lambda => lambda { |*args| I18n::Tests::Procs.filter_args(*args) })
|
41
42
|
assert_equal Proc, I18n.t(:a_lambda, :resolve => false).class
|
42
43
|
end
|
43
44
|
|
44
45
|
test "lookup: given the option :resolve => false was passed it does not resolve proc default" do
|
45
|
-
assert_equal Proc, I18n.t(nil, :default => lambda { |*args| filter_args(*args) }, :resolve => false).class
|
46
|
+
assert_equal Proc, I18n.t(nil, :default => lambda { |*args| I18n::Tests::Procs.filter_args(*args) }, :resolve => false).class
|
46
47
|
end
|
47
48
|
|
48
|
-
protected
|
49
49
|
|
50
|
-
def filter_args(*args)
|
50
|
+
def self.filter_args(*args)
|
51
51
|
args.map {|arg| arg.delete(:fallback) if arg.is_a?(Hash) ; arg }.inspect
|
52
52
|
end
|
53
53
|
end
|
data/lib/i18n/version.rb
CHANGED
data/test/backend/cache_test.rb
CHANGED
@@ -15,6 +15,7 @@ class I18nBackendCacheTest < I18n::TestCase
|
|
15
15
|
I18n.backend = Backend.new
|
16
16
|
super
|
17
17
|
I18n.cache_store = ActiveSupport::Cache.lookup_store(:memory_store)
|
18
|
+
I18n.cache_key_digest = nil
|
18
19
|
end
|
19
20
|
|
20
21
|
def teardown
|
@@ -37,6 +38,12 @@ class I18nBackendCacheTest < I18n::TestCase
|
|
37
38
|
assert_equal 'Bar', I18n.t(:bar)
|
38
39
|
end
|
39
40
|
|
41
|
+
test "translate returns a cached false response" do
|
42
|
+
I18n.backend.expects(:lookup).never
|
43
|
+
I18n.cache_store.expects(:read).returns(false)
|
44
|
+
assert_equal false, I18n.t(:foo)
|
45
|
+
end
|
46
|
+
|
40
47
|
test "still raises MissingTranslationData but also caches it" do
|
41
48
|
assert_raise(I18n::MissingTranslationData) { I18n.t(:missing, :raise => true) }
|
42
49
|
assert_raise(I18n::MissingTranslationData) { I18n.t(:missing, :raise => true) }
|
@@ -60,10 +67,19 @@ class I18nBackendCacheTest < I18n::TestCase
|
|
60
67
|
|
61
68
|
test "adds locale and hash of key and hash of options" do
|
62
69
|
options = { :bar=>1 }
|
63
|
-
options_hash =
|
70
|
+
options_hash = RUBY_VERSION <= "1.9" ? options.inspect.hash : options.hash
|
64
71
|
assert_equal "i18n//en/#{:foo.hash}/#{options_hash}", I18n.backend.send(:cache_key, :en, :foo, options)
|
65
72
|
end
|
66
73
|
|
74
|
+
test "cache_key uses configured digest method" do
|
75
|
+
md5 = Digest::MD5.new
|
76
|
+
options = { :bar=>1 }
|
77
|
+
options_hash = options.inspect
|
78
|
+
with_cache_key_digest(md5) do
|
79
|
+
assert_equal "i18n//en/#{md5.hexdigest(:foo.to_s)}/#{md5.hexdigest(options_hash)}", I18n.backend.send(:cache_key, :en, :foo, options)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
67
83
|
test "keys should not be equal" do
|
68
84
|
interpolation_values1 = { :foo => 1, :bar => 2 }
|
69
85
|
interpolation_values2 = { :foo => 2, :bar => 1 }
|
@@ -81,6 +97,12 @@ class I18nBackendCacheTest < I18n::TestCase
|
|
81
97
|
yield
|
82
98
|
I18n.cache_namespace = nil
|
83
99
|
end
|
100
|
+
|
101
|
+
def with_cache_key_digest(digest)
|
102
|
+
I18n.cache_key_digest = digest
|
103
|
+
yield
|
104
|
+
I18n.cache_key_digest = nil
|
105
|
+
end
|
84
106
|
end
|
85
107
|
|
86
108
|
end # AS cache check
|
@@ -141,6 +141,10 @@ class I18nBackendFallbacksWithChainTest < I18n::TestCase
|
|
141
141
|
assert_equal 'FOO', I18n.t(:foo, :locale => :'de-DE')
|
142
142
|
end
|
143
143
|
|
144
|
+
test "falls back from de-DE to de when there is no translation for de-DE available when using arrays, too" do
|
145
|
+
assert_equal ['FOO', 'FOO'], I18n.t([:foo, :foo], :locale => :'de-DE')
|
146
|
+
end
|
147
|
+
|
144
148
|
test "should not raise error when enforce_available_locales is true, :'pt' is missing and default is a Symbol" do
|
145
149
|
I18n.enforce_available_locales = true
|
146
150
|
begin
|
@@ -150,3 +154,45 @@ class I18nBackendFallbacksWithChainTest < I18n::TestCase
|
|
150
154
|
end
|
151
155
|
end
|
152
156
|
end
|
157
|
+
|
158
|
+
class I18nBackendFallbacksExistsTest < I18n::TestCase
|
159
|
+
class Backend < I18n::Backend::Simple
|
160
|
+
include I18n::Backend::Fallbacks
|
161
|
+
end
|
162
|
+
|
163
|
+
def setup
|
164
|
+
super
|
165
|
+
I18n.backend = Backend.new
|
166
|
+
store_translations(:en, :foo => 'Foo in :en', :bar => 'Bar in :en')
|
167
|
+
store_translations(:de, :bar => 'Bar in :de')
|
168
|
+
store_translations(:'de-DE', :baz => 'Baz in :de-DE')
|
169
|
+
end
|
170
|
+
|
171
|
+
test "exists? given an existing key will return true" do
|
172
|
+
assert_equal true, I18n.exists?(:foo)
|
173
|
+
end
|
174
|
+
|
175
|
+
test "exists? given a non-existing key will return false" do
|
176
|
+
assert_equal false, I18n.exists?(:bogus)
|
177
|
+
end
|
178
|
+
|
179
|
+
test "exists? given an existing key and an existing locale will return true" do
|
180
|
+
assert_equal true, I18n.exists?(:foo, :en)
|
181
|
+
assert_equal true, I18n.exists?(:bar, :de)
|
182
|
+
end
|
183
|
+
|
184
|
+
test "exists? given a non-existing key and an existing locale will return false" do
|
185
|
+
assert_equal false, I18n.exists?(:bogus, :en)
|
186
|
+
assert_equal false, I18n.exists?(:bogus, :de)
|
187
|
+
end
|
188
|
+
|
189
|
+
test "exists? should return true given a key which is missing from the given locale and exists in a fallback locale" do
|
190
|
+
assert_equal true, I18n.exists?(:foo, :de)
|
191
|
+
assert_equal true, I18n.exists?(:foo, :'de-DE')
|
192
|
+
end
|
193
|
+
|
194
|
+
test "exists? should return false given a key which is missing from the given locale and all its fallback locales" do
|
195
|
+
assert_equal false, I18n.exists?(:baz, :de)
|
196
|
+
assert_equal false, I18n.exists?(:bogus, :'de-DE')
|
197
|
+
end
|
198
|
+
end
|
data/test/core_ext/hash_test.rb
CHANGED
@@ -14,6 +14,18 @@ class I18nCoreExtHashInterpolationTest < I18n::TestCase
|
|
14
14
|
assert_equal expected, hash.slice(:foo)
|
15
15
|
end
|
16
16
|
|
17
|
+
test "#slice non-existent key" do
|
18
|
+
hash = { :foo => 'bar', :baz => 'bar' }
|
19
|
+
expected = { :foo => 'bar' }
|
20
|
+
assert_equal expected, hash.slice(:foo, :not_here)
|
21
|
+
end
|
22
|
+
|
23
|
+
test "#slice maintains subclasses of Hash" do
|
24
|
+
klass = Class.new(Hash)
|
25
|
+
hash = klass[:foo, 'bar', :baz, 'bar']
|
26
|
+
assert_instance_of klass, hash.slice(:foo)
|
27
|
+
end
|
28
|
+
|
17
29
|
test "#except" do
|
18
30
|
hash = { :foo => 'bar', :baz => 'bar' }
|
19
31
|
expected = { :foo => 'bar' }
|
data/test/gettext/api_test.rb
CHANGED
@@ -18,6 +18,13 @@ class I18nGettextApiTest < I18n::TestCase
|
|
18
18
|
}, :separator => '|'
|
19
19
|
end
|
20
20
|
|
21
|
+
# N_
|
22
|
+
def test_N_returns_original_msg
|
23
|
+
assert_equal 'foo|bar', N_('foo|bar')
|
24
|
+
I18n.locale = :de
|
25
|
+
assert_equal 'Hi Gettext!', N_('Hi Gettext!')
|
26
|
+
end
|
27
|
+
|
21
28
|
# gettext
|
22
29
|
def test_gettext_uses_msg_as_default
|
23
30
|
assert_equal 'Hi Gettext!', _('Hi Gettext!')
|
@@ -13,6 +13,11 @@ class I18nExceptionsTest < I18n::TestCase
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
+
test "MissingTranslation can be initialized without options" do
|
17
|
+
exception = I18n::MissingTranslation.new(:en, 'foo')
|
18
|
+
assert_equal({}, exception.options)
|
19
|
+
end
|
20
|
+
|
16
21
|
test "MissingTranslationData exception stores locale, key and options" do
|
17
22
|
force_missing_translation_data do |exception|
|
18
23
|
assert_equal 'de', exception.locale
|
@@ -66,6 +71,10 @@ class I18nExceptionsTest < I18n::TestCase
|
|
66
71
|
assert_equal 'reserved key :scope used in "%{scope}"', exception.message
|
67
72
|
end
|
68
73
|
end
|
74
|
+
|
75
|
+
test "MissingTranslationData#new can be initialized with just two arguments" do
|
76
|
+
assert I18n::MissingTranslationData.new('en', 'key')
|
77
|
+
end
|
69
78
|
|
70
79
|
private
|
71
80
|
|
@@ -57,6 +57,17 @@ class I18nInterpolateTest < I18n::TestCase
|
|
57
57
|
def test_sprintf_mix_unformatted_and_formatted_named_placeholders
|
58
58
|
assert_equal "foo 1.000000", I18n.interpolate("%{name} %<num>f", :name => "foo", :num => 1.0)
|
59
59
|
end
|
60
|
+
|
61
|
+
class RailsSafeBuffer < String
|
62
|
+
|
63
|
+
def gsub(*args, &block)
|
64
|
+
to_str.gsub(*args, &block)
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
test "with String subclass that redefined gsub method" do
|
69
|
+
assert_equal "Hello mars world", I18n.interpolate(RailsSafeBuffer.new("Hello %{planet} world"), :planet => 'mars')
|
70
|
+
end
|
60
71
|
end
|
61
72
|
|
62
73
|
class I18nMissingInterpolationCustomHandlerTest < I18n::TestCase
|
data/test/i18n_test.rb
CHANGED
@@ -269,6 +269,10 @@ class I18nTest < I18n::TestCase
|
|
269
269
|
assert_raise(I18n::ArgumentError) { I18n.l nil }
|
270
270
|
end
|
271
271
|
|
272
|
+
test "localize given nil and default returns default" do
|
273
|
+
assert_equal nil, I18n.l(nil, :default => nil)
|
274
|
+
end
|
275
|
+
|
272
276
|
test "localize given an Object raises an I18n::ArgumentError" do
|
273
277
|
assert_raise(I18n::ArgumentError) { I18n.l Object.new }
|
274
278
|
end
|
@@ -74,7 +74,7 @@
|
|
74
74
|
:or => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
|
75
75
|
:pa => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
|
76
76
|
:pap => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
|
77
|
-
:pl => { :i18n => { :plural => { :keys => [:one, :few, :other], :rule => lambda { |n| n == 1 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : :other } } } },
|
77
|
+
:pl => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n == 1 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : (n != 1 && [0, 1].include?(n % 10)) || [5, 6, 7, 8, 9].include?(n % 10) || [12, 13, 14].include?(n % 100) ? :many : :other } } } },
|
78
78
|
:ps => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
|
79
79
|
:pt => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } },
|
80
80
|
:"pt-PT" => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } },
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: i18n
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sven Fuchs
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date:
|
15
|
+
date: 2016-11-21 00:00:00.000000000 Z
|
16
16
|
dependencies: []
|
17
17
|
description: New wave Internationalization support for Ruby.
|
18
18
|
email: rails-i18n@googlegroups.com
|
@@ -30,6 +30,8 @@ files:
|
|
30
30
|
- gemfiles/Gemfile.rails-4.1.x.lock
|
31
31
|
- gemfiles/Gemfile.rails-4.2.x
|
32
32
|
- gemfiles/Gemfile.rails-4.2.x.lock
|
33
|
+
- gemfiles/Gemfile.rails-5.0.x
|
34
|
+
- gemfiles/Gemfile.rails-5.0.x.lock
|
33
35
|
- gemfiles/Gemfile.rails-master
|
34
36
|
- gemfiles/Gemfile.rails-master.lock
|
35
37
|
- lib/i18n.rb
|
@@ -136,7 +138,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
136
138
|
version: 1.3.5
|
137
139
|
requirements: []
|
138
140
|
rubyforge_project: "[none]"
|
139
|
-
rubygems_version: 2.
|
141
|
+
rubygems_version: 2.5.1
|
140
142
|
signing_key:
|
141
143
|
specification_version: 4
|
142
144
|
summary: New wave Internationalization support for Ruby
|