i18n 1.8.11 → 1.9.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: df354d130700e7c31886ac15782c2617d563dea021c586a9194b6494eadf3fde
4
- data.tar.gz: 4b72e7eb10961f84491eb11c33a0434276d0d780a77141d5dedc484a4a9561b3
3
+ metadata.gz: 73eb29528a85ee9bb80e81091573b181a768e7eca83afba1222884afbdc81928
4
+ data.tar.gz: a807e1a214d3195204fa37b7bc0ac57d7ac8125889405f943c07901b4e2b3972
5
5
  SHA512:
6
- metadata.gz: bf3447b5b7173685131998523341716b2b20cb03d256be5c8b287ba1fc44ce1e9cca0ed80b58708b860b5e01fa58c04b4a059978f2d8d2ed59bafa7a9dd35550
7
- data.tar.gz: ce81270e8fbc5937a6dea497c485583d4adb4c3295fb824797028f771e2ac54118db3df26d157f222a3b70821a80174b4d50f959fe7010490cfffd391572272d
6
+ metadata.gz: 4d7d1ac263e739f2b8a66fe50f0e5947150a9401e5e9a8d74a41d4f1ea0ac49b814a1474ab3b1e0e2c37f2e37692c76ab010cf962486709584580fc9ae72f15a
7
+ data.tar.gz: 3a87c0bb4b17b09af88afb9b8223df8f75251b9c073073200ed69ab343f8962ef230b63f93ad214243d1262c014ca6f735b1ef25f7c605c02249d66060fb3640
@@ -2,12 +2,10 @@
2
2
 
3
3
  require 'yaml'
4
4
  require 'json'
5
- require 'i18n/core_ext/hash'
6
5
 
7
6
  module I18n
8
7
  module Backend
9
8
  module Base
10
- using I18n::HashRefinements
11
9
  include I18n::Backend::Transliterator
12
10
 
13
11
  # Accepts a list of paths to translation files. Loads translations from
@@ -53,7 +51,7 @@ module I18n
53
51
  end
54
52
 
55
53
  deep_interpolation = options[:deep_interpolation]
56
- values = options.except(*RESERVED_KEYS)
54
+ values = Utils.except(options, *RESERVED_KEYS)
57
55
  if values
58
56
  entry = if deep_interpolation
59
57
  deep_interpolate(locale, entry, values)
@@ -223,17 +221,18 @@ module I18n
223
221
  def load_file(filename)
224
222
  type = File.extname(filename).tr('.', '').downcase
225
223
  raise UnknownFileType.new(type, filename) unless respond_to?(:"load_#{type}", true)
226
- data = send(:"load_#{type}", filename)
224
+ data, keys_symbolized = send(:"load_#{type}", filename)
227
225
  unless data.is_a?(Hash)
228
226
  raise InvalidLocaleData.new(filename, 'expects it to return a hash, but does not')
229
227
  end
230
- data.each { |locale, d| store_translations(locale, d || {}) }
228
+ data.each { |locale, d| store_translations(locale, d || {}, skip_symbolize_keys: keys_symbolized) }
231
229
  end
232
230
 
233
231
  # Loads a plain Ruby translations file. eval'ing the file must yield
234
232
  # a Hash containing translation data with locales as toplevel keys.
235
233
  def load_rb(filename)
236
- eval(IO.read(filename), binding, filename)
234
+ translations = eval(IO.read(filename), binding, filename)
235
+ [translations, false]
237
236
  end
238
237
 
239
238
  # Loads a YAML translations file. The data must have locales as
@@ -241,9 +240,9 @@ module I18n
241
240
  def load_yml(filename)
242
241
  begin
243
242
  if YAML.respond_to?(:unsafe_load_file) # Psych 4.0 way
244
- YAML.unsafe_load_file(filename)
243
+ [YAML.unsafe_load_file(filename, symbolize_names: true, freeze: true), true]
245
244
  else
246
- YAML.load_file(filename)
245
+ [YAML.load_file(filename), false]
247
246
  end
248
247
  rescue TypeError, ScriptError, StandardError => e
249
248
  raise InvalidLocaleData.new(filename, e.inspect)
@@ -255,7 +254,12 @@ module I18n
255
254
  # toplevel keys.
256
255
  def load_json(filename)
257
256
  begin
