i18n 1.6.0 → 1.8.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Build Status](https://
|
3
|
+
[![Build Status](https://github.com/ruby-i18n/i18n/workflows/Ruby/badge.svg)](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: []
|