i18n 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -3
- data/gemfiles/Gemfile.rails-3.2.x +1 -0
- data/gemfiles/Gemfile.rails-3.2.x.lock +7 -2
- data/gemfiles/Gemfile.rails-4.0.x +1 -0
- data/gemfiles/Gemfile.rails-4.0.x.lock +9 -5
- data/gemfiles/Gemfile.rails-4.1.x +1 -0
- data/gemfiles/Gemfile.rails-4.1.x.lock +9 -5
- data/gemfiles/Gemfile.rails-4.2.x +2 -1
- data/gemfiles/Gemfile.rails-4.2.x.lock +12 -8
- 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 +12 -9
- 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/lib/i18n.rb +5 -3
- 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: 9b6433e5959dd59f7cbc749fe2cf4ab553282d59
|
4
|
+
data.tar.gz: 56e4232a2598aacbfde0803866c8dbf0f7b65715
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9f853f9dbcaf818c62134260296b9daad5885c5e68c5cacf87e13d5526d41ab982ff5387c9037d988b97bfec87b5854666088683157979af7dabcbf21f7e9714
|
7
|
+
data.tar.gz: 2baa498d60d117129803c04336c7f1bc77d11a497890d2e68035bfe080d5b8827b52d7f04973339e90d9fc80e8025ae480b8e5f32f111465e523c551dffcee63
|
data/README.md
CHANGED
@@ -4,6 +4,8 @@
|
|
4
4
|
|
5
5
|
Ruby Internationalization and localization solution.
|
6
6
|
|
7
|
+
[See the Rails Guide](http://guides.rubyonrails.org/i18n.html) for an example of its usage. (Note: This library can be used independently from Rails.)
|
8
|
+
|
7
9
|
Features:
|
8
10
|
|
9
11
|
* translation and localization
|
@@ -22,7 +24,7 @@ Pluggable features:
|
|
22
24
|
* Cache
|
23
25
|
* Pluralization: lambda pluralizers stored as translation data
|
24
26
|
* Locale fallbacks, RFC4647 compliant (optionally: RFC4646 locale validation)
|
25
|
-
* Gettext support
|
27
|
+
* [Gettext support](https://github.com/svenfuchs/i18n/wiki/Gettext)
|
26
28
|
* Translation metadata
|
27
29
|
|
28
30
|
Alternative backends:
|
@@ -31,7 +33,7 @@ Alternative backends:
|
|
31
33
|
* ActiveRecord (optionally: ActiveRecord::Missing and ActiveRecord::StoreProcs)
|
32
34
|
* KeyValue (uses active_support/json and cannot store procs)
|
33
35
|
|
34
|
-
For more information and lots of resources see
|
36
|
+
For more information and lots of resources see [the 'Resources' page on the wiki](https://github.com/svenfuchs/i18n/wiki/Resources).
|
35
37
|
|
36
38
|
## Installation
|
37
39
|
|
@@ -71,7 +73,7 @@ follow the usual test setup and should be easy to grok.
|
|
71
73
|
* [Joshua Harvey](http://www.workingwithrails.com/person/759-joshua-harvey)
|
72
74
|
* [Stephan Soller](http://www.arkanis-development.de)
|
73
75
|
* [Saimon Moore](http://saimonmoore.net)
|
74
|
-
* [Matt Aimonetti](
|
76
|
+
* [Matt Aimonetti](https://matt.aimonetti.net/)
|
75
77
|
|
76
78
|
## Contributors
|
77
79
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ..
|
3
3
|
specs:
|
4
|
-
i18n (0.
|
4
|
+
i18n (0.8.0.beta1)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -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.7
|
@@ -1,12 +1,12 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ..
|
3
3
|
specs:
|
4
|
-
i18n (0.
|
4
|
+
i18n (0.8.0.beta1)
|
5
5
|
|
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.7
|
@@ -1,25 +1,25 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ..
|
3
3
|
specs:
|
4
|
-
i18n (0.
|
4
|
+
i18n (0.8.0.beta1)
|
5
5
|
|
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.7
|
@@ -1,25 +1,25 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ..
|
3
3
|
specs:
|
4
|
-
i18n (0.
|
4
|
+
i18n (0.8.0.beta1)
|
5
5
|
|
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.7
|
@@ -0,0 +1,37 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ..
|
3
|
+
specs:
|
4
|
+
i18n (0.8.0.beta1)
|
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.7
|
@@ -1,31 +1,30 @@
|
|
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
|
14
13
|
remote: ..
|
15
14
|
specs:
|
16
|
-
i18n (0.
|
15
|
+
i18n (0.8.0.beta1)
|
17
16
|
|
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.7
|
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/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/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
|
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: 2017-01-31 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.6.8
|
140
142
|
signing_key:
|
141
143
|
specification_version: 4
|
142
144
|
summary: New wave Internationalization support for Ruby
|