258
- ::JSON.parse(File.read(filename))
257
+ # Use #load_file as a proxy for a version of JSON where symbolize_names and freeze are supported.
258
+ if ::JSON.respond_to?(:load_file)
259
+ [::JSON.load_file(filename, symbolize_names: true, freeze: true), true]
260
+ else
261
+ [::JSON.parse(File.read(filename)), false]
262
+ end
259
263
  rescue TypeError, StandardError => e
260
264
  raise InvalidLocaleData.new(filename, e.inspect)
261
265
  end
@@ -17,8 +17,6 @@ module I18n
17
17
  # The implementation assumes that all backends added to the Chain implement
18
18
  # a lookup method with the same API as Simple backend does.
19
19
  class Chain
20
- using I18n::HashRefinements
21
-
22
20
  module Implementation
23
21
  include Base
24
22
 
@@ -55,7 +53,7 @@ module I18n
55
53
 
56
54
  def translate(locale, key, default_options = EMPTY_HASH)
57
55
  namespace = nil
58
- options = default_options.except(:default)
56
+ options = Utils.except(default_options, :default)
59
57
 
60
58
  backends.each do |backend|
61
59
  catch(:exception) do
@@ -101,7 +99,7 @@ module I18n
101
99
  init_translations unless initialized?
102
100
  translations
103
101
  end
104
- memo.deep_merge!(partial_translations) { |_, a, b| b || a }
102
+ Utils.deep_merge!(memo, partial_translations) { |_, a, b| b || a }
105
103
  end
106
104
  end
107
105
 
@@ -43,7 +43,7 @@ module I18n
43
43
  return super if options[:fallback_in_progress]
44
44
  default = extract_non_symbol_default!(options) if options[:default]
45
45
 
46
- fallback_options = options.merge(:fallback_in_progress => true)
46
+ fallback_options = options.merge(:fallback_in_progress => true, fallback_original_locale: locale)
47
47
  I18n.fallbacks[locale].each do |fallback|
48
48
  begin
49
49
  catch(:exception) do
@@ -64,6 +64,17 @@ module I18n
64
64
  throw(:exception, I18n::MissingTranslation.new(locale, key, options))
65
65
  end
66
66
 
67
+ def resolve(locale, object, subject, options = EMPTY_HASH)
68
+ return subject if options[:resolve] == false
69
+ return super unless subject.is_a?(Symbol)
70
+
71
+ result = catch(:exception) do
72
+ options.delete(:fallback_in_progress)
73
+ I18n.translate(subject, **options.merge(locale: options[:fallback_original_locale], throw: true))
74
+ end
75
+ result unless result.is_a?(MissingTranslation)
76
+ end
77
+
67
78
  def extract_non_symbol_default!(options)
68
79
  defaults = [options[:default]].flatten
69
80
  first_non_symbol_default = defaults.detect{|default| !default.is_a?(Symbol)}
@@ -31,8 +31,6 @@ module I18n
31
31
  # Without it strings containing periods (".") will not be translated.
32
32
 
33
33
  module Gettext
34
- using I18n::HashRefinements
35
-
36
34
  class PoData < Hash
37
35
  def set_comment(msgid_or_sym, comment)
38
36
  # ignore
@@ -43,7 +41,7 @@ module I18n
43
41
  def load_po(filename)
44
42
  locale = ::File.basename(filename, '.po').to_sym
45
43
  data = normalize(locale, parse(filename))
46
- { locale => data }
44
+ [{ locale => data }, false]
47
45
  end
48
46
 
49
47
  def parse(filename)
@@ -61,7 +59,7 @@ module I18n
61
59
  { part => _normalized.empty? ? value : _normalized }
62
60
  end
63
61
 
64
- result.deep_merge!(normalized)
62
+ Utils.deep_merge!(result, normalized)
65
63
  end
66
64
  result
67
65
  end
@@ -67,8 +67,6 @@ module I18n
67
67
  #
68
68
  # This is useful if you are using a KeyValue backend chained to a Simple backend.
69
69
  class KeyValue
70
- using I18n::HashRefinements
71
-
72
70
  module Implementation
73
71
  attr_accessor :store
74
72
 
@@ -91,7 +89,7 @@ module I18n
91
89
  when Hash
92
90
  if @subtrees && (old_value = @store[key])
93
91
  old_value = JSON.decode(old_value)
