i18n 1.6.0 → 1.8.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +21 -24
- data/lib/i18n.rb +27 -10
- data/lib/i18n/backend/base.rb +7 -6
- data/lib/i18n/backend/cache.rb +3 -3
- data/lib/i18n/backend/cache_file.rb +2 -2
- data/lib/i18n/backend/chain.rb +8 -5
- data/lib/i18n/backend/fallbacks.rb +16 -3
- data/lib/i18n/backend/flatten.rb +8 -5
- data/lib/i18n/backend/pluralization.rb +1 -1
- data/lib/i18n/backend/simple.rb +2 -4
- data/lib/i18n/core_ext/hash.rb +18 -6
- data/lib/i18n/gettext/helpers.rb +1 -1
- data/lib/i18n/interpolate/ruby.rb +2 -2
- data/lib/i18n/locale/fallbacks.rb +8 -5
- data/lib/i18n/locale/tag/parents.rb +8 -6
- data/lib/i18n/locale/tag/simple.rb +1 -1
- data/lib/i18n/tests/interpolation.rb +4 -0
- data/lib/i18n/tests/link.rb +11 -1
- data/lib/i18n/tests/localization/date.rb +2 -2
- data/lib/i18n/tests/localization/procs.rb +6 -5
- data/lib/i18n/tests/lookup.rb +2 -2
- data/lib/i18n/tests/procs.rb +5 -0
- data/lib/i18n/version.rb +1 -1
- metadata +11 -23
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 63d76facf7fc1bb9f1f7fca47cec61259726d0879a7e187646f54a7e94f0f73c
|
|
4
|
+
data.tar.gz: ca27ecf10f4cf612a57fa0747aeb2431f42fa08210ce1df79675c17d52b5c203
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3007d330c4b3c1d88feb69d3d4f877cfd19d15461929b7af70c902090ec8872e7dfe2c6a2ed0bec56111b3146fe7f2c1718c9e70aaa424369f6d5f8fa4a4ae42
|
|
7
|
+
data.tar.gz: 4c2119abc3a28a9d2a18e429befb3ade43de7b5cf65c48f2c67896905d30358759ada31c8442414fd6d859a6a45f52441b7ca05ccac18790ed3f6ea0edc3b97e
|
data/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Ruby I18n
|
|
2
2
|
|
|
3
|
-
[](https://github.com/ruby-i18n/i18n/actions?query=workflow%3ARuby)
|
|
4
4
|
|
|
5
|
-
Ruby
|
|
5
|
+
Ruby internationalization and localization (i18n) solution.
|
|
6
6
|
|
|
7
7
|
Currently maintained by @radar.
|
|
8
8
|
|
|
@@ -12,7 +12,7 @@ Currently maintained by @radar.
|
|
|
12
12
|
|
|
13
13
|
You will most commonly use this library within a Rails app.
|
|
14
14
|
|
|
15
|
-
[See the Rails Guide](
|
|
15
|
+
[See the Rails Guide](https://guides.rubyonrails.org/i18n.html) for an example of its usage.
|
|
16
16
|
|
|
17
17
|
### Ruby (without Rails)
|
|
18
18
|
|
|
@@ -51,16 +51,16 @@ I18n.t(:test) # => "Dies ist ein Test"
|
|
|
51
51
|
|
|
52
52
|
## Features
|
|
53
53
|
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
54
|
+
* Translation and localization
|
|
55
|
+
* Interpolation of values to translations
|
|
56
|
+
* Pluralization (CLDR compatible)
|
|
57
|
+
* Customizable transliteration to ASCII
|
|
58
|
+
* Flexible defaults
|
|
59
|
+
* Bulk lookup
|
|
60
|
+
* Lambdas as translation data
|
|
61
|
+
* Custom key/scope separator
|
|
62
|
+
* Custom exception handlers
|
|
63
|
+
* Extensible architecture with a swappable backend
|
|
64
64
|
|
|
65
65
|
## Pluggable Features
|
|
66
66
|
|
|
@@ -95,7 +95,7 @@ particular tests in different test cases.
|
|
|
95
95
|
The reason for this is that we need to enforce the I18n API across various
|
|
96
96
|
combinations of extensions. E.g. the Simple backend alone needs to support
|
|
97
97
|
the same API as any combination of feature and/or optimization modules included
|
|
98
|
-
to the Simple backend. We test this by reusing the same API
|
|
98
|
+
to the Simple backend. We test this by reusing the same API definition (implemented
|
|
99
99
|
as test methods) in test cases with different setups.
|
|
100
100
|
|
|
101
101
|
You can find the test cases that enforce the API in test/api. And you can find
|
|
@@ -106,19 +106,16 @@ follow the usual test setup and should be easy to grok.
|
|
|
106
106
|
|
|
107
107
|
## More Documentation
|
|
108
108
|
|
|
109
|
-
Additional documentation can be found here: https://github.com/
|
|
110
|
-
|
|
111
|
-
## Authors
|
|
112
|
-
|
|
113
|
-
* [Sven Fuchs](http://www.artweb-design.de)
|
|
114
|
-
* [Joshua Harvey](http://www.workingwithrails.com/person/759-joshua-harvey)
|
|
115
|
-
* [Stephan Soller](http://www.arkanis-development.de)
|
|
116
|
-
* [Saimon Moore](http://saimonmoore.net)
|
|
117
|
-
* [Matt Aimonetti](https://matt.aimonetti.net/)
|
|
109
|
+
Additional documentation can be found here: https://github.com/ruby-i18n/i18n/wiki
|
|
118
110
|
|
|
119
111
|
## Contributors
|
|
120
112
|
|
|
121
|
-
|
|
113
|
+
* @radar
|
|
114
|
+
* @carlosantoniodasilva
|
|
115
|
+
* @josevalim
|
|
116
|
+
* @knapo
|
|
117
|
+
* @tigrish
|
|
118
|
+
* [and many more](https://github.com/ruby-i18n/i18n/graphs/contributors)
|
|
122
119
|
|
|
123
120
|
## License
|
|
124
121
|
|
data/lib/i18n.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'concurrent/map'
|
|
4
|
+
require 'concurrent/hash'
|
|
4
5
|
|
|
5
6
|
require 'i18n/version'
|
|
6
7
|
require 'i18n/exceptions'
|
|
@@ -33,7 +34,7 @@ module I18n
|
|
|
33
34
|
EMPTY_HASH = {}.freeze
|
|
34
35
|
|
|
35
36
|
def self.new_double_nested_cache # :nodoc:
|
|
36
|
-
Concurrent::Map.new { |h,k| h[k] = Concurrent::Map.new }
|
|
37
|
+
Concurrent::Map.new { |h, k| h[k] = Concurrent::Map.new }
|
|
37
38
|
end
|
|
38
39
|
|
|
39
40
|
module Base
|
|
@@ -115,7 +116,7 @@ module I18n
|
|
|
115
116
|
# *PLURALIZATION*
|
|
116
117
|
#
|
|
117
118
|
# Translation data can contain pluralized translations. Pluralized translations
|
|
118
|
-
# are arrays of
|
|
119
|
+
# are arrays of singular/plural versions of translations like <tt>['Foo', 'Foos']</tt>.
|
|
119
120
|
#
|
|
120
121
|
# Note that <tt>I18n::Backend::Simple</tt> only supports an algorithm for English
|
|
121
122
|
# pluralization rules. Other algorithms can be supported by custom backends.
|
|
@@ -173,10 +174,26 @@ module I18n
|
|
|
173
174
|
#
|
|
174
175
|
# It is recommended to use/implement lambdas in an "idempotent" way. E.g. when
|
|
175
176
|
# a cache layer is put in front of I18n.translate it will generate a cache key
|
|
176
|
-
# from the argument values passed to #translate.
|
|
177
|
+
# from the argument values passed to #translate. Therefore your lambdas should
|
|
177
178
|
# always return the same translations/values per unique combination of argument
|
|
178
179
|
# values.
|
|
179
|
-
|
|
180
|
+
#
|
|
181
|
+
# *Ruby 2.7+ keyword arguments warning*
|
|
182
|
+
#
|
|
183
|
+
# This method uses keyword arguments.
|
|
184
|
+
# There is a breaking change in ruby that produces warning with ruby 2.7 and won't work as expected with ruby 3.0
|
|
185
|
+
# The "hash" parameter must be passed as keyword argument.
|
|
186
|
+
#
|
|
187
|
+
# Good:
|
|
188
|
+
# I18n.t(:salutation, :gender => 'w', :name => 'Smith')
|
|
189
|
+
# I18n.t(:salutation, **{ :gender => 'w', :name => 'Smith' })
|
|
190
|
+
# I18n.t(:salutation, **any_hash)
|
|
191
|
+
#
|
|
192
|
+
# Bad:
|
|
193
|
+
# I18n.t(:salutation, { :gender => 'w', :name => 'Smith' })
|
|
194
|
+
# I18n.t(:salutation, any_hash)
|
|
195
|
+
#
|
|
196
|
+
def translate(key = nil, throw: false, raise: false, locale: nil, **options) # TODO deprecate :raise
|
|
180
197
|
locale ||= config.locale
|
|
181
198
|
raise Disabled.new('t') if locale == false
|
|
182
199
|
enforce_available_locales!(locale)
|
|
@@ -201,17 +218,17 @@ module I18n
|
|
|
201
218
|
|
|
202
219
|
# Wrapper for <tt>translate</tt> that adds <tt>:raise => true</tt>. With
|
|
203
220
|
# this option, if no translation is found, it will raise <tt>I18n::MissingTranslationData</tt>
|
|
204
|
-
def translate!(key, options
|
|
205
|
-
translate(key, options
|
|
221
|
+
def translate!(key, **options)
|
|
222
|
+
translate(key, **options, raise: true)
|
|
206
223
|
end
|
|
207
224
|
alias :t! :translate!
|
|
208
225
|
|
|
209
226
|
# Returns true if a translation exists for a given key, otherwise returns false.
|
|
210
|
-
def exists?(key, _locale = nil, locale: _locale)
|
|
227
|
+
def exists?(key, _locale = nil, locale: _locale, **options)
|
|
211
228
|
locale ||= config.locale
|
|
212
229
|
raise Disabled.new('exists?') if locale == false
|
|
213
230
|
raise I18n::ArgumentError if key.is_a?(String) && key.empty?
|
|
214
|
-
config.backend.exists?(locale, key)
|
|
231
|
+
config.backend.exists?(locale, key, options)
|
|
215
232
|
end
|
|
216
233
|
|
|
217
234
|
# Transliterates UTF-8 characters to ASCII. By default this method will
|
|
@@ -265,7 +282,7 @@ module I18n
|
|
|
265
282
|
# I18n.transliterate("Jürgen") # => "Juergen"
|
|
266
283
|
# I18n.transliterate("Jürgen", :locale => :en) # => "Jurgen"
|
|
267
284
|
# I18n.transliterate("Jürgen", :locale => :de) # => "Juergen"
|
|
268
|
-
def transliterate(key,
|
|
285
|
+
def transliterate(key, throw: false, raise: false, locale: nil, replacement: nil, **options)
|
|
269
286
|
locale ||= config.locale
|
|
270
287
|
raise Disabled.new('transliterate') if locale == false
|
|
271
288
|
enforce_available_locales!(locale)
|
|
@@ -373,7 +390,7 @@ module I18n
|
|
|
373
390
|
@@normalized_key_cache[separator][key] ||=
|
|
374
391
|
case key
|
|
375
392
|
when Array
|
|
376
|
-
key.
|
|
393
|
+
key.flat_map { |k| normalize_key(k, separator) }
|
|
377
394
|
else
|
|
378
395
|
keys = key.to_s.split(separator)
|
|
379
396
|
keys.delete('')
|
data/lib/i18n/backend/base.rb
CHANGED
|
@@ -64,7 +64,7 @@ module I18n
|
|
|
64
64
|
entry
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
-
def exists?(locale, key)
|
|
67
|
+
def exists?(locale, key, options = EMPTY_HASH)
|
|
68
68
|
lookup(locale, key) != nil
|
|
69
69
|
end
|
|
70
70
|
|
|
@@ -81,7 +81,7 @@ module I18n
|
|
|
81
81
|
key = format
|
|
82
82
|
type = object.respond_to?(:sec) ? 'time' : 'date'
|
|
83
83
|
options = options.merge(:raise => true, :object => object, :locale => locale)
|
|
84
|
-
format = I18n.t(:"#{type}.formats.#{key}", options)
|
|
84
|
+
format = I18n.t(:"#{type}.formats.#{key}", **options)
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
format = translate_localization_format(locale, object, format, options)
|
|
@@ -122,7 +122,7 @@ module I18n
|
|
|
122
122
|
# first translation that can be resolved. Otherwise it tries to resolve
|
|
123
123
|
# the translation directly.
|
|
124
124
|
def default(locale, object, subject, options = EMPTY_HASH)
|
|
125
|
-
options = options.
|
|
125
|
+
options = options.reject { |key, value| key == :default }
|
|
126
126
|
case subject
|
|
127
127
|
when Array
|
|
128
128
|
subject.each do |item|
|
|
@@ -143,10 +143,10 @@ module I18n
|
|
|
143
143
|
result = catch(:exception) do
|
|
144
144
|
case subject
|
|
145
145
|
when Symbol
|
|
146
|
-
I18n.translate(subject, options.merge(:locale => locale, :throw => true))
|
|
146
|
+
I18n.translate(subject, **options.merge(:locale => locale, :throw => true))
|
|
147
147
|
when Proc
|
|
148
148
|
date_or_time = options.delete(:object) || object
|
|
149
|
-
resolve(locale, object, subject.call(date_or_time, options))
|
|
149
|
+
resolve(locale, object, subject.call(date_or_time, **options))
|
|
150
150
|
else
|
|
151
151
|
subject
|
|
152
152
|
end
|
|
@@ -163,7 +163,8 @@ module I18n
|
|
|
163
163
|
# not standard with regards to the CLDR pluralization rules.
|
|
164
164
|
# Other backends can implement more flexible or complex pluralization rules.
|
|
165
165
|
def pluralize(locale, entry, count)
|
|
166
|
-
|
|
166
|
+
entry = entry.reject { |k, _v| k == :attributes } if entry.is_a?(Hash)
|
|
167
|
+
return entry unless entry.is_a?(Hash) && count && entry.values.none? { |v| v.is_a?(Hash) }
|
|
167
168
|
|
|
168
169
|
key = pluralization_key(entry, count)
|
|
169
170
|
raise InvalidPluralizationData.new(entry, count, key) unless entry.has_key?(key)
|
data/lib/i18n/backend/cache.rb
CHANGED
|
@@ -17,11 +17,11 @@
|
|
|
17
17
|
#
|
|
18
18
|
# The cache_key implementation by default assumes you pass values that return
|
|
19
19
|
# a valid key from #hash (see
|
|
20
|
-
#
|
|
20
|
+
# https://www.ruby-doc.org/core/classes/Object.html#M000337). However, you can
|
|
21
21
|
# configure your own digest method via which responds to #hexdigest (see
|
|
22
|
-
#
|
|
22
|
+
# https://ruby-doc.org/stdlib/libdoc/openssl/rdoc/OpenSSL/Digest.html):
|
|
23
23
|
#
|
|
24
|
-
# I18n.cache_key_digest = Digest::
|
|
24
|
+
# I18n.cache_key_digest = OpenSSL::Digest::SHA256.new
|
|
25
25
|
#
|
|
26
26
|
# If you use a lambda as a default value in your translation like this:
|
|
27
27
|
#
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require '
|
|
3
|
+
require 'openssl'
|
|
4
4
|
|
|
5
5
|
module I18n
|
|
6
6
|
module Backend
|
|
@@ -19,7 +19,7 @@ module I18n
|
|
|
19
19
|
key = I18n::Backend::Flatten.escape_default_separator(normalized_path(filename))
|
|
20
20
|
old_mtime, old_digest = initialized && lookup(:i18n, key, :load_file)
|
|
21
21
|
return if (mtime = File.mtime(filename).to_i) == old_mtime ||
|
|
22
|
-
(digest = Digest::
|
|
22
|
+
(digest = OpenSSL::Digest::SHA256.file(filename).hexdigest) == old_digest
|
|
23
23
|
super
|
|
24
24
|
store_translations(:i18n, load_file: { key => [mtime, digest] })
|
|
25
25
|
end
|
data/lib/i18n/backend/chain.rb
CHANGED
|
@@ -73,9 +73,9 @@ module I18n
|
|
|
73
73
|
throw(:exception, I18n::MissingTranslation.new(locale, key, options))
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
-
def exists?(locale, key)
|
|
76
|
+
def exists?(locale, key, options = EMPTY_HASH)
|
|
77
77
|
backends.any? do |backend|
|
|
78
|
-
backend.exists?(locale, key)
|
|
78
|
+
backend.exists?(locale, key, options)
|
|
79
79
|
end
|
|
80
80
|
end
|
|
81
81
|
|
|
@@ -96,9 +96,12 @@ module I18n
|
|
|
96
96
|
end
|
|
97
97
|
|
|
98
98
|
def translations
|
|
99
|
-
backends.
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
backends.reverse.each_with_object({}) do |backend, memo|
|
|
100
|
+
partial_translations = backend.instance_eval do
|
|
101
|
+
init_translations unless initialized?
|
|
102
|
+
translations
|
|
103
|
+
end
|
|
104
|
+
memo.deep_merge!(partial_translations) { |_, a, b| b || a }
|
|
102
105
|
end
|
|
103
106
|
end
|
|
104
107
|
|
|
@@ -16,11 +16,13 @@ module I18n
|
|
|
16
16
|
# Returns the current fallbacks implementation. Defaults to +I18n::Locale::Fallbacks+.
|
|
17
17
|
def fallbacks
|
|
18
18
|
@@fallbacks ||= I18n::Locale::Fallbacks.new
|
|
19
|
+
Thread.current[:i18n_fallbacks] || @@fallbacks
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
# Sets the current fallbacks implementation. Use this to set a different fallbacks implementation.
|
|
22
23
|
def fallbacks=(fallbacks)
|
|
23
|
-
@@fallbacks = fallbacks
|
|
24
|
+
@@fallbacks = fallbacks.is_a?(Array) ? I18n::Locale::Fallbacks.new(fallbacks) : fallbacks
|
|
25
|
+
Thread.current[:i18n_fallbacks] = @@fallbacks
|
|
24
26
|
end
|
|
25
27
|
end
|
|
26
28
|
|
|
@@ -46,7 +48,10 @@ module I18n
|
|
|
46
48
|
begin
|
|
47
49
|
catch(:exception) do
|
|
48
50
|
result = super(fallback, key, fallback_options)
|
|
49
|
-
|
|
51
|
+
unless result.nil?
|
|
52
|
+
on_fallback(locale, fallback, key, options) if locale.to_s != fallback.to_s
|
|
53
|
+
return result
|
|
54
|
+
end
|
|
50
55
|
end
|
|
51
56
|
rescue I18n::InvalidLocale
|
|
52
57
|
# we do nothing when the locale is invalid, as this is a fallback anyways.
|
|
@@ -68,7 +73,8 @@ module I18n
|
|
|
68
73
|
return first_non_symbol_default
|
|
69
74
|
end
|
|
70
75
|
|
|
71
|
-
def exists?(locale, key)
|
|
76
|
+
def exists?(locale, key, options = EMPTY_HASH)
|
|
77
|
+
return super unless options.fetch(:fallback, true)
|
|
72
78
|
I18n.fallbacks[locale].each do |fallback|
|
|
73
79
|
begin
|
|
74
80
|
return true if super(fallback, key)
|
|
@@ -79,6 +85,13 @@ module I18n
|
|
|
79
85
|
|
|
80
86
|
false
|
|
81
87
|
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
# Overwrite on_fallback to add specified logic when the fallback succeeds.
|
|
92
|
+
def on_fallback(_original_locale, _fallback_locale, _key, _optoins)
|
|
93
|
+
nil
|
|
94
|
+
end
|
|
82
95
|
end
|
|
83
96
|
end
|
|
84
97
|
end
|
data/lib/i18n/backend/flatten.rb
CHANGED
|
@@ -18,14 +18,17 @@ module I18n
|
|
|
18
18
|
# and creates way less objects than the one at I18n.normalize_keys.
|
|
19
19
|
# It also handles escaping the translation keys.
|
|
20
20
|
def self.normalize_flat_keys(locale, key, scope, separator)
|
|
21
|
-
keys = [scope, key]
|
|
21
|
+
keys = [scope, key]
|
|
22
|
+
keys.flatten!
|
|
23
|
+
keys.compact!
|
|
24
|
+
|
|
22
25
|
separator ||= I18n.default_separator
|
|
23
26
|
|
|
24
27
|
if separator != FLATTEN_SEPARATOR
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
from_str = "#{FLATTEN_SEPARATOR}#{separator}"
|
|
29
|
+
to_str = "#{SEPARATOR_ESCAPE_CHAR}#{FLATTEN_SEPARATOR}"
|
|
30
|
+
|
|
31
|
+
keys.map! { |k| k.to_s.tr from_str, to_str }
|
|
29
32
|
end
|
|
30
33
|
|
|
31
34
|
keys.join(".")
|
|
@@ -29,7 +29,7 @@ module I18n
|
|
|
29
29
|
# either pick a special :zero translation even for languages where the
|
|
30
30
|
# pluralizer does not return a :zero key.
|
|
31
31
|
def pluralize(locale, entry, count)
|
|
32
|
-
return entry unless entry.is_a?(Hash)
|
|
32
|
+
return entry unless entry.is_a?(Hash) && count
|
|
33
33
|
|
|
34
34
|
pluralizer = pluralizer(locale)
|
|
35
35
|
if pluralizer.respond_to?(:call)
|
data/lib/i18n/backend/simple.rb
CHANGED
|
@@ -21,8 +21,6 @@ module I18n
|
|
|
21
21
|
class Simple
|
|
22
22
|
using I18n::HashRefinements
|
|
23
23
|
|
|
24
|
-
(class << self; self; end).class_eval { public :include }
|
|
25
|
-
|
|
26
24
|
module Implementation
|
|
27
25
|
include Base
|
|
28
26
|
|
|
@@ -42,7 +40,7 @@ module I18n
|
|
|
42
40
|
return data
|
|
43
41
|
end
|
|
44
42
|
locale = locale.to_sym
|
|
45
|
-
translations[locale] ||=
|
|
43
|
+
translations[locale] ||= Concurrent::Hash.new
|
|
46
44
|
data = data.deep_symbolize_keys
|
|
47
45
|
translations[locale].deep_merge!(data)
|
|
48
46
|
end
|
|
@@ -73,7 +71,7 @@ module I18n
|
|
|
73
71
|
# call `init_translations`
|
|
74
72
|
init_translations if do_init && !initialized?
|
|
75
73
|
|
|
76
|
-
@translations ||= {}
|
|
74
|
+
@translations ||= Concurrent::Hash.new { |h, k| h[k] = Concurrent::Hash.new }
|
|
77
75
|
end
|
|
78
76
|
|
|
79
77
|
protected
|
data/lib/i18n/core_ext/hash.rb
CHANGED
|
@@ -4,7 +4,7 @@ module I18n
|
|
|
4
4
|
using I18n::HashRefinements
|
|
5
5
|
def except(*keys)
|
|
6
6
|
dup.except!(*keys)
|
|
7
|
-
end
|
|
7
|
+
end unless method_defined?(:except)
|
|
8
8
|
|
|
9
9
|
def except!(*keys)
|
|
10
10
|
keys.each { |key| delete(key) }
|
|
@@ -18,12 +18,24 @@ module I18n
|
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
#
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
# deep_merge from activesupport 5
|
|
22
|
+
# Copyright (c) 2005-2019 David Heinemeier Hansson
|
|
23
|
+
def deep_merge(other_hash, &block)
|
|
24
|
+
dup.deep_merge!(other_hash, &block)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# deep_merge! from activesupport 5
|
|
28
|
+
# Copyright (c) 2005-2019 David Heinemeier Hansson
|
|
29
|
+
def deep_merge!(other_hash, &block)
|
|
30
|
+
merge!(other_hash) do |key, this_val, other_val|
|
|
31
|
+
if this_val.is_a?(Hash) && other_val.is_a?(Hash)
|
|
32
|
+
this_val.deep_merge(other_val, &block)
|
|
33
|
+
elsif block_given?
|
|
34
|
+
block.call(key, this_val, other_val)
|
|
35
|
+
else
|
|
36
|
+
other_val
|
|
37
|
+
end
|
|
25
38
|
end
|
|
26
|
-
merge!(data, &merger)
|
|
27
39
|
end
|
|
28
40
|
|
|
29
41
|
def symbolize_key(key)
|
data/lib/i18n/gettext/helpers.rb
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
module I18n
|
|
5
5
|
DEFAULT_INTERPOLATION_PATTERNS = [
|
|
6
6
|
/%%/,
|
|
7
|
-
/%\{(\w+)\}/,
|
|
7
|
+
/%\{([\w|]+)\}/, # matches placeholders like "%{foo} or %{foo|word}"
|
|
8
8
|
/%<(\w+)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/ # matches placeholders like "%<foo>.d"
|
|
9
9
|
].freeze
|
|
10
10
|
INTERPOLATION_PATTERN = Regexp.union(DEFAULT_INTERPOLATION_PATTERNS)
|
|
11
|
-
deprecate_constant :INTERPOLATION_PATTERN
|
|
11
|
+
deprecate_constant :INTERPOLATION_PATTERN
|
|
12
12
|
|
|
13
13
|
class << self
|
|
14
14
|
# Return String or raises MissingInterpolationArgument exception.
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
#
|
|
27
27
|
# I18n.default_locale = :"en-US"
|
|
28
28
|
# I18n.fallbacks = I18n::Locale::Fallbacks.new(:"de-AT" => :"de-DE")
|
|
29
|
-
# I18n.fallbacks[:"de-AT"] # => [:"de-AT", :
|
|
29
|
+
# I18n.fallbacks[:"de-AT"] # => [:"de-AT", :de, :"de-DE"]
|
|
30
30
|
#
|
|
31
31
|
# # using a custom locale as default fallback locale
|
|
32
32
|
#
|
|
@@ -60,12 +60,13 @@ module I18n
|
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def defaults=(defaults)
|
|
63
|
-
@defaults = defaults.
|
|
63
|
+
@defaults = defaults.flat_map { |default| compute(default, false) }
|
|
64
64
|
end
|
|
65
65
|
attr_reader :defaults
|
|
66
66
|
|
|
67
67
|
def [](locale)
|
|
68
68
|
raise InvalidLocale.new(locale) if locale.nil?
|
|
69
|
+
raise Disabled.new('fallback#[]') if locale == false
|
|
69
70
|
locale = locale.to_sym
|
|
70
71
|
super || store(locale, compute(locale))
|
|
71
72
|
end
|
|
@@ -83,13 +84,15 @@ module I18n
|
|
|
83
84
|
protected
|
|
84
85
|
|
|
85
86
|
def compute(tags, include_defaults = true, exclude = [])
|
|
86
|
-
result = Array(tags).
|
|
87
|
+
result = Array(tags).flat_map do |tag|
|
|
87
88
|
tags = I18n::Locale::Tag.tag(tag).self_and_parents.map! { |t| t.to_sym } - exclude
|
|
88
89
|
tags.each { |_tag| tags += compute(@map[_tag], false, exclude + tags) if @map[_tag] }
|
|
89
90
|
tags
|
|
90
|
-
end
|
|
91
|
+
end
|
|
91
92
|
result.push(*defaults) if include_defaults
|
|
92
|
-
result.uniq
|
|
93
|
+
result.uniq!
|
|
94
|
+
result.compact!
|
|
95
|
+
result
|
|
93
96
|
end
|
|
94
97
|
end
|
|
95
98
|
end
|
|
@@ -3,18 +3,20 @@ module I18n
|
|
|
3
3
|
module Tag
|
|
4
4
|
module Parents
|
|
5
5
|
def parent
|
|
6
|
-
@parent ||=
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
@parent ||=
|
|
7
|
+
begin
|
|
8
|
+
segs = to_a
|
|
9
|
+
segs.compact!
|
|
10
|
+
segs.length > 1 ? self.class.tag(*segs[0..(segs.length - 2)].join('-')) : nil
|
|
11
|
+
end
|
|
10
12
|
end
|
|
11
13
|
|
|
12
14
|
def self_and_parents
|
|
13
|
-
@self_and_parents ||= [self]
|
|
15
|
+
@self_and_parents ||= [self].concat parents
|
|
14
16
|
end
|
|
15
17
|
|
|
16
18
|
def parents
|
|
17
|
-
@parents ||=
|
|
19
|
+
@parents ||= parent ? [parent].concat(parent.parents) : []
|
|
18
20
|
end
|
|
19
21
|
end
|
|
20
22
|
end
|
|
@@ -24,6 +24,10 @@ module I18n
|
|
|
24
24
|
assert_equal 'Hi David!', interpolate(:default => 'Hi %{name}!', :name => 'David')
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
test "interpolation: works with a pipe" do
|
|
28
|
+
assert_equal 'Hi david!', interpolate(:default => 'Hi %{name|lowercase}!', :'name|lowercase' => 'david')
|
|
29
|
+
end
|
|
30
|
+
|
|
27
31
|
test "interpolation: given a nil value it still interpolates it into the string" do
|
|
28
32
|
assert_equal 'Hi !', interpolate(:default => 'Hi %{name}!', :name => nil)
|
|
29
33
|
end
|
data/lib/i18n/tests/link.rb
CHANGED
|
@@ -26,7 +26,7 @@ module I18n
|
|
|
26
26
|
}
|
|
27
27
|
assert_equal('linked', I18n.backend.translate('en', :'foo.link'))
|
|
28
28
|
end
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
test "linked lookup: if a dot-separated key resolves to a dot-separated symbol it looks up the symbol" do
|
|
31
31
|
I18n.backend.store_translations 'en', {
|
|
32
32
|
:foo => { :link => :"bar.linked" },
|
|
@@ -51,6 +51,16 @@ module I18n
|
|
|
51
51
|
assert_equal "can't be blank", I18n.t(:"activerecord.errors.messages.blank")
|
|
52
52
|
assert_equal "can't be blank", I18n.t(:"activerecord.errors.messages.blank")
|
|
53
53
|
end
|
|
54
|
+
|
|
55
|
+
test "linked lookup: a link can resolve with option :count" do
|
|
56
|
+
I18n.backend.store_translations 'en', {
|
|
57
|
+
:counter => :counted,
|
|
58
|
+
:counted => { :foo => { :one => "one", :other => "other" }, :bar => "bar" }
|
|
59
|
+
}
|
|
60
|
+
assert_equal "one", I18n.t(:'counter.foo', count: 1)
|
|
61
|
+
assert_equal "other", I18n.t(:'counter.foo', count: 2)
|
|
62
|
+
assert_equal "bar", I18n.t(:'counter.bar', count: 3)
|
|
63
|
+
end
|
|
54
64
|
end
|
|
55
65
|
end
|
|
56
66
|
end
|
|
@@ -68,9 +68,9 @@ module I18n
|
|
|
68
68
|
|
|
69
69
|
test "localize Date: does not modify the options hash" do
|
|
70
70
|
options = { :format => '%b', :locale => :de }
|
|
71
|
-
assert_equal 'Mär', I18n.l(@date, options)
|
|
71
|
+
assert_equal 'Mär', I18n.l(@date, **options)
|
|
72
72
|
assert_equal({ :format => '%b', :locale => :de }, options)
|
|
73
|
-
assert_nothing_raised { I18n.l(@date, options.freeze) }
|
|
73
|
+
assert_nothing_raised { I18n.l(@date, **options.freeze) }
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
test "localize Date: given nil with default value it returns default" do
|
|
@@ -52,19 +52,20 @@ 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 I18n::Tests::Localization::Procs.inspect_args([time, {}
|
|
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 I18n::Tests::Localization::Procs.inspect_args([time, options
|
|
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 self.inspect_args(args)
|
|
67
|
+
def self.inspect_args(args, kwargs)
|
|
68
|
+
args << kwargs
|
|
68
69
|
args = args.map do |arg|
|
|
69
70
|
case arg
|
|
70
71
|
when ::Time, ::DateTime
|
|
@@ -85,12 +86,12 @@ module I18n
|
|
|
85
86
|
I18n.backend.store_translations :ru, {
|
|
86
87
|
:time => {
|
|
87
88
|
:formats => {
|
|
88
|
-
:proc => lambda { |*args| I18n::Tests::Localization::Procs.inspect_args(args) }
|
|
89
|
+
:proc => lambda { |*args, **kwargs| I18n::Tests::Localization::Procs.inspect_args(args, kwargs) }
|
|
89
90
|
}
|
|
90
91
|
},
|
|
91
92
|
:date => {
|
|
92
93
|
:formats => {
|
|
93
|
-
:proc => lambda { |*args| I18n::Tests::Localization::Procs.inspect_args(args) }
|
|
94
|
+
:proc => lambda { |*args, **kwargs| I18n::Tests::Localization::Procs.inspect_args(args, kwargs) }
|
|
94
95
|
},
|
|
95
96
|
:'day_names' => lambda { |key, options|
|
|
96
97
|
(options[:format] =~ /^%A/) ?
|
data/lib/i18n/tests/lookup.rb
CHANGED
|
@@ -43,9 +43,9 @@ module I18n
|
|
|
43
43
|
|
|
44
44
|
test "lookup: does not modify the options hash" do
|
|
45
45
|
options = {}
|
|
46
|
-
assert_equal "a", I18n.t(:string, options)
|
|
46
|
+
assert_equal "a", I18n.t(:string, **options)
|
|
47
47
|
assert_equal({}, options)
|
|
48
|
-
assert_nothing_raised { I18n.t(:string, options.freeze) }
|
|
48
|
+
assert_nothing_raised { I18n.t(:string, **options.freeze) }
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
test "lookup: given an array of keys it translates all of them" do
|
data/lib/i18n/tests/procs.rb
CHANGED
|
@@ -8,6 +8,11 @@ module I18n
|
|
|
8
8
|
assert_equal '[:a_lambda, {:foo=>"foo"}]', I18n.t(:a_lambda, :foo => 'foo')
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
test "lookup: given a translation is a proc it passes the interpolation values as keyword arguments" do
|
|
12
|
+
I18n.backend.store_translations(:en, :a_lambda => lambda { |key, foo:, **| I18n::Tests::Procs.filter_args(key, foo: foo) })
|
|
13
|
+
assert_equal '[:a_lambda, {:foo=>"foo"}]', I18n.t(:a_lambda, :foo => 'foo')
|
|
14
|
+
end
|
|
15
|
+
|
|
11
16
|
test "defaults: given a default is a Proc it calls it with the key and interpolation values" do
|
|
12
17
|
proc = lambda { |*args| I18n::Tests::Procs.filter_args(*args) }
|
|
13
18
|
assert_equal '[nil, {:foo=>"foo"}]', I18n.t(nil, :default => proc, :foo => 'foo')
|
data/lib/i18n/version.rb
CHANGED
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: 1.
|
|
4
|
+
version: 1.8.10
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sven Fuchs
|
|
@@ -10,18 +10,18 @@ authors:
|
|
|
10
10
|
- Stephan Soller
|
|
11
11
|
- Saimon Moore
|
|
12
12
|
- Ryan Bigg
|
|
13
|
-
autorequire:
|
|
13
|
+
autorequire:
|
|
14
14
|
bindir: bin
|
|
15
15
|
cert_chain: []
|
|
16
|
-
date:
|
|
16
|
+
date: 2021-03-30 00:00:00.000000000 Z
|
|
17
17
|
dependencies:
|
|
18
18
|
- !ruby/object:Gem::Dependency
|
|
19
|
-
name: concurrent-ruby
|
|
20
19
|
requirement: !ruby/object:Gem::Requirement
|
|
21
20
|
requirements:
|
|
22
21
|
- - "~>"
|
|
23
22
|
- !ruby/object:Gem::Version
|
|
24
23
|
version: '1.0'
|
|
24
|
+
name: concurrent-ruby
|
|
25
25
|
type: :runtime
|
|
26
26
|
prerelease: false
|
|
27
27
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -82,27 +82,15 @@ files:
|
|
|
82
82
|
- lib/i18n/tests/pluralization.rb
|
|
83
83
|
- lib/i18n/tests/procs.rb
|
|
84
84
|
- lib/i18n/version.rb
|
|
85
|
-
homepage:
|
|
85
|
+
homepage: https://github.com/ruby-i18n/i18n
|
|
86
86
|
licenses:
|
|
87
87
|
- MIT
|
|
88
88
|
metadata:
|
|
89
|
-
bug_tracker_uri: https://github.com/
|
|
90
|
-
changelog_uri: https://github.com/
|
|
89
|
+
bug_tracker_uri: https://github.com/ruby-i18n/i18n/issues
|
|
90
|
+
changelog_uri: https://github.com/ruby-i18n/i18n/releases
|
|
91
91
|
documentation_uri: https://guides.rubyonrails.org/i18n.html
|
|
92
|
-
source_code_uri: https://github.com/
|
|
93
|
-
post_install_message:
|
|
94
|
-
|
|
95
|
-
HEADS UP! i18n 1.1 changed fallbacks to exclude default locale.
|
|
96
|
-
But that may break your application.
|
|
97
|
-
|
|
98
|
-
Please check your Rails app for 'config.i18n.fallbacks = true'.
|
|
99
|
-
If you're using I18n (>= 1.1.0) and Rails (< 5.2.2), this should be
|
|
100
|
-
'config.i18n.fallbacks = [I18n.default_locale]'.
|
|
101
|
-
If not, fallbacks will be broken in your app by I18n 1.1.x.
|
|
102
|
-
|
|
103
|
-
For more info see:
|
|
104
|
-
https://github.com/svenfuchs/i18n/releases/tag/v1.1.0
|
|
105
|
-
|
|
92
|
+
source_code_uri: https://github.com/ruby-i18n/i18n
|
|
93
|
+
post_install_message:
|
|
106
94
|
rdoc_options: []
|
|
107
95
|
require_paths:
|
|
108
96
|
- lib
|
|
@@ -117,8 +105,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
117
105
|
- !ruby/object:Gem::Version
|
|
118
106
|
version: 1.3.5
|
|
119
107
|
requirements: []
|
|
120
|
-
rubygems_version: 3.0.
|
|
121
|
-
signing_key:
|
|
108
|
+
rubygems_version: 3.0.6
|
|
109
|
+
signing_key:
|
|
122
110
|
specification_version: 4
|
|
123
111
|
summary: New wave Internationalization support for Ruby
|
|
124
112
|
test_files: []
|