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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2b03b84d726d3cafa7ea2ecaa6f258cb911696c338cb32c769f71b6d62ca5fb5
4
- data.tar.gz: 7a489bf447e9fedee8fd043cc8ba324c0afc0c34701fbfeff5f15b1c1f4fa6b4
3
+ metadata.gz: 63d76facf7fc1bb9f1f7fca47cec61259726d0879a7e187646f54a7e94f0f73c
4
+ data.tar.gz: ca27ecf10f4cf612a57fa0747aeb2431f42fa08210ce1df79675c17d52b5c203
5
5
  SHA512:
6
- metadata.gz: 534659c40a8df9eb3fd9a123b779de0f1b66471ecd3fb35e2083128193f51aeea95df6aad06039b6309932a6efb3426c525a8759e70c1104754fdd921732d6cd
7
- data.tar.gz: caddebe447a797be3dde1607e74d826588ee13ee1317c6abfbb08cbd8a22d79a6aa10a84d3fd21fa45ca9348317aa0e0aafe1c408d4e1c989fbee7f09cd87606
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://api.travis-ci.org/ruby-i18n/i18n.svg?branch=master)](https://travis-ci.org/ruby-i18n/i18n)
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 Internationalization and localization solution.
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](http://guides.rubyonrails.org/i18n.html) for an example of its usage.
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
- * translation and localization
55
- * interpolation of values to translations (Ruby 1.9 compatible syntax)
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
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 defition (implemented
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/svenfuchs/i18n/wiki
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
- https://github.com/svenfuchs/i18n/graphs/contributors
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 singluar/plural versions of translations like <tt>['Foo', 'Foos']</tt>.
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. Therefor your lambdas should
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
- def translate(key = nil, *, throw: false, raise: false, locale: nil, **options) # TODO deprecate :raise
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 = EMPTY_HASH)
205
- translate(key, options.merge(:raise => true))
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, *, throw: false, raise: false, locale: nil, replacement: nil, **options)
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.map { |k| normalize_key(k, separator) }.flatten
393
+ key.flat_map { |k| normalize_key(k, separator) }
377
394
  else
378
395
  keys = key.to_s.split(separator)
379
396
  keys.delete('')
@@ -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.dup.reject { |key, value| key == :default }
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
- return entry unless entry.is_a?(Hash) && count
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)
@@ -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
- # http://www.ruby-doc.org/core/classes/Object.html#M000337). However, you can
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
- # http://ruby-doc.org/stdlib/libdoc/digest/rdoc/index.html):
22
+ # https://ruby-doc.org/stdlib/libdoc/openssl/rdoc/OpenSSL/Digest.html):
23
23
  #
24
- # I18n.cache_key_digest = Digest::MD5.new
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 'digest/sha2'
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::SHA2.file(filename).hexdigest) == old_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
@@ -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.first.instance_eval do
100
- init_translations unless initialized?
101
- translations
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
- return result unless result.nil?
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
@@ -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].flatten.compact
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
- keys.map! do |k|
26
- k.to_s.tr("#{FLATTEN_SEPARATOR}#{separator}",
27
- "#{SEPARATOR_ESCAPE_CHAR}#{FLATTEN_SEPARATOR}")
28
- end
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) and count
32
+ return entry unless entry.is_a?(Hash) && count
33
33
 
34
34
  pluralizer = pluralizer(locale)
35
35
  if pluralizer.respond_to?(:call)
@@ -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
@@ -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
- # deep_merge_hash! by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
22
- def deep_merge!(data)
23
- merger = lambda do |_key, v1, v2|
24
- Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2
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)
@@ -19,7 +19,7 @@ module I18n
19
19
  end
20
20
 
21
21
  def gettext(msgid, options = EMPTY_HASH)
22
- I18n.t(msgid, { :default => msgid, :separator => '|' }.merge(options))
22
+ I18n.t(msgid, **{:default => msgid, :separator => '|'}.merge(options))
23
23
  end
24
24
  alias _ gettext
25
25
 
@@ -4,11 +4,11 @@
4
4
  module I18n
5
5
  DEFAULT_INTERPOLATION_PATTERNS = [
6
6
  /%%/,
7
- /%\{(\w+)\}/, # matches placeholders like "%{foo}"
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 if method_defined? :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", :"de-DE", :de, :"en-US", :en]
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.map { |default| compute(default, false) }.flatten
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).collect do |tag|
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.flatten
91
+ end
91
92
  result.push(*defaults) if include_defaults
92
- result.uniq.compact
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 ||= begin
7
- segs = to_a.compact
8
- segs.length > 1 ? self.class.tag(*segs[0..(segs.length-2)].join('-')) : nil
9
- end
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] + parents
15
+ @self_and_parents ||= [self].concat parents
14
16
  end
15
17
 
16
18
  def parents
17
- @parents ||= ([parent] + (parent ? parent.parents : [])).compact
19
+ @parents ||= parent ? [parent].concat(parent.parents) : []
18
20
  end
19
21
  end
20
22
  end
@@ -19,7 +19,7 @@ module I18n
19
19
  end
20
20
 
21
21
  def subtags
22
- @subtags = tag.to_s.split('-').map { |subtag| subtag.to_s }
22
+ @subtags = tag.to_s.split('-').map!(&:to_s)
23
23
  end
24
24
 
25
25
  def to_sym
@@ -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
@@ -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, {}]), 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 I18n::Tests::Localization::Procs.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 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/) ?
@@ -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
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module I18n
4
- VERSION = "1.6.0"
4
+ VERSION = "1.8.10"
5
5
  end
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.6.0
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: 2019-03-03 00:00:00.000000000 Z
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: http://github.com/ruby-i18n/i18n
85
+ homepage: https://github.com/ruby-i18n/i18n
86
86
  licenses:
87
87
  - MIT
88
88
  metadata:
89
- bug_tracker_uri: https://github.com/svenfuchs/i18n/issues
90
- changelog_uri: https://github.com/svenfuchs/i18n/releases
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/svenfuchs/i18n
93
- post_install_message: |2+
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.2
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: []