94
- value = old_value.deep_symbolize_keys.deep_merge!(value) if old_value.is_a?(Hash)
92
+ value = Utils.deep_merge!(Utils.deep_symbolize_keys(old_value), value) if old_value.is_a?(Hash)
95
93
  end
96
94
  when Proc
97
95
  raise "Key-value stores cannot handle procs"
@@ -115,12 +113,12 @@ module I18n
115
113
  # them into a hash such as the one returned from loading the
116
114
  # haml files
117
115
  def translations
118
- @translations = @store.keys.clone.map do |main_key|
116
+ @translations = Utils.deep_symbolize_keys(@store.keys.clone.map do |main_key|
119
117
  main_value = JSON.decode(@store[main_key])
120
118
  main_key.to_s.split(".").reverse.inject(main_value) do |value, key|
121
119
  {key.to_sym => value}
122
120
  end
123
- end.inject{|hash, elem| hash.deep_merge!(elem)}.deep_symbolize_keys
121
+ end.inject{|hash, elem| Utils.deep_merge!(hash, elem)})
124
122
  end
125
123
 
126
124
  def init_translations
@@ -141,7 +139,7 @@ module I18n
141
139
  value = JSON.decode(value) if value
142
140
 
143
141
  if value.is_a?(Hash)
144
- value.deep_symbolize_keys
142
+ Utils.deep_symbolize_keys(value)
145
143
  elsif !value.nil?
146
144
  value
147
145
  elsif !@subtrees
@@ -19,8 +19,6 @@ module I18n
19
19
  #
20
20
  # I18n::Backend::Simple.include(I18n::Backend::Pluralization)
21
21
  class Simple
22
- using I18n::HashRefinements
23
-
24
22
  module Implementation
25
23
  include Base
26
24
 
@@ -40,8 +38,8 @@ module I18n
40
38
  end
41
39
  locale = locale.to_sym
42
40
  translations[locale] ||= Concurrent::Hash.new
43
- data = data.deep_symbolize_keys
44
- translations[locale].deep_merge!(data)
41
+ data = Utils.deep_symbolize_keys(data) unless options.fetch(:skip_symbolize_keys, false)
42
+ Utils.deep_merge!(translations[locale], data)
45
43
  end
46
44
 
47
45
  # Get available locales from the translations hash
@@ -47,10 +47,12 @@ module I18n
47
47
 
48
48
  class MissingTranslation < ArgumentError
49
49
  module Base
50
+ PERMITTED_KEYS = [:scope].freeze
51
+
50
52
  attr_reader :locale, :key, :options
51
53
 
52
54
  def initialize(locale, key, options = EMPTY_HASH)
53
- @key, @locale, @options = key, locale, options.dup
55
+ @key, @locale, @options = key, locale, options.slice(*PERMITTED_KEYS)
54
56
  options.each { |k, v| self.options[k] = v.inspect if v.is_a?(Proc) }
55
57
  end
56
58
 
@@ -5,7 +5,7 @@ module I18n
5
5
  DEFAULT_INTERPOLATION_PATTERNS = [
6
6
  /%%/,
7
7
  /%\{([\w|]+)\}/, # matches placeholders like "%{foo} or %{foo|word}"
8
- /%<(\w+)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/ # matches placeholders like "%<foo>.d"
8
+ /%<(\w+)>([^\d]*?\d*\.?\d*[bBdiouxXeEfgGcps])/ # matches placeholders like "%<foo>.d"
9
9
  ].freeze
10
10
  INTERPOLATION_PATTERN = Regexp.union(DEFAULT_INTERPOLATION_PATTERNS)
11
11
  deprecate_constant :INTERPOLATION_PATTERN
@@ -14,7 +14,7 @@ module I18n
14
14
  # Return String or raises MissingInterpolationArgument exception.
15
15
  # Missing argument's logic is handled by I18n.config.missing_interpolation_argument_handler.
16
16
  def interpolate(string, values)
17
- raise ReservedInterpolationKey.new($1.to_sym, string) if string =~ RESERVED_KEYS_PATTERN
17
+ raise ReservedInterpolationKey.new($1.to_sym, string) if string =~ I18n.reserved_keys_pattern
18
18
  raise ArgumentError.new('Interpolation values must be a Hash.') unless values.kind_of?(Hash)
19
19
  interpolate_hash(string, values)
20
20
  end
@@ -15,19 +15,12 @@
15
15
  # * all parent locales of a given locale (e.g. :es for :"es-MX") first,
16
16
  # * the current default locales and all of their parents second
17
17
  #
18
- # The default locales are set to [I18n.default_locale] by default but can be
19
- # set to something else.
18
+ # The default locales are set to [] by default but can be set to something else.
20
19
  #
21
20
  # One can additionally add any number of additional fallback locales manually.
22
21
  # These will be added before the default locales to the fallback chain. For
23
22
  # example:
24
23
  #
25
- # # using the default locale as default fallback locale
26
- #
27
- # I18n.default_locale = :"en-US"
28
- # I18n.fallbacks = I18n::Locale::Fallbacks.new(:"de-AT" => :"de-DE")
29
- # I18n.fallbacks[:"de-AT"] # => [:"de-AT", :de, :"de-DE"]
30
- #
31
24
  # # using a custom locale as default fallback locale
32
25
  #
33
26
  # I18n.fallbacks = I18n::Locale::Fallbacks.new(:"en-GB", :"de-AT" => :de, :"de-CH" => :de)
@@ -71,13 +64,18 @@ module I18n
71
64
  super || store(locale, compute(locale))
72
65
  end
73
66
 
74
- def map(mappings)
75
- mappings.each do |from, to|
76
- from, to = from.to_sym, Array(to)
77
- to.each do |_to|
78
- @map[from] ||= []
79
- @map[from] << _to.to_sym
67
+ def map(*args, &block)
68
+ if args.count == 1 && !block_given?
69
+ mappings = args.first
70
+ mappings.each do |from, to|
71
+ from, to = from.to_sym, Array(to)
72
+ to.each do |_to|
73
+ @map[from] ||= []
74
+ @map[from] << _to.to_sym
75
+ end
80
76
  end
77
+ else
78
+ @map.map(*args, &block)
81
79
  end
82
80
  end
83
81
 
@@ -37,7 +37,7 @@ module I18n
37
37
  end
38
38
 
39
39
  test "defaults: given an array of missing keys it raises a MissingTranslationData exception" do
40
- assert_raise I18n::MissingTranslationData do
40
+ assert_raises I18n::MissingTranslationData do
41
41
  I18n.t(:does_not_exist, :default => [:does_not_exist_2, :does_not_exist_3], :raise => true)
42
42
  end
43
43
  end
@@ -41,7 +41,7 @@ module I18n
41
41
  end
42
42
 
43
43
  test "interpolation: given values but missing a key it raises I18n::MissingInterpolationArgument" do
44
- assert_raise(I18n::MissingInterpolationArgument) do
44
+ assert_raises(I18n::MissingInterpolationArgument) do
45
45
  interpolate(:default => '%{foo}', :bar => 'bar')
46
46
  end
47
47
  end
@@ -77,13 +77,13 @@ module I18n
77
77
 
78
78
  if Object.const_defined?(:Encoding)
79
79
  test "interpolation: given a euc-jp translation and a utf-8 value it raises Encoding::CompatibilityError" do
80
- assert_raise(Encoding::CompatibilityError) do
80
+ assert_raises(Encoding::CompatibilityError) do
81
81
  interpolate(:default => euc_jp('こんにちは、%{name}さん!'), :name => 'ゆきひろ')
82
82
  end
83
83
  end
84
84
 
85
85
  test "interpolation: given a utf-8 translation and a euc-jp value it raises Encoding::CompatibilityError" do
86
- assert_raise(Encoding::CompatibilityError) do
86
+ assert_raises(Encoding::CompatibilityError) do
87
87
  interpolate(:default => 'こんにちは、%{name}さん!', :name => euc_jp('ゆきひろ'))
88
88
  end
89
89
  end
@@ -108,10 +108,10 @@ module I18n
108
108
  end
109
109
 
110
110
  test "interpolation: given a translations containing a reserved key it raises I18n::ReservedInterpolationKey" do
111
- assert_raise(I18n::ReservedInterpolationKey) { interpolate(:foo => :bar, :default => '%{exception_handler}') }
112
- assert_raise(I18n::ReservedInterpolationKey) { interpolate(:foo => :bar, :default => '%{default}') }
113
- assert_raise(I18n::ReservedInterpolationKey) { interpolate(:foo => :bar, :default => '%{separator}') }
114
- assert_raise(I18n::ReservedInterpolationKey) { interpolate(:foo => :bar, :default => '%{scope}') }
111
+ assert_raises(I18n::ReservedInterpolationKey) { interpolate(:foo => :bar, :default => '%{exception_handler}') }
112
+ assert_raises(I18n::ReservedInterpolationKey) { interpolate(:foo => :bar, :default => '%{default}') }
113
+ assert_raises(I18n::ReservedInterpolationKey) { interpolate(:foo => :bar, :default => '%{separator}') }
114
+ assert_raises(I18n::ReservedInterpolationKey) { interpolate(:foo => :bar, :default => '%{scope}') }
115
115
  end
116
116
 
117
117
  test "interpolation: deep interpolation for default string" do
@@ -78,15 +78,15 @@ module I18n
78
78
  end
79
79
 
80
80
  test "localize Date: given nil it raises I18n::ArgumentError" do
81
- assert_raise(I18n::ArgumentError) { I18n.l(nil) }
81
+ assert_raises(I18n::ArgumentError) { I18n.l(nil) }
82
82
  end
83
83
 
84
84
  test "localize Date: given a plain Object it raises I18n::ArgumentError" do
85
- assert_raise(I18n::ArgumentError) { I18n.l(Object.new) }
85
+ assert_raises(I18n::ArgumentError) { I18n.l(Object.new) }
86
86
  end
87
87
 
88
88
  test "localize Date: given a format is missing it raises I18n::MissingTranslationData" do
89
- assert_raise(I18n::MissingTranslationData) { I18n.l(@date, :format => :missing) }
89
+ assert_raises(I18n::MissingTranslationData) { I18n.l(@date, :format => :missing) }
90
90
  end
91
91
 
92
92
  test "localize Date: it does not alter the format string" do
@@ -78,7 +78,7 @@ module I18n
78
78
  end
79
79
 
80
80
  test "localize DateTime: given a format is missing it raises I18n::MissingTranslationData" do
81
- assert_raise(I18n::MissingTranslationData) { I18n.l(@datetime, :format => :missing) }
81
+ assert_raises(I18n::MissingTranslationData) { I18n.l(@datetime, :format => :missing) }
82
82
  end
83
83
 
84
84
  protected
@@ -74,6 +74,7 @@ module I18n
74
74
  arg.strftime('%a, %d %b %Y')
75
75
  when Hash
76
76
  arg.delete(:fallback_in_progress)
77
+ arg.delete(:fallback_original_locale)
77
78
  arg.inspect
78
79
  else
79
80
  arg.inspect
@@ -79,7 +79,7 @@ module I18n
79
79
  end
80
80
 
81
81
  test "localize Time: given a format is missing it raises I18n::MissingTranslationData" do
82
- assert_raise(I18n::MissingTranslationData) { I18n.l(@time, :format => :missing) }
82
+ assert_raises(I18n::MissingTranslationData) { I18n.l(@time, :format => :missing) }
83
83
  end
84
84
 
85
85
  protected
@@ -34,7 +34,7 @@ module I18n
34
34
  end
35
35
 
36
36
  test "lookup: given a missing key, no default and the raise option it raises MissingTranslationData" do
37
- assert_raise(I18n::MissingTranslationData) { I18n.t(:missing, :raise => true) }
37
+ assert_raises(I18n::MissingTranslationData) { I18n.t(:missing, :raise => true) }
38
38
  end
39
39
 
40
40
  test "lookup: does not raise an exception if no translation data is present for the given locale" do
@@ -61,7 +61,7 @@ module I18n
61
61
  # In fact it probably *should* fail but Rails currently relies on using the default locale instead.
62
62
  # So we'll stick to this for now until we get it fixed in Rails.
63
63
  test "lookup: given nil as a locale it does not raise but use the default locale" do
64
- # assert_raise(I18n::InvalidLocale) { I18n.t(:bar, :locale => nil) }
64
+ # assert_raises(I18n::InvalidLocale) { I18n.t(:bar, :locale => nil) }
65
65
  assert_nothing_raised { I18n.t(:bar, :locale => nil) }
66
66
  end
67
67
 
@@ -28,7 +28,7 @@ module I18n
28
28
  end
29
29
 
30
30
  test "pluralization: given incomplete pluralization data it raises I18n::InvalidPluralizationData" do
31
- assert_raise(I18n::InvalidPluralizationData) { I18n.t(:default => { :one => 'bar' }, :count => 2) }
31
+ assert_raises(I18n::InvalidPluralizationData) { I18n.t(:default => { :one => 'bar' }, :count => 2) }
32
32
  end
33
33
  end
34
34
  end
@@ -53,7 +53,13 @@ module I18n
53
53
 
54
54
 
55
55
  def self.filter_args(*args)
56
- args.map {|arg| arg.delete(:fallback_in_progress) if arg.is_a?(Hash) ; arg }.inspect
56
+ args.map do |arg|
57
+ if arg.is_a?(Hash)
58
+ arg.delete(:fallback_in_progress)
59
+ arg.delete(:fallback_original_locale)
60
+ end
61
+ arg
62
+ end.inspect
57
63
  end
58
64
  end
59
65
  end
data/lib/i18n/utils.rb ADDED
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module I18n
4
+ module Utils
5
+ class << self
6
+ if Hash.method_defined?(:except)
7
+ def except(hash, *keys)
8
+ hash.except(*keys)
9
+ end
10
+ else
11
+ def except(hash, *keys)
12
+ hash = hash.dup
13
+ keys.each { |k| hash.delete(k) }
14
+ hash
15
+ end
16
+ end
17
+
18
+ def deep_merge(hash, other_hash, &block)
19
+ deep_merge!(hash.dup, other_hash, &block)
20
+ end
21
+
22
+ def deep_merge!(hash, other_hash, &block)
23
+ hash.merge!(other_hash) do |key, this_val, other_val|
24
+ if this_val.is_a?(Hash) && other_val.is_a?(Hash)
25
+ deep_merge(this_val, other_val, &block)
26
+ elsif block_given?
27
+ yield key, this_val, other_val
28
+ else
29
+ other_val
30
+ end
31
+ end
32
+ end
33
+
34
+ def deep_symbolize_keys(hash)
35
+ hash.each_with_object({}) do |(key, value), result|
36
+ result[key.respond_to?(:to_sym) ? key.to_sym : key] = deep_symbolize_keys_in_object(value)
37
+ result
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def deep_symbolize_keys_in_object(value)
44
+ case value
45
+ when Hash
46
+ deep_symbolize_keys(value)
47
+ when Array
48
+ value.map { |e| deep_symbolize_keys_in_object(e) }
49
+ else
50
+ value
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
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.8.11"
4
+ VERSION = "1.9.1"
5
5
  end
data/lib/i18n.rb CHANGED
@@ -4,6 +4,7 @@ require 'concurrent/map'
4
4
  require 'concurrent/hash'
5
5
 
6
6
  require 'i18n/version'
7
+ require 'i18n/utils'
7
8
  require 'i18n/exceptions'
8
9
  require 'i18n/interpolate/ruby'
9
10
 
@@ -22,6 +23,7 @@ module I18n
22
23
  exception_handler
23
24
  fallback
24
25
  fallback_in_progress
26
+ fallback_original_locale
25
27
  format
26
28
  object
27
29
  raise
@@ -29,14 +31,26 @@ module I18n
29
31
  scope
30
32
  separator
31
33
  throw
32
- ].freeze
33
- RESERVED_KEYS_PATTERN = /%\{(#{RESERVED_KEYS.join("|")})\}/
34
+ ]
34
35
  EMPTY_HASH = {}.freeze
35
36
 
36
37
  def self.new_double_nested_cache # :nodoc:
37
38
  Concurrent::Map.new { |h, k| h[k] = Concurrent::Map.new }
38
39
  end
39
40
 
41
+ # Marks a key as reserved. Reserved keys are used internally,
42
+ # and can't also be used for interpolation. If you are using any
43
+ # extra keys as I18n options, you should call I18n.reserve_key
44
+ # before any I18n.translate (etc) calls are made.
45
+ def self.reserve_key(key)
46
+ RESERVED_KEYS << key.to_sym
47
+ @reserved_keys_pattern = nil
48
+ end
49
+
50
+ def self.reserved_keys_pattern # :nodoc:
51
+ @reserved_keys_pattern ||= /%\{(#{RESERVED_KEYS.join("|")})\}/
52
+ end
53
+
40
54
  module Base
41
55
  # Gets I18n configuration object.
42
56
  def config
@@ -260,14 +274,14 @@ module I18n
260
274
  #
261
275
  # Setting a Hash using Ruby:
262
276
  #
263
- # store_translations(:de, :i18n => {
264
- # :transliterate => {
265
- # :rule => {
266
- # "ü" => "ue",
267
- # "ö" => "oe"
268
- # }
269
- # }
270
- # )
277
+ # store_translations(:de, i18n: {
278
+ # transliterate: {
279
+ # rule: {
280
+ # 'ü' => 'ue',
281
+ # 'ö' => 'oe'
282
+ # }
283
+ # }
284
+ # })
271
285
  #
272
286
  # Setting a Proc:
273
287
  #
@@ -396,7 +410,7 @@ module I18n
396
410
  keys.delete('')
397
411
  keys.map! do |k|
398
412
  case k
399
- when /\A[-+]?[1-9]\d*\z/ # integer
413
+ when /\A[-+]?([1-9]\d*|0)\z/ # integer
400
414
  k.to_i
401
415
  when 'true'
402
416
  true
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.8.11
4
+ version: 1.9.1
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: 2021-11-02 00:00:00.000000000 Z
16
+ date: 2022-01-27 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
+ name: concurrent-ruby
19
20
  requirement: !ruby/object:Gem::Requirement
20
21
  requirements:
21
22
  - - "~>"
22
23
  - !ruby/object:Gem::Version
23
24
  version: '1.0'
24
- name: concurrent-ruby
25
25
  type: :runtime
26
26
  prerelease: false
27
27
  version_requirements: !ruby/object:Gem::Requirement
@@ -55,7 +55,6 @@ files:
55
55
  - lib/i18n/backend/simple.rb
56
56
  - lib/i18n/backend/transliterator.rb
57
57
  - lib/i18n/config.rb
58
- - lib/i18n/core_ext/hash.rb
59
58
  - lib/i18n/exceptions.rb
60
59
  - lib/i18n/gettext.rb
61
60
  - lib/i18n/gettext/helpers.rb
@@ -81,6 +80,7 @@ files:
81
80
  - lib/i18n/tests/lookup.rb
82
81
  - lib/i18n/tests/pluralization.rb
83
82
  - lib/i18n/tests/procs.rb
83
+ - lib/i18n/utils.rb
84
84
  - lib/i18n/version.rb
85
85
  homepage: https://github.com/ruby-i18n/i18n
86
86
  licenses:
@@ -90,7 +90,7 @@ metadata:
90
90
  changelog_uri: https://github.com/ruby-i18n/i18n/releases
91
91
  documentation_uri: https://guides.rubyonrails.org/i18n.html
92
92
  source_code_uri: https://github.com/ruby-i18n/i18n
93
- post_install_message:
93
+ post_install_message:
94
94
  rdoc_options: []
95
95
  require_paths:
96
96
  - lib
@@ -105,8 +105,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
105
105
  - !ruby/object:Gem::Version
106
106
  version: 1.3.5
107
107
  requirements: []
108
- rubygems_version: 3.0.6
109
- signing_key:
108
+ rubygems_version: 3.1.6
109
+ signing_key:
110
110
  specification_version: 4
111
111
  summary: New wave Internationalization support for Ruby
112
112
  test_files: []
@@ -1,59 +0,0 @@
1
- module I18n
2
- module HashRefinements
3
- refine Hash do
4
- using I18n::HashRefinements
5
- def except(*keys)
6
- dup.except!(*keys)
7
- end unless method_defined?(:except)
8
-
9
- def except!(*keys)
10
- keys.each { |key| delete(key) }
11
- self
12
- end
13
-
14
- def deep_symbolize_keys
15
- each_with_object({}) do |(key, value), result|
16
- result[symbolize_key(key)] = deep_symbolize_keys_in_object(value)
17
- result
18
- end
19
- end
20
-
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
38
- end
39
- end
40
-
41
- def symbolize_key(key)
42
- key.respond_to?(:to_sym) ? key.to_sym : key
43
- end
44
-
45
- private
46
-
47
- def deep_symbolize_keys_in_object(value)
48
- case value
49
- when Hash
50
- value.deep_symbolize_keys
51
- when Array
52
- value.map { |e| deep_symbolize_keys_in_object(e) }
53
- else
54
- value
55
- end
56
- end
57
- end
58
- end
59
- end